Compare commits

...

215 Commits

Author SHA1 Message Date
basti d35cab0a67 Refactor code, courtesy of @galfert 2018-07-16 20:10:57 +02:00
basti b4a094bc89 Add tests for contributions-by-type chart 2018-07-15 11:57:38 +02:00
basti e43706e2a6 Remove obsolete title from proposal list items 2018-07-14 17:36:45 +02:00
basti 2befc91d90 Add data from proposals to contribution chart 2018-07-14 17:35:06 +02:00
basti 7a35f5177c Add charts.js plugin, basic chart component 2018-07-14 16:36:23 +02:00
basti 6efc3ea790 Merge pull request #76 from 67P/feature/improve_contrast
Add background overlay to improve contrast
2018-07-14 14:00:27 +02:00
basti ef0f3ffbfb Add background overlay to improve contrast
And use lowercase-hyphenated syntax for variables/mixins.
2018-07-12 11:29:45 +02:00
basti 1e14cc695e Merge pull request #75 from 67P/feature/improve_layout
Improve layout, show contribution details
2018-06-22 13:27:00 -07:00
basti c074ccf460 Improve all the things 2018-06-17 16:15:02 +02:00
basti 5436469128 Don't show vote count for confirmed contributions 2018-06-17 15:32:08 +02:00
basti 32714efeb9 Improve layout a bit.
WIP, because this is not ideal. But now it's readable at least.
2018-06-16 17:12:53 +02:00
basti cf777571dc Move contribution info from title to actual content 2018-06-16 16:37:39 +02:00
basti 44b4340a91 New CSS grid layout 2018-06-16 16:09:33 +02:00
basti 24e3171311 Add topbar, move account info to top-right corner 2018-06-16 15:36:35 +02:00
basti 575c1a11c2 Merge pull request #70 from 67P/feature/loading-indicator
Feature: Loading indicator
2018-06-14 19:24:03 +02:00
fsmanuel 47b8421d35 Rename loading.hbs to application-loading.hbs 2018-06-14 19:04:42 +02:00
fsmanuel 38d853a272 Remove spinner route 2018-06-14 19:04:22 +02:00
basti 0320d6149f Merge pull request #69 from 67P/rename-truffle-kredits
Rename truffle-kredits to kredits-contracts
2018-06-14 18:51:05 +02:00
fsmanuel ba9b3530fa Rename truffle-kredits to kredits-contracts 2018-06-14 18:11:56 +02:00
basti e78832e2c8 Merge pull request #67 from 67P/eth-signer-loading
Pass ETH signer only if it can actually sign
2018-06-14 15:14:23 +02:00
bumi 804060ad34 Pass ETH signer only if it can actually sign
We have seen an "no accounts" invalid operation getAddress when trying
to call a contract function in the init calls.
It seems ethers has a problem with a signer that does not have any
accounts - even though we don't try to sign an actual transaction and
only read data from the contract.

This change only passes the signer if we have an unlocked web3 provider
(metamask).
2018-06-14 15:00:43 +02:00
basti 840519a437 Merge pull request #66 from 67P/features/run-scripts
Add npm script to run app with local network
2018-06-07 17:58:02 +02:00
bumi 7c54ebcbd0 Add npm script to run app with local network
npm run start:local - sets NETWORK_ID and WEB3_PROVIDER_URL to local
network defaults
2018-06-07 17:28:13 +02:00
bumi d07c0d67bf Merge pull request #65 from 67P/feature/switch_to_infura
Update contracts, Ethereum/IPFS configs
2018-06-07 14:37:17 +00:00
basti a0ee2f7bb8 Use our new IPFS node 2018-06-07 13:32:43 +02:00
basti d070288e17 Switch from our own Parity to Infura in prod 2018-06-04 14:13:53 +02:00
basti 681f80f6ab Use contracts from npm 2018-06-04 14:13:46 +02:00
bumi eb5c87308a update kredits-contracts/truffle-kredits 2018-04-22 19:47:53 +02:00
basti 3e7fe1b5be Add npm command for running on kovan 2018-04-21 20:19:21 +02:00
basti 552ca3026d Update truffle-kredits 2018-04-21 20:19:01 +02:00
fsmanuel ba50e5a878 Merge pull request #58 from 67P/cleanup/kredits-service
Cleanup kredits service
2018-04-21 10:09:35 +00:00
fsmanuel acd1ce28f4 Use .init() instead of static .setup() 2018-04-21 11:58:17 +02:00
fsmanuel 1b2b1bd447 Fix contributor deserialize test 2018-04-21 11:34:41 +02:00
fsmanuel 7083d2369d Fix eslint 2018-04-21 11:20:47 +02:00
fsmanuel 8fed06b494 Rename initEthProvider to getEthProvider 2018-04-21 11:15:13 +02:00
fsmanuel 75650c19ab Cleanup kredits service 2018-04-21 11:05:02 +02:00
fsmanuel ade47dadde Lock ethers.js at 3.0.15 2018-04-21 10:43:03 +02:00
fsmanuel eaec617756 Merge pull request #57 from 67P/use-kredits-npm-lib
Remove kredits library files and use npm package
2018-04-18 22:50:41 +00:00
fsmanuel 2199394abc Use kredits-contracts master 2018-04-19 00:44:34 +02:00
fsmanuel 93a18a8a20 Remove unused dependencies 2018-04-19 00:10:43 +02:00
fsmanuel 2190b7af68 Remove in-repo addon 2018-04-18 22:55:49 +02:00
bumi 7da4956f1e Fix tests
These tests must be moved into the kredits-contract package. The
serialization is happening there.
They should be ported to mocha/chai probably
2018-04-18 20:19:26 +02:00
bumi 31e27865bb Use kredits-contracts from npm 2018-04-18 19:26:35 +02:00
bumi 967f213cba Remove build script to write address/abi json
those are now in the kredits-contracts library
2018-04-18 19:24:38 +02:00
bumi 0e556aa794 Remove kredits library files and use npm package
Currently it uses the package directly from git
2018-04-18 19:01:26 +02:00
basti e2ac6099e3 Merge pull request #56 from 67P/naming-conventions
Adjust for latest naming conventions
2018-04-17 13:29:08 +02:00
basti a4088e7acf Merge branch 'master' into naming-conventions 2018-04-17 12:55:20 +02:00
basti 550bd48fdb Merge pull request #55 from 67P/dev/ember_upgrade
Update Ember to 3.1
2018-04-17 12:53:30 +02:00
bumi f0f82a96fb Adjust for latest naming conventions
expept the renaming of ipfsHash to hashDigest all are already reflected
in the contracts
2018-04-17 11:43:44 +02:00
basti 8d303ea5af Fix tests, remove obsolete initializers 2018-04-17 01:38:21 +02:00
basti c0b5c36115 Don't inject kredits service in models 2018-04-16 19:23:28 +02:00
basti ba3c707da3 Fix ESLint errors 2018-04-16 19:14:37 +02:00
basti b09f19f399 Update Ember to 3.1 2018-04-16 18:11:52 +02:00
bumi 539f32911d Merge pull request #53 from 67P/docs/readme_local_contracts
Update README
2018-04-16 15:12:20 +00:00
basti e009caaf25 Add ember-cli-update 2018-04-16 13:38:27 +02:00
basti 1761ffbd3b Update README
Improve description and instructions for running against a local
development chain/node and IPFS node.
2018-04-16 12:49:49 +02:00
bumi 33d6499dd4 Merge pull request #51 from 67P/feature/contract-events
Move contract event handling into service
2018-04-15 19:58:23 +00:00
fsmanuel 3839eb2be4 Fixes and cleanup 2018-04-15 21:33:37 +02:00
fsmanuel a04ab09ed6 Move event handler assignment into method 2018-04-15 20:49:17 +02:00
fsmanuel 89d6b920b1 Remove unused Ember import 2018-04-15 20:38:24 +02:00
fsmanuel d425ed6b9e Move contract event handling into service 2018-04-15 20:36:21 +02:00
fsmanuel e4215557c8 Merge pull request #49 from 67P/feature/store-models-in-service
Move contributors + proposals to service
2018-04-15 18:35:10 +00:00
fsmanuel fcc4045a4e Merge branch 'master' into feature/store-models-in-service
# Conflicts:
#	app/controllers/index.js
#	tests/unit/controllers/index-test.js
2018-04-15 20:23:39 +02:00
fsmanuel 5bcce71b36 Merge pull request #50 from 67P/refactor/naming-conventions
Refactor/naming conventions
2018-04-15 18:17:47 +00:00
fsmanuel c9abec8a46 Merge pull request #47 from 67P/features/initialize-error-handling
Fail smarter with better error handling
2018-04-15 18:14:12 +00:00
bumi 62e195d186 remove fake bignumber from tests
We make everything bignumber-y now so that is no longer needed
2018-04-15 19:56:03 +02:00
fsmanuel e5f69a5e9a Remove unused computed 2018-04-15 19:09:16 +02:00
bumi e31ff492d5 merge master 2018-04-15 19:08:26 +02:00
fsmanuel 99bbcda709 Move contributors + proposals to service
With this change we have all the contributors + proposals all the time in one place and can update them via events.
2018-04-15 19:02:57 +02:00
bumi 8aa941e704 Rename recipientId to contributorId in proposals 2018-04-15 19:02:35 +02:00
bumi fdda30da67 Merge pull request #48 from 67P/revert-45-refactor/naming-conventions
Revert "Adjust for new naming conventions"
2018-04-15 16:42:36 +00:00
bumi 399ee27af9 Revert "Adjust for new naming conventions" 2018-04-15 16:35:31 +00:00
bumi d2c43b9ef1 Merge pull request #45 from 67P/refactor/naming-conventions
Adjust for new naming conventions
2018-04-15 16:24:17 +00:00
bumi 7e6c6a037d code style. use interpolation syntax 2018-04-15 18:02:52 +02:00
bumi e1f19a1832 Fail smarter with better error handling
This should give more insights in case of an error during loading data
from Ethereum and IPFS.
2018-04-15 16:45:09 +02:00
bumi 7b5cea3613 Adjust for new naming conventions 2018-04-10 18:46:54 +02:00
fsmanuel 5d875d23e2 Merge pull request #44 from 67P/fix/proposal-loading
Fix proposal loading
2018-04-10 17:48:53 +02:00
bumi e24fc6c81d Fix proposal loading
we changed the storage of proposals from an array to a mapping.
Because of this keys/ids now no longer start with zero but with 1.
2018-04-10 16:29:18 +02:00
bumi 72c6266474 Merge pull request #42 from 67P/kredits-module
Refactor contract interaction in its own module
2018-04-10 13:46:57 +00:00
bumi 4a711788f6 cleanup 2018-04-10 15:27:38 +02:00
bumi c373c901a2 Fix test to work with the bignumber depencency 2018-04-09 20:54:07 +02:00
bumi 32846194b6 Handle bignumber conversions
The kredits module always returns raw data from the contract. For uint
values these are bignumbers.
To handle those in the ember app we need to convert them to string or to
number.
2018-04-09 20:31:34 +02:00
bumi 1a5d33b1f0 Move IPFS handling in utils class 2018-04-09 19:28:49 +02:00
bumi 0e330a6529 Cleanup 2018-04-09 15:15:33 +02:00
bumi e9f0638ca2 Unify Contributor/Proposal saving 2018-04-09 15:05:13 +02:00
bumi ce8fef79dc Search proposal contributor by stringified ID
Kredits returns default web3/ethers.js values which are BigNumbers.
Internally we use Strings. We need to do this somewhere centrally
though.
2018-04-09 14:58:50 +02:00
bumi b9fa0e2d60 typo 2018-04-09 14:58:25 +02:00
bumi 41a04d6b53 Refactor IPFS hash handling
This now uses the multihashes dependency (which is an ipfs dependency)
to decode/encode the ipfs hashes.
2018-04-09 14:52:39 +02:00
fsmanuel e527099b00 Fix tests 2018-04-09 02:26:03 +02:00
fsmanuel 7e04cef964 Extract kredits module 2018-04-09 02:09:38 +02:00
bumi cb1208acaa Merge pull request #41 from 67P/refactor/cleanup-models
Cleanup controller, templates and service
2018-04-08 12:56:05 +00:00
fsmanuel f985ce6402 So sad that I can not force push! 2018-04-08 14:41:08 +02:00
fsmanuel f54a36f240 Use getProposalById instead of buildModel 2018-04-08 14:33:38 +02:00
bumi 4182f0993c Merge pull request #39 from 67P/refactor/contributor-management
Use contributors contract to add new contributors
2018-04-08 12:12:52 +00:00
fsmanuel 28f7466595 Cleanup controller, templates and service 2018-04-08 14:06:16 +02:00
bumi 6cfd4240de Merge pull request #38 from 67P/refactor/proposal
Refactor proposals
2018-04-08 12:02:28 +00:00
bumi 6e24015f76 Readme 2018-04-08 13:54:50 +02:00
bumi 5390c5cea8 Use contributors contract to add new contributors
related: https://github.com/67P/truffle-kredits/pull/17
2018-04-08 13:17:35 +02:00
fsmanuel adcc81ba52 Move tests 2018-04-08 02:27:56 +02:00
fsmanuel 31a1f21dcd Fix linting 2018-04-08 02:13:07 +02:00
fsmanuel b7316e15a2 Align *Proposal to *Contributor methods 2018-04-08 02:01:10 +02:00
fsmanuel ad92ffa447 Extract serializer 2018-04-08 02:01:10 +02:00
fsmanuel 45044c0f61 Refactor controller and route
I’m not sure how the queryParams are used so I removed them. I’m happy to readd them.
2018-04-08 02:01:10 +02:00
fsmanuel cc7c47cbab Refactor proposal form 2018-04-08 02:01:02 +02:00
fsmanuel 27f4006f05 Merge pull request #37 from 67P/refactor/get-contributor-by-id
Refactor getContributorById to be move generic
2018-04-08 01:32:46 +02:00
fsmanuel 576313a7b0 Update according to suggestions 2018-04-08 01:25:59 +02:00
bumi 13c3feaa69 Merge pull request #36 from 67P/fix/contributor-ipfshash
Replace missed profileHash with ipfsHash
2018-04-07 23:09:03 +00:00
fsmanuel a0dd8b925b Refactor getContributorById to be move generic
We can use this generic functions also for the upcomig proposal refactoring.
2018-04-08 00:54:42 +02:00
fsmanuel 21f187cac2 Replace missed profileHash with ipfsHash 2018-04-08 00:44:25 +02:00
fsmanuel d8951caddb Block in application route if accountNeedsUnlock
The index route never gets invoked if you reload on the proposal page. That change makes sure we always initialize `ethProvider`.
2018-04-07 22:43:22 +00:00
fsmanuel 206c657b98 Merge pull request #34 from 67P/features/balance-from-contributors
Use getContributorById call to get contributor data
2018-04-07 22:22:35 +02:00
fsmanuel 6e262537a4 Extract multihash functions 2018-04-07 18:47:16 +00:00
bumi 0587559939 Use getContributorById call to get contributor data
This new call also returns the balance of the contributor so we don't
have to make an extra call to the token contract.
2018-04-07 20:33:41 +02:00
fsmanuel 939baec1a8 Add alert message after saved 2018-04-07 17:22:33 +00:00
fsmanuel 429f670c12 Better assign 2018-04-07 17:22:33 +00:00
fsmanuel b21d2ad864 Extract serializer logic into lib
To use the same code in hubot I extracted the serializer logic into lib to later move it into `truffle-kredits` when we are done with all other common logic.
2018-04-07 17:22:33 +00:00
fsmanuel 8f841522a3 Refactor add-contributor component to DDAU 2018-04-07 17:22:33 +00:00
fsmanuel fa7db66f4e Merge pull request #31 from 67P/feature/account-badge
Add account badge
2018-04-06 17:54:53 +02:00
bumi 499b22af15 Fix JS/Ember style 2018-04-06 17:40:45 +02:00
bumi f992fb10bd Fix JS style 2018-04-06 17:00:25 +02:00
bumi 603224e906 Use ember style empty promise 2018-04-06 16:53:01 +02:00
bumi bbea07b2a7 Add account badge
This gives easy access to test if the "user/visitor" has an account, is
a contributor and if she is core.
This is used to display some account information.
2018-04-06 16:45:55 +02:00
bumi b9d78efaff More details about running the app locally 2018-04-04 15:43:52 +02:00
fsmanuel 1a58099413 Merge pull request #29 from 67P/ethers
Use ethers.js instead of the old web3
2018-04-04 14:20:55 +02:00
bumi e2d02d357c linting 2018-04-04 14:15:07 +02:00
bumi 0812ff3467 Add comment about ethProvider loading.
It is loaded in the route and thus available for the rest of the
application.
2018-04-04 14:02:27 +02:00
bumi 6dafc26deb Refactor web3 initialization
This changes how we wait for web3 and accounts to be available and
renames web3Provider to ethProvider as we could be able to swap the
different providers (ethers.js vs. web3)
2018-04-04 13:56:41 +02:00
bumi a418ddbe52 fix tests 2018-04-04 12:49:41 +02:00
bumi 6a5eb5a313 use kovan as default network id 2018-04-03 21:34:45 +02:00
bumi 6354b03717 cleanup 2018-04-03 20:32:46 +02:00
bumi 5d6b4a5255 fix linting errors 2018-04-03 19:19:11 +02:00
bumi 3f0b1ccbcd Use ethers.js instead of the old web3
the web3 we are using is old and everything will change with web3 1.0
which is currently in beta for ages.
ethers.js seems to be a bit more lightweight and implements pretty much
the same API so let's give it a try.
2018-04-03 18:35:29 +02:00
fsmanuel f00ecfd9ab Merge pull request #28 from 67P/refactor/proposal-ipfs-hash
Handle new IPFS hashes in proposal
2018-04-02 21:00:58 +02:00
fsmanuel ee0d3370b1 Fix travis 2018-04-02 20:40:59 +02:00
fsmanuel 73d7ed52dd Refactor contributor 2018-04-02 19:08:29 +02:00
fsmanuel 6a31b0d579 Rename contributor.kredits to .balance 2018-04-02 19:07:42 +02:00
bumi 869c4c0ae9 Handle new IPFS hashes in proposal 2018-04-02 18:19:22 +02:00
bumi 4be696ea62 Fix contract events handling 2018-04-02 18:18:34 +02:00
bumi 83a0d56314 Update ipfs-api dependency 2018-04-02 18:17:50 +02:00
fsmanuel 0d1418e595 Fix package.json 2018-04-01 15:24:45 +02:00
fsmanuel 7db521a72a Fix kredits-contracts dependency 2018-04-01 15:23:05 +02:00
fsmanuel e7ff606891 Update readme 2018-04-01 15:21:14 +02:00
bumi 60de3d8a31 Merge pull request #27 from 67P/truffle-contracts
Truffle contracts
2018-04-01 12:19:21 +00:00
fsmanuel f12740dfa8 Update kredits service 2018-04-01 03:20:16 +02:00
fsmanuel 020a1c5682 In repo contracts addon 2018-04-01 03:16:22 +02:00
basti 576e2741a9 More contract-related adjustments 2017-08-14 15:31:58 +02:00
basti bc19363bf9 Update kosmos-schemas 2017-08-12 14:05:31 +02:00
basti 72cf4b495e Adjust for new contract wrapper (WIP) 2017-08-12 13:59:18 +02:00
basti b1db64a07d Merge pull request #23 from 67P/feature/22-handle_locked_metamask
Ask user to unlock their account
2017-06-14 22:15:18 +02:00
basti 22affa7663 Edit account-unlock message/hint 2017-06-14 07:43:38 +02:00
galfert b125d941ba Ask user to unlock their account 2017-06-14 01:55:39 +02:00
basti 9b37f130f3 Merge pull request #21 from 67P/feature/wait_for_web3
Pause app boot until window load event fired
2017-06-13 14:37:19 +02:00
basti 993cd85e4a Improve waiting for window.onload
In case the window load event was fired before getting to the Ember
initializer, it would pause app loading indefinitely. This makes sure
that app loading is only paused, if there's still an event to wait for.
2017-06-13 14:27:09 +02:00
galfert e51586a78c Pause app boot until window load event fired
Refs #17

Allows the user provided Web3 instance to be inserted before we
try to use it.
2017-06-13 13:35:44 +02:00
basti 8aa86fa553 Merge pull request #16 from 67P/feature/ipfs-metadata
Move all metadata to IPFS with proper schemas
2017-06-09 00:28:14 +02:00
basti 510f7de693 Use HTTPS for production IPFS 2017-06-08 23:56:43 +02:00
basti 918ae5b2d1 Fix property dep 2017-06-08 17:06:19 +02:00
basti 6263254a1b Specify radix parameter for parsing amount 2017-06-08 16:53:40 +02:00
basti 99e921c302 Throw error when file couldn't be fetched from IPFS 2017-06-08 16:42:16 +02:00
basti 7f2f781a77 Don't try to save proposal when invalid 2017-06-08 16:41:53 +02:00
basti 3571cc633e Improve computed-property dependency 2017-06-08 16:41:03 +02:00
basti 2011636173 Add contribution details as title 2017-06-07 13:01:35 +02:00
basti cbb15f4d6f Fix mobile layout, add contribution kind to props 2017-06-07 12:46:03 +02:00
basti 33072900e9 Use contributor's display name for proposal list 2017-06-07 12:45:30 +02:00
basti c98a50cd42 Fix mobile layout
Unfortunately also breaks the animation when opening contributor
details for now. Couldn't get it to work again.
2017-06-07 11:11:01 +02:00
basti 1f41dbcae5 Load contribution details from IPFS 2017-06-07 10:35:57 +02:00
basti a918719b8f Decrease overall UI size a bit 2017-06-07 10:35:36 +02:00
basti 7b91e64af4 Create contribution IPFS objects, attach to proposals 2017-06-06 21:27:05 +02:00
basti 0afbeea01b Use newly generated UUID as ID for new contributor 2017-06-06 19:57:24 +02:00
basti 0510cf2389 Set contributor kind from IPFS profile 2017-06-06 19:56:55 +02:00
basti c893f91ce2 Improve logging 2017-06-06 19:56:40 +02:00
basti eced2f423f Minimal validation for amount 2017-06-06 19:56:15 +02:00
basti 6eeb505701 Use correct property for dep 2017-06-06 19:55:59 +02:00
basti 4cb3f5d8ba Use profile data from IPFS for contributor models 2017-06-06 19:05:42 +02:00
basti 372a8a9b2d Unify author value across packages 2017-06-06 13:33:41 +02:00
basti a714601c63 Update kosmos-schemas
This should fix the CI builds.
2017-06-05 22:10:10 +02:00
basti 240d446b97 Remove obsolete output 2017-06-05 22:08:13 +02:00
basti 33e81a7668 Store proper contributor profile in IPFS 2017-06-05 21:58:46 +02:00
bumi f8b75cca61 Manually set networkId
By default kredits-contracts gets the networkId from the web3 object.
This causes some random JSON RPC errors when using MetaMask:
https://github.com/MetaMask/metamask-plugin/issues/1538

This set the default networkId to 17 which is our custom private
network. For development purposes the networkId can also be set
dynamically through the config:networkId local storage entry.
2017-06-04 19:28:14 +02:00
bumi 32e1bbd617 no more evil while loop 2017-06-04 19:27:59 +02:00
basti 1de26004ab Use correct properties as dependency 2017-06-04 17:09:21 +02:00
bumi 0a66c19b0a Specify localhost as web3 provider URL for local deployments 2017-06-01 15:06:52 +02:00
basti 40d6b9905a Observe kredits property in computed array 2017-05-31 17:06:50 +02:00
basti 228825053c Update metadata, version 2017-05-31 16:54:08 +02:00
basti eda0f7a467 Hide new-proposal link for non-wallet users 2017-05-20 21:25:48 +02:00
basti 3052c9dfc5 Merge pull request #15 from 67P/feature/12-new_proposal_contributor_list
Choose contributors from list for new proposals
2017-05-20 18:52:41 +02:00
galfert dbb4094eab Merge pull request #14 from 67P/feature/5-public_ipfs
Use public IPFS node, improve config
2017-05-20 18:14:44 +02:00
galfert f875cd7b91 Choose contributors from list for new proposals 2017-05-20 02:37:11 +02:00
basti 45f17100a2 Use public IPFS node, improve config
Only use local IPFS in development. Set default parity provider URL and
overwrite if set in ENV.
2017-05-16 15:28:11 +02:00
basti dda5a8d350 Merge pull request #11 from 67P/feature/contributors_list
Contributor list improvements
2017-05-14 19:28:17 +02:00
basti 4f57ceaee9 Remove debug log statement 2017-05-14 16:27:43 +02:00
basti 2405e3e118 Show correct number for kredits distribution 2017-05-14 16:25:02 +02:00
basti d7aaadea2b Only show conributors with Kredits in list 2017-05-14 16:16:12 +02:00
basti 8d07c7d741 Update page title 2017-05-14 15:18:15 +02:00
basti 2b260bccd5 Merge pull request #10 from 67P/feature/locally-configurable-provider
Add support to locally conifgure web3 provider
2017-05-14 15:05:31 +02:00
bumi 7299394809 Add support to locally conifgure web3 provider
If no web3 is provided from the browser we check if a
config:web3ProviderUrl item is set in localstorage.
If yes we connect to that provider otherwise use the default config.
Helpful for working simultanously with different chains and providers.
2017-05-14 12:37:47 +02:00
basti 0b7afc0dd5 Update kredits, be expressive about contract options 2017-05-14 01:09:53 +02:00
basti f57e90941c Merge pull request #9 from 67P/configurable-provider
Make web3 provider URL configurable; default to infura
2017-05-13 17:06:33 +02:00
basti be6c5574ff Change default Ethereum RPC provider
Use our own Parity node.
2017-05-13 17:00:04 +02:00
bumi 859cca3820 Merge 2017-05-12 20:04:28 +02:00
bumi ba1cd1cd49 Make web3 provider URL configurable; default to infura
This uses infura (kovan network) as the default web3 provider URL
2017-05-12 17:17:20 +02:00
bumi a1f73b0307 Readme about locally deployed contracts 2017-05-08 13:02:26 +02:00
bumi 3be14b945a Use kredits-contracts from npm
This also configures the npm scripts to work with locally deployed
contracts
2017-05-08 13:00:33 +02:00
bumi 675dff7314 Add support for new splitted Token contract 2017-05-05 20:25:57 +02:00
basti 2ff76c902b Update author info 2017-05-03 17:22:06 +02:00
galfert 642015700e Don't resolve promises after rejecting them 2017-05-03 11:53:21 +02:00
galfert 969ef8d1ed Add proposal form
Closes #6

Form can be prefilled using query params on the route. The params are
`recipient`, `amount`, `url` and `ipfsHash`.
2017-05-03 11:52:42 +02:00
basti b5f8216f7f Show checkmark placeholder 2017-02-19 13:53:19 +08:00
basti e6740d9b4f Funky custom checkmark 2017-02-19 13:49:54 +08:00
basti 2e61b4c8ad Add checkbox for isCore 2017-02-19 11:44:24 +08:00
basti 62a0733a11 Use corrrect (temporary) placeholder 2017-02-19 11:16:51 +08:00
basti 09aa8a5669 Remove obsolete TODO 2017-02-19 11:15:18 +08:00
basti 01c1a9c804 Toggle metadata visibility by clicking name 2017-02-19 11:13:14 +08:00
basti 0a2f44cec8 Add metadata row to contributor list
Contains links to Ethereum address and IPFS content.
2017-02-19 10:30:56 +08:00
basti e7fa188797 Store contributor profile data in IPFS
This stores the GitHub username and UID in IPFS, and stores the
resulting IPFS hash in the contract. Now we can remove the GitHub
profile data from the contract entirely.
2017-02-18 18:44:14 +08:00
182 changed files with 28485 additions and 914 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"directory": "bower_components",
"analytics": false
}
+1 -1
View File
@@ -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
} }
+43
View File
@@ -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
}
}
]
};
+7
View File
@@ -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
-32
View File
@@ -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
View File
@@ -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
+65 -8
View File
@@ -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,11 +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
The smart contracts and their JavaScript wrapper library are developed in the
[kredits-contracts](https://github.com/67P/kredits-contracts) repo/package.
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).
[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.
These are the basic steps to get up and running:
#### 1. IPFS
Run a local IPFS deamon in offline mode.
* Make sure CORS headers are configured. See [IPFS](#ipfs) for more info.
* `ipfs daemon --offline`
#### 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
View File
@@ -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
+44 -50
View File
@@ -1,70 +1,64 @@
import Ember from 'ember'; import Component from '@ember/component';
import { and, notEmpty } from '@ember/object/computed';
import { inject as injectService } from '@ember/service';
export default Ember.Component.extend({
id: null, export default Component.extend({
realName: null, kredits: injectService(),
address: null,
ipfsHash: null,
isCore: true,
inProgress: false, // 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,
},
isValidId: function() { didInsertElement() {
return Ember.isPresent(this.get('id')); this._super(...arguments);
}.property('id'), this.reset();
},
isValidRealName: function() { // TODO: add proper address validation
return Ember.isPresent(this.get('realName')); isValidAccount: notEmpty('account'),
}.property('realName'), isValidName: notEmpty('name'),
isValidURL: notEmpty('url'),
isValidAddress: function() { isValidGithubUID: notEmpty('github_uid'),
return this.get('kredits.web3Instance').isAddress(this.get('address')); isValidGithubUsername: notEmpty('github_username'),
}.property('address'), isValidWikiUsername: notEmpty('wiki_username'),
isValid: and(
isValid: function() { 'isValidAccount',
return this.get('isValidId') && this.get('isValidRealName') && this.get('isValidAddress'); 'isValidName',
}.property('isValidAddress', 'isValidId', 'isValidRealName'), 'isValidGithubUID'
),
reset: function() { reset: function() {
this.setProperties({ this.setProperties(this.attributes);
id: null,
realName: null,
address: null,
ipfsHash: null,
isCore: true,
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( // The promise handles inProgress
this.get('address'), this.set('inProgress', saved);
this.get('realName'),
'', // TODO: this.get('ipfsHash') saved.then(() => {
this.get('isCore'),
this.get('id')
).then(contributor => {
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.');
} }
} }
}
}); });
+55 -17
View File
@@ -1,26 +1,64 @@
<form {{action "save" on="submit"}}> <form {{action "submit" on="submit"}}>
<p> <p>
{{input name="id" {{input name="is-core"
type="text" type="checkbox"
placeholder="GitHub UID (123)" id="is-core"
value=id checked=isCore}}
class=(if isValidId 'valid' '')}} <label for="is-core" class="checkbox">
Core team member (can add contributors)
</label>
</p> </p>
<p> <p>
{{input name="realname" {{input name="account"
type="text"
placeholder="Carl Sagan"
value=realName
class=(if isValidRealName 'valid' '')}}
</p>
<p>
{{input name="address"
type="text" type="text"
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
value=address value=account
class=(if isValidAddress 'valid' '')}} class=(if isValidAccount 'valid' '')}}
</p>
<p>
<select required onchange={{action (mut kind) value="target.value"}}>
<option value="person" selected={{eq kind "person"}}>Person</option>
<option value="organization" selected={{eq kind "organization"}}>Organization</option>
</select>
</p>
<p>
{{input name="name"
type="text"
placeholder="Name"
value=name
class=(if isValidName 'valid' '')}}
</p>
<p>
{{input name="url"
type="text"
placeholder="URL"
value=url
class=(if isValidURL 'valid' '')}}
</p>
<p>
{{input name="github_uid"
type="text"
placeholder="GitHub UID (123)"
value=github_uid
class=(if isValidGithubUID 'valid' '')}}
</p>
<p>
{{input name="github_username"
type="text"
placeholder="GitHub username"
value=github_username
class=(if isValidGithubUsername 'valid' '')}}
</p>
<p>
{{input name="wiki_username"
type="text"
placeholder="Wiki Username"
value=wiki_username
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>
+57
View File
@@ -0,0 +1,57 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed';
export default Component.extend({
// Default attributes used by reset
attributes: {
contributorId: null,
kind: 'community',
amount: null,
description: null,
url: null,
},
didInsertElement() {
this._super(...arguments);
this.reset();
},
contributors: [],
isValidContributor: notEmpty('contributorId'),
isValidAmount: computed('amount', function() {
return parseInt(this.amount, 10) > 0;
}),
isValidDescription: notEmpty('description'),
isValidUrl: notEmpty('url'),
isValid: and('isValidContributor',
'isValidAmount',
'isValidDescription'),
reset: function() {
this.setProperties(this.attributes);
},
actions: {
submit() {
if (!this.isValid) {
alert('Invalid data. Please review and try again.');
return;
}
let attributes = Object.keys(this.attributes);
let proposal = this.getProperties(attributes);
let saved = this.save(proposal);
// The promise handles inProgress
this.set('inProgress', saved);
saved.then(() => {
this.reset();
window.scroll(0,0);
window.alert('Proposal added.');
});
}
}
});
+44
View File
@@ -0,0 +1,44 @@
<form {{action "submit" on="submit"}}>
<p>
<select required onchange={{action (mut contributorId) value="target.value"}}>
<option value="" selected disabled hidden>Contributor</option>
{{#each contributors as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.github_username}}</option>
{{/each}}
</select>
</p>
<p>
<select required onchange={{action (mut kind) value="target.value"}}>
<option value="community" selected={{eq kind "community"}}>Community</option>
<option value="design" selected={{eq kind "design"}}>Design</option>
<option value="dev" selected={{eq kind "dev"}}>Development</option>
<option value="docs" selected={{eq kind "docs"}}>Documentation</option>
<option value="ops" selected={{eq kind "ops"}}>IT Operations</option>
</select>
</p>
<p>
{{input type="text"
placeholder="100"
value=amount
class=(if isValidAmount 'valid' '')}}
</p>
<p>
{{input type="text"
placeholder="Description"
value=description
class=(if isValidDescription 'valid' '')}}
</p>
<p>
{{input type="text"
placeholder="URL (optional)"
value=url
class=(if isValidUrl 'valid' '')}}
</p>
<p class="actions">
{{input type="submit"
disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}}
{{#link-to 'index'}}Back{{/link-to}}
</p>
</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>
+17 -3
View File
@@ -1,8 +1,22 @@
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',
selectedContributor: null,
actions: {
toggleContributorInfo(contributor) {
if (contributor.get('showMetadata')) {
contributor.set('showMetadata', false);
} else {
this.contributors.setEach('showMetadata', false);
contributor.set('showMetadata', true);
}
}
}
}); });
+16 -3
View File
@@ -1,14 +1,27 @@
<tbody> <tbody>
{{#each contributors as |contributor|}} {{#each contributors as |contributor|}}
<tr class="{{if contributor.isCurrentUser 'current-user'}}"> <tr class="{{if contributor.isCurrentUser 'current-user'}}" {{action "toggleContributorInfo" contributor}}>
<td class="person"> <td class="person">
<img class="avatar" src={{contributor.avatarURL}}> <img class="avatar" src={{contributor.avatarURL}}>
{{contributor.github_username}} {{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'}}">
<td colspan="2">
<ul>
<li><a href="https://testnet.etherscan.io/address/{{contributor.account}}">Inspect Ethereum transactions</a></li>
{{#if contributor.ipfsHash}}
<li><a href="https://ipfs.io/ipfs/{{contributor.ipfsHash}}">Inspect IPFS profile</a></li>
{{/if}}
</ul>
{{#if contributor.showMetadata}}
<pre>{{contributor.ipfsData}}</pre>
{{/if}}
</td>
</tr>
{{/each}} {{/each}}
</tbody> </tbody>
+2 -2
View File
@@ -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']
+4 -4
View File
@@ -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.');
} }
+15 -6
View File
@@ -1,11 +1,20 @@
{{#each proposals as |proposal|}} {{#each proposals as |proposal|}}
<li> <li data-proposal-id={{proposal.id}} class={{if proposal.isExecuted "confirmed" "unconfirmed"}}>
<span class="id">#{{proposal.id}}</span> <p class="meta">
Issue{{#if proposal.executed}}d{{/if}} <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>
to <span class="recipient" title="{{proposal.recipientAddress}}">{{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}}
+4 -2
View File
@@ -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 -131
View File
@@ -1,142 +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')).github_username);
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')).github_username);
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'),
contributorsSorting: ['kredits:desc'],
contributorsSorted: Ember.computed.sort('model.contributors', '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;
}); });
} }
} }
}); });
+24
View File
@@ -0,0 +1,24 @@
import Controller from '@ember/controller';
import { alias, filterBy } from '@ember/object/computed';
import { inject as injectService } from '@ember/service';
export default Controller.extend({
kredits: injectService(),
contributors: alias('kredits.contributors'),
minedContributors: filterBy('contributors', 'id'),
actions: {
save(proposal) {
// 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;
});
}
}
});
+9 -3
View File
@@ -3,16 +3,22 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Kredits</title> <title>Kosmos Kredits</title>
<meta name="description" content=""> <meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
{{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"}}
<script>
window.addEventListener('load', function() {
window.windowLoadComplete = true;
});
</script>
</head> </head>
<body> <body>
{{content-for "body"}} {{content-for "body"}}
+24 -9
View File
@@ -1,16 +1,31 @@
import Ember from 'ember'; import { computed } from '@ember/object';
import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default Ember.Object.extend({ export default EmberObject.extend({
// Contract
id: bignumber('idRaw', 'toString'),
account: null,
balance: bignumber('balanceRaw', 'toNumber'),
isCore: false,
ipfsHash: null,
address: null, // IPFS
kind: null,
name: null,
url: null,
github_username: null, github_username: null,
github_uid: null, github_uid: null,
ipfsHash: null, wiki_username: null,
kredits: null, ipfsData: '',
// 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`;
}
}),
}); });
+21 -12
View File
@@ -1,16 +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';
export default Ember.Object.extend({ export default EmberObject.extend({
// Contract
id: null, id: bignumber('idRaw', 'toString'),
creatorAddress: null, creatorAccount: null,
recipientAddress: null, contributorId: bignumber('contributorIdRaw', 'toString'),
recipientName: null, amount: bignumber('amountRaw', 'toNumber'),
votesCount: null, votesCount: bignumber('votesCountRaw', 'toNumber'),
votesNeeded: null, votesNeeded: bignumber('votesNeededRaw', 'toNumber'),
amount: null,
executed: null, executed: null,
url: null, ipfsHash: null,
ipfsHash: null
// Shortcuts
isExecuted: alias('executed'),
// IPFS
kind: null,
description: null,
details: {},
url: null,
ipfsData: '',
}); });
+5 -3
View File
@@ -1,13 +1,15 @@
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('new');
});
}); });
export default Router; export default Router;
+25 -2
View File
@@ -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();
});
}
}); });
-18
View File
@@ -1,18 +0,0 @@
import Ember from 'ember';
export default Ember.Route.extend({
kredits: Ember.inject.service(),
model() {
let kredits = this.get('kredits');
return Ember.RSVP.hash({
contributors: kredits.getContributors(),
totalSupply: kredits.getValueFromContract('totalSupply'),
contributorsCount: kredits.getValueFromContract('contributorsCount'),
proposals: kredits.getProposals()
});
}
});
-4
View File
@@ -1,4 +0,0 @@
import Ember from 'ember';
export default Ember.Route.extend({
});
-31
View File
@@ -1,31 +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 => {
return res[0].hash;
});
},
getFile(hash) {
return this.get('ipfs').cat(hash, { buffer: true }).then(res => {
return res.toString();
});
}
});
+192 -125
View File
@@ -1,164 +1,231 @@
import Ember from 'ember'; import ethers from 'npm:ethers';
import Web3 from 'npm:web3'; import Kredits from 'npm:kredits-contracts';
import RSVP from 'rsvp';
import Service from '@ember/service';
import { computed } from '@ember/object';
import { alias, notEmpty } from '@ember/object/computed';
import { isEmpty } from '@ember/utils';
import config from 'kredits-web/config/environment'; import config from 'kredits-web/config/environment';
import Contributor from 'kredits-web/models/contributor'; import Contributor from 'kredits-web/models/contributor'
import Proposal from 'kredits-web/models/proposal'; import Proposal from 'kredits-web/models/proposal'
import kreditsContracts from 'npm:kredits-contracts';
export default Ember.Service.extend({
ipfs: Ember.inject.service(), export default Service.extend({
web3Instance: null, currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
web3Provided: false, // Web3 provided (using Mist Browser, Metamask et al.) currentUser: null,
currentUserIsContributor: notEmpty('currentUser'),
web3: function() { currentUserIsCore: alias('currentUser.isCore'),
if (this.get('web3Instance')) { hasAccounts: notEmpty('currentUserAccounts'),
return this.get('web3Instance'); accountNeedsUnlock: computed('currentUserAccounts', function() {
} return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
}),
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(
window.web3.currentProvider,
{ chainId: networkId }
);
ethProvider.listAccounts().then((accounts) => {
this.set('currentUserAccounts', accounts);
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner();
resolve({
ethProvider,
ethSigner
});
});
} else { } else {
Ember.Logger.debug('[kredits] Creating new instance from npm module class'); console.debug('[kredits] Creating new instance from npm module class');
let provider = new Web3.providers.HttpProvider(config.web3ProviderUrl); networkId = parseInt(config.contractMetadata.networkId);
web3Instance = new Web3(provider); console.debug(`[kredits] networkId=${networkId} providerURL: ${config.web3ProviderUrl}`);
ethProvider = new ethers.providers.JsonRpcProvider(
config.web3ProviderUrl,
{ chainId: networkId }
);
resolve({
ethProvider: ethProvider,
ethSigner: null
});
} }
});
},
this.set('web3Instance', web3Instance); setup() {
window.web3 = web3Instance; return this.getEthProvider().then((providerAndSigner) => {
return web3Instance; let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
}.property('web3Instance'), ipfsConfig: config.ipfs
});
currentUserAccounts: function() { return kredits
return (this.get('web3Provided') && this.get('web3').eth.accounts) || []; .init()
}.property('web3', 'web3Provided'), .then((kredits) => {
this.set('kredits', kredits);
kreditsContract: function() { if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
if (this.get('kreditsContractInstance')) { this.getCurrentUser.then((contributorData) => {
return this.get('kreditsContractInstance'); this.set('currentUser', contributorData);
});
} }
return kredits;
console.log(config.ethereumChain);
let contract = kreditsContracts(this.get('web3'), config.ethereumChain)['Kredits'];
this.set('kreditsContractInstance', contract);
return contract;
}.property('web3'),
getValueFromContract(contractMethod, ...args) {
return new Ember.RSVP.Promise((resolve, reject) => {
this.get('kreditsContract')[contractMethod](...args, (err, data) => {
if (err) { reject(err); }
resolve(data);
}); });
}); });
}, },
getContributorData(i) { totalSupply: computed(function() {
let promise = new Ember.RSVP.Promise((resolve, reject) => { return this.kredits.Token.functions.totalSupply();
this.getValueFromContract('contributorAddresses', i).then(address => { }),
this.getValueFromContract('contributors', address).then(person => {
this.getValueFromContract('balanceOf', address).then(balance => { contributors: [],
let contributor = Contributor.create({ proposals: [],
address: address,
github_username: person[1], loadContributorsAndProposals() {
github_uid: person[0], return this.getContributors()
ipfsHash: person[3], .then(contributors => this.contributors.pushObjects(contributors))
kredits: balance.toNumber(), .then(() => this.getProposals())
isCurrentUser: this.get('currentUserAccounts').includes(address) .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);
}); });
Ember.Logger.debug('[kredits] contributor', contributor);
resolve(contributor);
});
});
}).catch(err => reject(err));
});
return promise;
}, },
getContributors() { getContributors() {
return this.getValueFromContract('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('proposals', i).then(p => {
let proposal = Proposal.create({ return this.kredits.Operator.addProposal(attributes)
id : i, .then((data) => {
creatorAddress : p[0], console.debug('[kredits] add proposal response', data);
recipientAddress : p[1], attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
votesCount : p[2].toNumber(), return Proposal.create(attributes);
votesNeeded : p[3].toNumber(),
amount : p[4].toNumber(),
executed : p[5],
url : p[6],
ipfsHash : p[7]
}); });
Ember.Logger.debug('[kredits] proposal', proposal);
resolve(proposal);
}).catch(err => reject(err));
});
return promise;
}, },
getProposals() { getProposals() {
return this.getValueFromContract('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); } .then((data) => {
Ember.Logger.debug('[kredits] vote response', data); console.debug('[kredits] vote response', data);
resolve(data); return data;
});
}); });
}, },
addContributor(address, name, ipfsHash, isCore, id) { getCurrentUser: computed('kredits.provider', function() {
Ember.Logger.debug('[kredits] add contributor', name, address); if (isEmpty(this.currentUserAccounts)) {
return new Ember.RSVP.Promise((resolve, reject) => { return RSVP.resolve();
this.get('kreditsContract').addContributor(address, name, ipfsHash, isCore, id, (err, data) => { }
if (err) { reject(err); } return this.kredits.Contributor
Ember.Logger.debug('[kredits] add contributor response', data); .functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
let contributor = Contributor.create({ .then((id) => {
address: address, id = id.toNumber();
github_username: name, // check if the user is a contributor or not
github_uid: id, if (id === 0) {
ipfsHash: ipfsHash, return RSVP.resolve();
kredits: 0, } else {
isCurrentUser: this.get('currentUserAccounts').includes(address) return this.kredits.Contributor.getById(id);
}); }
resolve(contributor);
}); });
}),
findProposalById(proposalId) {
return this.proposals.findBy('id', proposalId.toString());
},
// Contract events
addContractEventHandlers() {
// Operator events
this.kredits.Operator
.on('ProposalCreated', this.handleProposalCreated.bind(this))
.on('ProposalVoted', this.handleProposalVoted.bind(this))
.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));
}); });
}, },
logKreditsContract: function() { // TODO: We may want to reload that proposal to show the voter as voted
Ember.Logger.debug('[kredits] kreditsContract', this.get('kreditsContract')); handleProposalVoted(proposalId, voterId, totalVotes) {
}.on('init') let proposal = this.findProposalById(proposalId);
if (proposal) {
proposal.set('votesCount', totalVotes);
}
},
handleProposalExecuted(proposalId, contributorId, amount) {
let proposal = this.findProposalById(proposalId);
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);
},
}); });
+21
View File
@@ -0,0 +1,21 @@
$blue: #68d7fb;
$purple: #8f68fb;
$pink: #e068fb;
$green: #97fb68;
$yellow: #fbe468;
$red: #fb6868;
$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
View File
@@ -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);
}
} }
} }
+48 -15
View File
@@ -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,22 +11,20 @@
-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";
$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: 16px;
font-weight: 300; font-weight: 300;
color: #fff; color: $body-text-color;
} }
@media screen and (min-width: 900px) { @media screen and (min-width: 900px) {
@@ -42,19 +41,16 @@ h1, h2, h3, h4, h5, input, button {
section { section {
h2 { h2 {
font-size: 2.8rem; font-size: 1.4rem;
color: lightblue; color: $primary-color;
@include media($mobile) {
font-size: 2rem;
}
} }
&#contributors { &#people {
.content { .content {
p.stats { p.stats {
padding-top: 3rem; padding-top: 3rem;
font-size: 1rem; font-size: 1rem;
color: lightblue; color: white;
text-align: center; text-align: center;
span.number { span.number {
font-weight: 600; font-weight: 600;
@@ -65,13 +61,41 @@ section {
} }
} }
} }
&#contributions-by-type {
.chart {
width: 50%;
margin-left: auto;
margin-right: auto;
}
@include media($mobile) {
width: 90%;
}
}
&#proposals-open, &#proposals-closed {
.actions {
padding-top: 3rem;
font-size: 1rem;
color: $primary-color;
text-align: center;
@include media($mobile) {
padding-top: 2rem;
}
a {
color: $primary-color;
}
}
}
} }
button, input[type=submit] { 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: lightblue; color: $primary-color;
border-radius: 3px; border-radius: 3px;
font-weight: 500; font-weight: 500;
text-transform: uppercase; text-transform: uppercase;
@@ -83,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
View File
@@ -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.");
}
+425
View File
@@ -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);
}
+90
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 5962) 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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,48 @@
@charset "UTF-8";
/// Provides a quick method for setting an elements 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
View File
@@ -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
View File
@@ -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;
}
}
+51
View File
@@ -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
View File
@@ -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);
+34
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,5 @@
@mixin appearance($value) {
@include _bourbon-deprecate-for-prefixing("appearance");
@include prefixer(appearance, $value, webkit moz ms o spec);
}
+5
View File
@@ -0,0 +1,5 @@
@mixin backface-visibility($visibility) {
@include _bourbon-deprecate-for-prefixing("backface-visibility");
@include prefixer(backface-visibility, $visibility, webkit spec);
}
+44
View File
@@ -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
View File
@@ -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
View File
@@ -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));
+6
View File
@@ -0,0 +1,6 @@
@mixin calc($property, $value) {
@include _bourbon-deprecate-for-prefixing("calc");
#{$property}: -webkit-calc(#{$value});
#{$property}: calc(#{$value});
}
+67
View File
@@ -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);
}
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
);
}
}
+6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+16
View File
@@ -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;
}
+25
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+69
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
@@ -0,0 +1,53 @@
// Used for creating the source string for fonts using @font-face
// Reference: http://goo.gl/Ru1bKP
@function font-url-prefixer($asset-pipeline) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `font-url-prefixer` is deprecated and " +
"will be removed in 5.0.0.";
}
@if $asset-pipeline == true {
@return font-url;
} @else {
@return url;
}
}
@function font-source-declaration(
$font-family,
$file-path,
$asset-pipeline,
$file-formats,
$font-url) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `font-source-declaration` is deprecated " +
"and will be removed in 5.0.0.";
}
$src: ();
$formats-map: (
eot: "#{$file-path}.eot?#iefix" format("embedded-opentype"),
woff2: "#{$file-path}.woff2" format("woff2"),
woff: "#{$file-path}.woff" format("woff"),
ttf: "#{$file-path}.ttf" format("truetype"),
svg: "#{$file-path}.svg##{$font-family}" format("svg")
);
@each $key, $values in $formats-map {
@if contains($file-formats, $key) {
$file-path: nth($values, 1);
$font-format: nth($values, 2);
@if $asset-pipeline == true {
$src: append($src, font-url($file-path) $font-format, comma);
} @else {
$src: append($src, url($file-path) $font-format, comma);
}
}
}
@return $src;
}
@@ -0,0 +1,18 @@
@function _gradient-positions-parser($gradient-type, $gradient-positions) {
@if $output-bourbon-deprecation-warnings == true {
@warn "[Bourbon] [Deprecation] `_gradient-positions-parser` is " +
"deprecated and will be removed in 5.0.0.";
}
@if $gradient-positions
and ($gradient-type == linear)
and (type-of($gradient-positions) != color) {
$gradient-positions: _linear-positions-parser($gradient-positions);
}
@else if $gradient-positions
and ($gradient-type == radial)
and (type-of($gradient-positions) != color) {
$gradient-positions: _radial-positions-parser($gradient-positions);
}
@return $gradient-positions;
}

Some files were not shown because too many files have changed in this diff Show More