Compare commits

..

185 Commits

Author SHA1 Message Date
basti 525a39479b 1.5.1 2019-07-15 19:19:29 +02:00
basti c65880972a Merge pull request #138 from 67P/feature/use_ember_auto_import
Use ember-auto-import instead of ember-browserify
2019-07-15 18:54:55 +02:00
galfert c94319e6a8 Use same fork of ndjson as ipfs-http-client does 2019-07-15 18:07:58 +02:00
galfert f9dbd47922 Add ndjson dependency
Required for ipfs-http-client dependency
2019-07-15 17:50:30 +02:00
galfert 636ac30622 Use ember-auto-import instead of ember-browserify 2019-07-15 17:11:41 +02:00
galfert 791943bb95 Merge pull request #137 from 67P/feature/mark_selected_profile
Mark contributor in list as selected when opening details
2019-07-12 19:02:00 +02:00
basti d9ae654f42 Mark contributor in list as selected when opening details 2019-07-12 16:02:32 +02:00
basti 8a156f964b Remove obsolete CSS 2019-07-12 16:02:21 +02:00
basti 743a4cc3ac Add release drafter config
Configures the Release Drafter app, which automatically adds draft
release notes with the titles of merged PRs.

https://github.com/organizations/67P/settings/installations/1265652
2019-07-12 12:04:29 +02:00
basti 5f1ec4887e 1.5.0 2019-07-12 11:50:55 +02:00
bumi fe16a605c1 Merge pull request #136 from 67P/dev/local_ipfs_gateway
Use local default IPFS gateway
2019-07-12 09:43:36 +00:00
basti 9cf743b02a Merge pull request #135 from 67P/feature/contributor_profiles
Contributor profiles
2019-07-12 11:42:26 +02:00
basti d7294d9d7f Remove debug log 2019-07-12 11:39:18 +02:00
basti d8a9886442 Use local default IPFS gateway
Same as for ipfs.kosmos.org, this turns document fetches into GET
requests, which will be cached.
2019-07-11 22:24:51 +02:00
basti d83d671580 Add account links to contributor profiles 2019-07-11 15:33:17 +02:00
basti 0bbca079c5 Link site name to dashboard
Enables closing of details pane
2019-07-11 10:02:50 +02:00
basti 8bd7a77d44 Only use 3-pane layout when profile selected
Later it can be used for contribution details and other things, too.
2019-07-11 09:59:03 +02:00
basti a73a4da575 Redirect index to dashboard 2019-07-11 09:43:24 +02:00
basti 8cc1b02d19 WIP Details pane + contributor profiles
Adds a 3-pane layout option for showing details, as well as a dashboard
sub-route for showing contributor details in the new details pane.
2019-07-11 09:35:01 +02:00
basti 6b49ca26c0 Add link title for contributions
Enables a user to read the whole title in case it's cut off
2019-07-10 15:00:44 +02:00
basti ffe555e2ee Cut off long contribution titles with an ellipsis
Prevents line breaks of titles
2019-07-10 14:48:18 +02:00
basti aa98b35796 1.4.0 2019-07-10 12:31:45 +02:00
galfert b2bbbfd010 Merge pull request #134 from 67P/feature/quick_filter
Add quick filters
2019-07-08 15:52:06 +02:00
basti edd7ffd3c5 Add quick filter for contribution kind 2019-07-06 16:16:28 +02:00
basti abb7d95804 Add quick filters 2019-07-06 15:49:29 +02:00
basti 0e0b1afe3a WIP: add quick filter buttons 2019-07-05 22:01:51 +02:00
basti 29a6d79d38 Merge pull request #132 from 67P/feature/ipfs_gateway
Use new IPFS gateway for document fetches
2019-07-02 18:46:10 +02:00
basti 07c579850e 1.3.0 2019-07-02 17:32:25 +02:00
basti 091ebf4b6a Use new IPFS gateway for GET fetches
When adding the gateway URL, the wrapper will do a normal fetch to
`https://ipfs.kosmos.org/ipfs/$hash` instead of using the generic POST
requests to the IPFS API endpoint. This enables normal browser caching
for all IPFS documents in this app.
2019-07-02 17:20:19 +02:00
basti b4e89492b4 1.2.0 2019-06-27 19:51:21 +02:00
galfert f5c89fd567 Merge pull request #131 from 67P/dev/version_script
Ensure share repo is updated when versioning
2019-06-27 17:51:11 +02:00
galfert 1fbf8f0769 Merge pull request #130 from 67P/feature/sort_contributions
Sort contributions by date instead of ID
2019-06-27 17:50:21 +02:00
basti cc8952f844 Ensure share repo is updated when versioning
Currently, it's easy to forget manually pushing to master after
versioning/building. In which case things can be merged remotely, and
when pulling with rebase the version commit ends up being changed and
behind what was actually tagged.

This adds a postversion script which simply pushes to the remote
automatically after the version commit and tag have been added by npm.
2019-06-27 16:40:42 +02:00
basti 25ea20e63b Sort contributions by date instead of ID
This changes the sorting to sort first by date and time, and only then
by ID.

refs #121
2019-06-27 16:38:56 +02:00
basti e60651858d 1.1.1 2019-06-27 16:37:37 +02:00
basti 24390c6503 Merge pull request #129 from 67P/feature/disable_travis_success_notifcations
Don't send emails for successful Travis builds
2019-06-27 16:18:56 +02:00
galfert c1d5c90745 Don't send emails for successful Travis builds 2019-06-27 16:09:24 +02:00
basti 3d20bce5e3 Merge pull request #128 from 67P/feature/new_contribution_form_name
Use generic contributor name in new contribution form
2019-06-27 15:06:09 +02:00
galfert 3fe8043c76 Use generic contributor name in new contribution form
Use the generic name instead of the Github username
2019-06-27 14:57:52 +02:00
basti bad86232b4 Merge pull request #127 from 67P/bugfix/125-contribution_kind
Fix template syntax error
2019-06-12 16:47:09 +02:00
basti 7eb14ac4e2 Ignore release dir in eslint 2019-06-12 16:27:01 +02:00
basti 8b0cd8c986 Fix template syntax error
Wasn't able to select the "special" contribution kind, due to it being
bound to the wrong property.

fixes #125
2019-06-12 16:24:01 +02:00
basti 5064a472f2 1.1.0 2019-06-12 16:02:02 +02:00
basti afc5a5fe50 Merge pull request #126 from 67P/dev/production_builds
Improve production builds and deployment
2019-06-12 15:59:10 +02:00
basti f9f6786f95 Improve production builds and deployment
Removes all custom build & deploy scripts, switching to 5apps Deploy's
new build directory config instead. Builds production to `release/` when
updating the version using `npm version`.
2019-06-12 15:22:29 +02:00
basti 03f31e2d20 Merge pull request #123 from 67P/feature/122-edit_contributors
Update contributor profiles
2019-06-02 01:56:16 +02:00
basti 1c1f772ff5 Update contributor profiles
* Adds a new page for updating profiles
* Refactors the add-contributor component to allow for updating
  existing contributors
* Adds input labels to the contributor form and improves placeholders
* Adds event handlers for all contract contributor changes and uses them
  for updating the UI

refs #122
2019-05-29 16:14:11 +02:00
basti 58bd729acb Formatting 2019-05-29 16:12:48 +02:00
basti 1ea5305e6e Merge pull request #119 from 67P/feature/118-contribution_form
Submit contributions via form
2019-05-29 09:49:16 +02:00
basti e84a1cbc4e Remove obsolete scrolls 2019-05-28 16:31:56 +02:00
basti 29115d9b16 Make spacing/layout more consistent 2019-05-23 14:55:03 +02:00
basti 409d5e33f9 Add "special" category to chart test
Ensure that it's not shown in the chart (which is already the case).
2019-05-23 14:30:36 +02:00
basti 883b5dc415 Add "special" category for contributions 2019-05-23 14:22:34 +02:00
basti 16ee3b6b6b Move contributor-creation form for separate view
closes #74
2019-05-23 14:17:33 +02:00
basti 4efd6f8f88 Improve whitespace a bit 2019-05-23 14:09:20 +02:00
basti af9951ca5a Add newly created contributions to collection 2019-05-23 12:48:18 +02:00
basti c30c6c2b36 Refactor layout and button styles
* Add single-column layout for forms
* Add optional nav/buttons to section headers
2019-05-23 12:19:29 +02:00
basti 8bd4c6e6e1 Use common forms partial for form styles 2019-05-23 10:36:27 +02:00
basti 9a98b37bb6 Improve error message 2019-05-23 10:29:31 +02:00
basti fad10ec1ea Use sorted contributor list for dropdown 2019-05-23 10:29:18 +02:00
basti cec87f6caf Dat gas issue 2019-05-23 10:22:37 +02:00
basti 22a9786168 Add contribution basics 2019-05-23 09:46:09 +02:00
galfert 18be60da9b Merge pull request #116 from 67P/bugfix/vetoed_in_type_graph
Don't include vetoed contributions in graph
2019-05-10 15:58:12 +02:00
galfert 4b6c79345b Merge pull request #117 from 67P/bugfix/vetoed_in_confirmed_list
Remove vetoed contributions from confirmed list
2019-05-10 15:54:41 +02:00
basti f07100b7ea Fix component test 2019-05-10 15:01:51 +02:00
basti 7083da367d Remove vetoed contributions from confirmed list 2019-05-10 13:49:55 +02:00
basti 8720a0faab Add failing test for vetoed in confirmed list 2019-05-10 13:45:54 +02:00
basti d8b1b3d777 Don't include vetoed contributions in graph
Currently, kredits from vetoed contributions are counted in the graph
data.
2019-05-10 13:40:58 +02:00
basti 4c2d95f8f1 Merge pull request #115 from 67P/bugfix/114-contributor_list
Fix contributor list, add kredits service tests
2019-05-08 12:35:00 +02:00
galfert 9386c30d09 Update toplist balance when contribution is vetoed
Co-Authored-By: skddc <sebastian@kip.pe>
2019-05-08 12:15:51 +02:00
basti 6638691c9f Remove vetoed amounts from unconfirmed balances 2019-05-06 18:46:59 +02:00
basti d2bb70e8a1 Add failing test for vetoed amounts in toplist
This specifies the correct behaviour for unconfirmed kredits balances,
which is that amounts from vetoed contributions shouldn't be added at
all.
2019-05-06 18:32:00 +02:00
basti ef45d55521 Fix missing contributors in toplist
Adds contributors without unconfirmed contributions to the
kreditsByContributor collection.

fixes #114
2019-05-06 14:44:42 +01:00
basti f86c53de89 Add failing test for missing contributors in toplist 2019-05-06 14:23:47 +01:00
basti 82b275caaa Add some unit tests for kredits service 2019-05-06 14:21:37 +01:00
basti e524e5558d Add more test fixtures 2019-05-06 14:18:02 +01:00
basti 6593005566 Merge pull request #112 from 67P/feature/111-unconfirmed_balances
Show unconfirmed balances in toplist
2019-05-01 20:30:35 +01:00
basti cf77f20d41 Merge branch 'master' into feature/111-unconfirmed_balances 2019-05-01 19:56:43 +01:00
basti 67ee2d9a74 Merge pull request #113 from 67P/feature/98-veto_unconfirmed_contributions
Veto contributions
2019-05-01 19:43:28 +01:00
basti 48e5ddbc02 Merge pull request #108 from 67P/feature/add_contributor
Fix/update adding contributors
2019-04-29 12:44:15 +01:00
basti e1737392a7 Mark contributions as vetoed on incoming event 2019-04-28 15:26:26 +01:00
basti 00b7b380b9 Fix veto tx failing
Still have to set the gas limit for most functions in order for them to
work. :/
2019-04-28 15:06:28 +01:00
basti 9821c8b2ea Veto contributions 2019-04-28 15:05:56 +01:00
basti c6a37f7e94 Refactor and add new button styles 2019-04-28 14:42:14 +01:00
basti f25c74c7e9 Add helper functions for confirmation status 2019-04-28 14:42:09 +01:00
basti 1c49d56b19 Fix whitespace for contributor stats on mobile 2019-04-28 12:21:18 +01:00
basti 109ffd2898 Fix contributor details view in toplist 2019-04-28 11:58:58 +01:00
basti 7d5b93a62c Start basic fixtures 2019-04-27 20:54:17 +01:00
basti 6c0cb1a29c Add integration test for contributor list
And comment out the index controller one for now, as it's failing and
almost all code in the controller is framework DSL atm.
2019-04-27 20:53:36 +01:00
basti d444b86ca8 Show unconfirmed kredits in toplist
And allow toggling between confirmed and unconfirmed
2019-04-27 19:59:24 +01:00
basti 185d7c58c2 Finish group-by util
Move function to util, add tests.
2019-04-27 18:41:26 +01:00
basti 283ec1d48a Add kreditsByContributor collection
Lists contributors along with their confirmed, unconfirmed, and total
kredits amounts.
2019-04-27 17:35:05 +01:00
basti f32a34a702 Improve code comment 2019-04-27 17:34:04 +01:00
basti afa09f4b8f Move computed collections to kredits service
They should be available from any place with access to the service.
2019-04-27 17:33:25 +01:00
basti 24a3d8beb4 Merge pull request #110 from 67P/chore/more-contributions-per-page
List 200 contributions per page
2019-04-26 18:59:51 +01:00
bumi 2a7ec1073f List 200 contribtuions per page
Kicking the can a bit further down the road...
Currently we do not have any pagination and we got already more
contributions than the default per page setting.
2019-04-25 23:56:18 +02:00
bumi d02fb4716a Merge pull request #109 from 67P/feature/contribution_lists
Improve contribution lists
2019-04-25 19:45:50 +00:00
basti 49bfad51b6 Fix failing test 2019-04-25 19:28:10 +01:00
basti 25ead15014 Fix incorrect loading text
It's mostly waiting for IPFS, not Ethereum.
2019-04-25 19:22:47 +01:00
basti 85a9d1c73b Show person's name when hovering their avatar 2019-04-25 19:22:29 +01:00
basti 247fc8785a Link contributions to their main URL 2019-04-25 19:21:55 +01:00
basti efd1b371f9 Make contribution lists much more dense
We have too many contributions for them to be multiple lines by default.
2019-04-25 19:09:20 +01:00
basti 3c73ddd443 Turn user avatars into a component
This allows us to add them anywhere in the app, and also to add more
features, like e.g. hover info boxes, links, and so on.
2019-04-25 19:08:22 +01:00
basti 29e9df97fb Use Rinkeby explorer URL 2019-04-25 13:33:31 +01:00
basti fa95376fda WIP: Fix/update adding contributors 2019-04-25 13:24:36 +01:00
basti 028391386b Merge pull request #107 from 67P/feature/use-contribution-balances
Display contribution balances not token balances
2019-04-24 09:57:25 +01:00
bumi de937aceee Shorter JS syntax 2019-04-24 09:48:24 +02:00
basti 2f4807cdda Update kredits-contracts 2019-04-23 14:08:01 +01:00
bumi d1791cadfe Display contribution balances not token balances
We want to display the contribution balances (kredits earned) of the
contributors not the token balances.
2019-04-19 18:16:02 +02:00
bumi 83fd9a94b0 Merge pull request #104 from 67P/feature/handle-kredits-balances
Fix kredits balance handling
2019-04-19 13:44:35 +00:00
bumi 562a8d159b Cleanup 2019-04-19 14:54:18 +02:00
basti 1000dd12bc Merge pull request #105 from 67P/chore/package_cleanup
Clean up package.json a bit
2019-04-19 11:09:32 +01:00
bumi 6f4afaf84a List only contrbutors with more than 0 kredits
This is a bit of a problem because we list integer values, so if
somebody has less than 1 Kredit than they would still show up. Not sure
what is the best to do here. - but this should be good enough for now.
2019-04-19 11:45:53 +02:00
basti 5de33a18f5 Clean up package.json a bit
* Remove node 6 from engines
* Remove contributors array, because we don't maintain it automatically
  and it was already out of date
2019-04-19 10:45:30 +01:00
bumi 2aae2a8f90 Fix kredits balance handling
Kredits are stored on the Token contract as uint256 / bignumbers with 18
decimal points. Just like Ether and required by the ERC20 standard.
So we need to work with bignumbers and format a bignumber value here.
2019-04-19 11:37:18 +02:00
bumi 6dcbd1efbf Default to open.aragonpm.eth in production. 2019-04-18 11:23:24 +02:00
basti a2ddb2313d Merge pull request #103 from 67P/chore/weltempfaenger-release
Update kredits-contracts
2019-04-17 10:54:34 +01:00
basti 6de7f64d3d Update kosmos-schemas 2019-04-17 10:45:38 +01:00
basti d0cae59070 Update kredits-contracts 2019-04-17 10:38:36 +01:00
bumi 6bd87ae0de Fu** you failing tests. You should not be here anyway
take my rm -rf muhahaha
2019-04-16 11:04:57 +02:00
bumi 0380ea3e98 Update kredits-contracts to weltempfänger rinkeby release 2019-04-15 19:31:58 +02:00
bumi 9dba432542 Merge pull request #102 from 67P/chore/ethereum-provider
Do not rely on web3 injection with new ethereum provider standard
2019-04-10 14:19:45 +00:00
bumi da608a0012 Do not rely on web3 injection with new ethereum provider standard
when window.ethereum is available web3 is likely not available as the
browser uses the new provider standard.
So we either user the window.ethereum as provider or the
web3.currentProvider as legacy option.
2019-04-09 22:37:48 +02:00
basti 0af78e3dbd Merge pull request #100 from 67P/feature/20-contributions
Switch to contribution token instead of proposals
2019-04-09 22:25:35 +02:00
basti 5db628311e Fix confirmed/unconfirmed filter 2019-04-09 22:15:09 +02:00
basti aa28a14d04 Switch to contribution token instead of proposals
No vetos yet, and only for collections (not creation).

closes #20
2019-04-09 12:55:44 +02:00
basti 9c4e431e3b Update contracts module 2019-04-09 12:54:00 +02:00
basti 8984b1ecb7 Update start script for Rinkeby deployment 2019-04-09 10:10:17 +02:00
basti f05b0bbd8b Remove Bourbon and Neat 2019-04-09 10:09:29 +02:00
bumi bff611f866 Merge pull request #94 from 67P/refactor/healthcheck-1
Add Kredits preflight checks
2019-04-05 13:01:02 +00:00
basti b17e4c0a63 Use latest node.js LTS on Travis CI (#99)
Upgrade Travis image to 18.04, use nodejs 10
2019-04-05 13:54:16 +02:00
bumi f0b75d96db No bignumber anymore
as numbers are uint32 in the contract so no bignumber in JS anymore
2019-04-05 12:31:21 +02:00
bumi 190975b981 Merge pull request #97 from 67P/feature/87-metamask_privacy_mode
Support new Metamask/Web3 privacy mode
2019-04-04 23:24:02 +00:00
bumi e4c9a88eca Merge pull request #95 from 67P/chore/83-infura_auth
Use new Infura URL/auth
2019-04-04 23:07:00 +00:00
basti 1f8fa367a5 Support new Metamask/Web3 privacy mode
closes #87
2019-04-04 12:13:39 +02:00
basti 5202fe0f8e Use new Infura URL/auth
closes #83
2019-04-04 10:57:02 +02:00
bumi f202b34d28 Update README.md 2019-04-04 08:11:53 +00:00
bumi 07fa07eb22 Update README 2019-04-04 10:08:53 +02:00
bumi 137f9ae8bf Add Kredits preflight checks
This should identify potential config issues and print a usefull error
message. e.g. if ipfs is not available.
2019-04-03 20:06:40 +02:00
basti e44a8efa4a Merge pull request #93 from 67P/fix/contributor-list-on-proposals
Remove default contributors value in propsal component
2019-04-03 19:02:05 +02:00
basti a7b288c25f Merge pull request #92 from 67P/fix/bignumber-property
Fix bignumber computed property
2019-04-03 19:01:34 +02:00
galfert 3dbcba5eb2 Add empty array as default for contributors list 2019-04-03 14:25:59 +02:00
bumi bd2a640be0 Remove default contributors value in propsal component
It seems that this somehow prevents/overwrites the contributors which
actually should be loaded in the controller.
2019-04-03 13:46:49 +02:00
bumi 889f2c05fc Fix bignumber computed property
The ethers.js bignumber API changed in v4.x
2019-04-03 13:43:57 +02:00
basti c3f2afcaf5 Use correct contracts branch 2019-04-03 12:35:44 +02:00
basti 32c85ea56b Merge pull request #90 from 67P/update-kredits-contracts
Update kredits-contracts and ethers.js
2019-04-03 11:02:15 +02:00
basti 97210df0ba Merge pull request #91 from 67P/bugfix/72-fix_current_user_highlighting
Fix highlighting of current user in contributor list
2019-04-03 11:00:03 +02:00
galfert 88be3525b5 Fix highlighting of current user in contributor list
Fixes #72

Introduces a helper to determine if a contributor is the current user.
2019-04-03 01:35:08 +02:00
basti 7da7de1edc Make some kredits params configurable 2019-04-02 17:56:27 +02:00
basti 7592d67d69 Update package lock, use new contracts branch 2019-04-01 11:11:31 +02:00
bumi b75ac5c8b9 Update kredits-contracts and ethers.js
This uses the new ethers.js 4.x API which is required by the new
kredits-contracts.
Also Operator is now Proposal in the new kredits-contracts wrapper
2019-03-29 19:54:45 +01:00
basti 2954955e39 Merge pull request #89 from 67P/feature/84-update_ember_3_8
Update Ember to version 3.8.0
2019-03-29 13:03:55 +01:00
galfert 1fb20b1d08 Fix linting errors 2019-03-27 18:19:25 +01:00
galfert bfa0b350ac Fix template linting errors 2019-03-27 18:19:10 +01:00
galfert 487013f301 Run codemods 2019-03-26 23:07:02 +01:00
galfert b16bb2d93a Update to Ember 3.8.0 2019-03-26 22:10:30 +01:00
basti 64997d74e9 Fix wrong level for CSS mobile query 2018-07-20 12:31:32 +02:00
basti 297ed4ee45 Merge pull request #77 from 67P/feature/charts
Add doughnut chart for contribution composition
2018-07-17 20:45:32 +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
227 changed files with 31104 additions and 17025 deletions
+21
View File
@@ -0,0 +1,21 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
/release/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/package.json.ember-try
+5 -1
View File
@@ -25,10 +25,14 @@ module.exports = {
// node files // node files
{ {
files: [ files: [
'.eslintrc.js',
'.template-lintrc.js',
'ember-cli-build.js', 'ember-cli-build.js',
'testem.js', 'testem.js',
'blueprints/*/index.js',
'config/**/*.js', 'config/**/*.js',
'lib/*/index.js' 'lib/*/index.js',
'server/**/*.js'
], ],
parserOptions: { parserOptions: {
sourceType: 'script', sourceType: 'script',
+4
View File
@@ -0,0 +1,4 @@
template: |
## Changes
$CHANGES
+13 -11
View File
@@ -1,24 +1,26 @@
# See https://help.github.com/ignore-files/ for more about ignoring files. # See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output # compiled output
/dist /dist/
/tmp /tmp/
# dependencies # dependencies
/node_modules /bower_components/
/bower_components /node_modules/
# misc # misc
/.env*
/.pnp*
/.sass-cache /.sass-cache
/connect.lock /connect.lock
/coverage/* /coverage/
/libpeerconnection.log /libpeerconnection.log
npm-debug.log* /npm-debug.log*
yarn-error.log /testem.log
testem.log /yarn-error.log
.tm_properties .tm_properties
# ember-try # ember-try
.node_modules.ember-try/ /.node_modules.ember-try/
bower.json.ember-try /bower.json.ember-try
package.json.ember-try /package.json.ember-try
+9
View File
@@ -0,0 +1,9 @@
'use strict';
module.exports = {
extends: 'recommended',
rules: {
'simple-unless': false
}
};
+8 -2
View File
@@ -1,10 +1,15 @@
--- ---
language: node_js language: node_js
node_js: node_js:
- "6" - "10"
sudo: false sudo: false
dist: trusty dist: xenial
notifications:
email:
on_success: never
on_failure: always
addons: addons:
chrome: stable chrome: stable
@@ -22,5 +27,6 @@ before_install:
- npm config set spin false - npm config set spin false
script: script:
- npm run lint:hbs
- npm run lint:js - npm run lint:js
- npm test - npm test
+20 -13
View File
@@ -22,7 +22,8 @@ You will need the following things properly installed on your computer.
## 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). * Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests).
@@ -39,6 +40,7 @@ Make use of the many generators for code, try `ember help generate` for more det
### Linting ### Linting
* `npm run lint:hbs`
* `npm run lint:js` * `npm run lint:js`
* `npm run lint:js -- --fix` * `npm run lint:js -- --fix`
@@ -56,13 +58,12 @@ _(You need collaborator permissions on the 5apps Deploy project.)_
## Working with locally deployed contracts ## Working with locally deployed contracts
The smart contracts and their JavaScript wrapper library are developed in the The smart contracts and their JavaScript wrapper library are developed in the
[truffle-kredits](https://github.com/67P/truffle-kredits) repo/package. [kredits-contracts](https://github.com/67P/kredits-contracts) repo/package.
You can run `kredits-web` on your machine, against a local, simulated Ethereum You can run `kredits-web` on your machine, against a local, simulated Ethereum
network, provided e.g. by [ganache](http://truffleframework.com/ganache/) or network, provided e.g. by [ganache](http://truffleframework.com/ganache/).
[ganache-cli](https://github.com/trufflesuite/ganache-cli).
[truffle-kredits](https://github.com/67P/truffle-kredits) holds all the tools [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 to start and set up such a simulated network, as well as to deploy smart
contracts to it. contracts to it.
@@ -75,22 +76,28 @@ Run a local IPFS deamon in offline mode.
* Make sure CORS headers are configured. See [IPFS](#ipfs) for more info. * Make sure CORS headers are configured. See [IPFS](#ipfs) for more info.
* `ipfs daemon --offline` * `ipfs daemon --offline`
#### 2. truffle-kredits #### 2. kredits-contracts
Get your local Ethereum development node running. Get your local Ethereum development node running. (See [kredits-contracts README](https://github.com/67P/kredits-contracts)
for details.
* Clone [truffle-kredits](https://github.com/67P/truffle-kredits) * Clone [kredits-contracts](https://github.com/67P/kredits-contracts)
* `npm install` * `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 devchain` - runs a local development chain
* `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 run bootstrap` - bootstrap runs deploys all the contracts
* `npm link` - make the `truffle-kredits` module linkable as `kredits-contracts` on your machine * `npm link` - make the `kredits-contracts` module linkable as `kredits-contracts` on your machine (currently broken :( )
#### 3. kredits-web #### 3. kredits-web
With IPFS and Ethereum/ganache running, you can now start this Ember app. With IPFS and Ethereum/ganache running, you can now start this Ember app.
* `npm link kredits-contracts` - link the local `truffle-kredits` package (it will become `kredits-contracts` soon) * `npm link kredits-contracts` - link the local `kredits-contracts` package
* `npm start` * `npm run start:local` - WEB3_PROVIDER_URL=http://localhost:7545 must be set for local settings
If you restart the devchain your network ID changes. So kredits-contracts does not find the correct DAO Kernel address.
If that is the case the DAO address must be provided as `KREDITS_KERNEL_ADDRESS` environment variable.
* `KREDITS_KERNEL_ADDRESS=<0xYOURADDRESS> npm run start:local` - find your address with the `npm run dao:address` command in the kredits-contracts repo.
#### IPFS #### IPFS
@@ -0,0 +1,70 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed';
export default Component.extend({
attributes: null,
contributors: Object.freeze([]),
isValidContributor: notEmpty('contributorId'),
isValidKind: notEmpty('kind'),
isValidAmount: computed('amount', function() {
return parseInt(this.amount, 10) > 0;
}),
isValidDescription: notEmpty('description'),
isValidUrl: notEmpty('url'),
isValid: and('isValidContributor',
'isValidKind',
'isValidAmount',
'isValidDescription'),
init () {
this._super(...arguments);
this.set('defaultDate', new Date());
// Default attributes used by reset
this.set('attributes', {
contributorId: null,
kind: null,
date: [new Date()],
amount: null,
description: null,
url: null,
});
this.reset();
},
reset () {
this.setProperties(this.attributes);
},
actions: {
submit () {
if (!this.isValid) {
alert('Invalid data. Please review and try again.');
return;
}
const attributes = this.getProperties(Object.keys(this.attributes));
const [ date/* , time */ ] = attributes.date[0].toISOString().split('T');
attributes.date = date;
this.set('inProgress', true);
this.save(attributes)
.then((/*contribution*/) => {
this.reset();
}, err => {
console.warn(err);
window.alert('Something went wrong. Check the browser console for details.');
})
.finally(() => this.set('inProgress', false));
}
}
});
@@ -0,0 +1,58 @@
<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.name}}</option>
{{/each}}
</select>
</p>
<p>
<select required onchange={{action (mut kind) value="target.value"}}>
<option value="" selected disabled hidden>Kind</option>
<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>
<option value="special" selected={{eq kind "special"}}>Special</option>
</select>
</p>
<p>
{{ember-flatpickr
allowInput=false
altFormat="F j, Y"
altInput=true
altInputClass="date-alt"
date=date
dateFormat="Y-m-d"
defaultDate=defaultDate
maxDate=defaultDate
onChange=(action (mut date))
}}
</p>
<p>
{{input type="text"
placeholder="500"
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=inProgress
value=(if inProgress "Processing" "Save")}}
</p>
</form>
+41 -27
View File
@@ -1,27 +1,13 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { and, notEmpty } from '@ember/object/computed'; import { and, notEmpty } from '@ember/object/computed';
import { inject as injectService } from '@ember/service'; import { inject as service } from '@ember/service';
import { isPresent } from '@ember/utils';
export default Component.extend({ export default Component.extend({
kredits: injectService(),
// Default attributes used by reset kredits: service(),
attributes: {
account: null,
name: null,
kind: 'person',
url: null,
github_username: null,
github_uid: null,
wiki_username: null,
isCore: false,
},
didInsertElement() { attributes: null,
this._super(...arguments);
this.reset();
},
// TODO: add proper address validation // TODO: add proper address validation
isValidAccount: notEmpty('account'), isValidAccount: notEmpty('account'),
@@ -29,6 +15,7 @@ export default Component.extend({
isValidURL: notEmpty('url'), isValidURL: notEmpty('url'),
isValidGithubUID: notEmpty('github_uid'), isValidGithubUID: notEmpty('github_uid'),
isValidGithubUsername: notEmpty('github_username'), isValidGithubUsername: notEmpty('github_username'),
isValidGiteaUsername: notEmpty('gitea_username'),
isValidWikiUsername: notEmpty('wiki_username'), isValidWikiUsername: notEmpty('wiki_username'),
isValid: and( isValid: and(
'isValidAccount', 'isValidAccount',
@@ -36,29 +23,56 @@ export default Component.extend({
'isValidGithubUID' 'isValidGithubUID'
), ),
inProgress: false,
init () {
this._super(...arguments);
this.setDefaultAttributes();
this.reset();
},
setDefaultAttributes () {
if (isPresent(this.attributes)) { return; }
this.set('attributes', {
account: null,
name: null,
kind: 'person',
url: null,
github_username: null,
github_uid: null,
gitea_username: null,
wiki_username: null
});
},
reset: function() { reset: function() {
this.setProperties(this.attributes); this.setProperties(this.attributes);
}, },
actions: { actions: {
submit() {
submit () {
if (!this.isValid) { if (!this.isValid) {
alert('Invalid data. Please review and try again.'); alert('Invalid data. Please review and try again.');
return; return;
} }
let attributes = Object.keys(this.attributes); const attributes = Object.keys(this.attributes);
let contributor = this.getProperties(attributes); const contributor = this.getProperties(attributes);
let saved = this.save(contributor);
// The promise handles inProgress this.set('inProgress', true);
this.set('inProgress', saved);
saved.then(() => { this.save(contributor).then(() => {
this.reset(); this.reset();
window.scroll(0,0); }).catch(err => {
window.alert('Contributor added.'); console.warn(err);
window.alert('Something went wrong. Please check the browser console.');
}).finally(() => {
this.set('inProgress', false);
}); });
} }
} }
}); });
+28 -42
View File
@@ -1,64 +1,50 @@
<form {{action "submit" on="submit"}}> <form {{action "submit" on="submit"}}>
<p> <p>
{{input name="is-core" <label for="c-account">Ethereum account</label>
type="checkbox" {{input name="account" id="c-account"
id="is-core" type="text" value=account
checked=isCore}}
<label for="is-core" class="checkbox">
Core team member (can add contributors)
</label>
</p>
<p>
{{input name="account"
type="text"
placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4" placeholder="0xF18E631Ea191aE4ebE70046Fcb01a436554421BA4"
value=account class=(if isValidAccount "valid" "")}}
class=(if isValidAccount 'valid' '')}}
</p> </p>
<p> <p>
<select required onchange={{action (mut kind) value="target.value"}}> <label for="c-kind">Kind</label>
<select required onchange={{action (mut kind) value="target.value"}} id="c-kind">
<option value="person" selected={{eq kind "person"}}>Person</option> <option value="person" selected={{eq kind "person"}}>Person</option>
<option value="organization" selected={{eq kind "organization"}}>Organization</option> <option value="organization" selected={{eq kind "organization"}}>Organization</option>
</select> </select>
</p> </p>
<p> <p>
{{input name="name" <label for="c-name">Name</label>
type="text" {{input name="name" type="text" value=name placeholder="Zero Cool"
placeholder="Name" class=(if isValidName "valid" "") id="c-name"}}
value=name
class=(if isValidName 'valid' '')}}
</p> </p>
<p> <p>
{{input name="url" <label for="c-url">URL</label>
type="text" {{input name="url" type="text" value=url placeholder="http://zerocool.bit"
placeholder="URL" class=(if isValidURL "valid" "") id="c-url"}}
value=url
class=(if isValidURL 'valid' '')}}
</p> </p>
<p> <p>
{{input name="github_uid" <label for="c-github-uid">GitHub UID</label>
type="text" {{input name="github_uid" type="text" value=github_uid placeholder="2342"
placeholder="GitHub UID (123)" class=(if isValidGithubUID "valid" "") id="c-github-uid"}}
value=github_uid
class=(if isValidGithubUID 'valid' '')}}
</p> </p>
<p> <p>
{{input name="github_username" <label for="c-github-username">GitHub username</label>
type="text" {{input name="github_username" type="text" value=github_username placeholder="zerocool"
placeholder="GitHub username" class=(if isValidGithubUsername "valid" "") id="c-github-username"}}
value=github_username
class=(if isValidGithubUsername 'valid' '')}}
</p> </p>
<p> <p>
{{input name="wiki_username" <label for="c-gitea-username">Gitea username</label>
type="text" {{input name="gitea_username" type="text" value=gitea_username placeholder="zerocool"
placeholder="Wiki Username" class=(if isValidGiteaUsername "valid" "") id="c-gitea-username"}}
value=wiki_username </p>
class=(if isValidWikiUsername 'valid' '')}} <p>
<label for="c-wiki-username">Wiki username</label>
{{input name="wiki_username" type="text" value=wiki_username placeholder="ZeroCool"
class=(if isValidWikiUsername "valid" "") id="c-wiki-username"}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" {{input type="submit" disabled=inProgress
disabled=(is-pending inProgress) value=(if inProgress "Processing" "Save")}}
value=(if (is-pending inProgress) 'Processing' 'Save')}}
</p> </p>
</form> </form>
+21 -14
View File
@@ -3,21 +3,9 @@ import { computed } from '@ember/object';
import { and, notEmpty } from '@ember/object/computed'; import { and, notEmpty } from '@ember/object/computed';
export default Component.extend({ export default Component.extend({
// Default attributes used by reset
attributes: {
contributorId: null,
kind: 'community',
amount: null,
description: null,
url: null,
},
didInsertElement() { attributes: null,
this._super(...arguments); contributors: Object.freeze([]),
this.reset();
},
contributors: [],
isValidContributor: notEmpty('contributorId'), isValidContributor: notEmpty('contributorId'),
isValidAmount: computed('amount', function() { isValidAmount: computed('amount', function() {
@@ -29,6 +17,24 @@ export default Component.extend({
'isValidAmount', 'isValidAmount',
'isValidDescription'), 'isValidDescription'),
init () {
this._super(...arguments);
// Default attributes used by reset
this.set('attributes', {
contributorId: null,
kind: 'community',
amount: null,
description: null,
url: null,
});
},
didInsertElement() {
this._super(...arguments);
this.reset();
},
reset: function() { reset: function() {
this.setProperties(this.attributes); this.setProperties(this.attributes);
}, },
@@ -54,4 +60,5 @@ export default Component.extend({
}); });
} }
} }
}); });
+5 -5
View File
@@ -20,25 +20,25 @@
{{input type="text" {{input type="text"
placeholder="100" placeholder="100"
value=amount value=amount
class=(if isValidAmount 'valid' '')}} class=(if isValidAmount "valid" "")}}
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="Description" placeholder="Description"
value=description value=description
class=(if isValidDescription 'valid' '')}} class=(if isValidDescription "valid" "")}}
</p> </p>
<p> <p>
{{input type="text" {{input type="text"
placeholder="URL (optional)" placeholder="URL (optional)"
value=url value=url
class=(if isValidUrl 'valid' '')}} class=(if isValidUrl "valid" "")}}
</p> </p>
<p class="actions"> <p class="actions">
{{input type="submit" {{input type="submit"
disabled=(is-pending inProgress) disabled=(is-pending inProgress)
value=(if (is-pending inProgress) 'Processing' 'Save')}} value=(if (is-pending inProgress) "Processing" "Save")}}
{{#link-to 'index'}}Back{{/link-to}} {{#link-to "index"}}Back{{/link-to}}
</p> </p>
</form> </form>
@@ -0,0 +1,64 @@
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,
chartOptions: Object.freeze({
legend: {
display: false
}
}),
chartData: computed('contributions', function() {
let kredits = this.contributions
.filterBy('vetoed', false)
.map(c => {
return { kind: c.kind, amount: c.amount }
})
.reduce(function (kinds, c) {
if (c.kind in kinds) {
kinds[c.kind] = kinds[c.kind] + c.amount
} else {
kinds[c.kind] = 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'
],
}
})
});
@@ -0,0 +1,7 @@
<div class="chart">
{{ember-chart type="doughnut"
data=chartData
options=chartOptions
width=200
height=200}}
</div>
@@ -0,0 +1,64 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { sort } from '@ember/object/computed';
import { isPresent } from '@ember/utils';
import { inject as service } from '@ember/service';
export default Component.extend({
tagName: 'div',
classNames: ['contributions'],
showQuickFilter: false,
hideSmallContributions: false,
contributorId: null,
contributionKind: null,
kredits: service(),
contributorsSorting: Object.freeze(['name:asc']),
contributors: sort('kredits.contributors', 'contributorsSorting'),
contributorsActive: computed('contributors.[]', 'contributions', function() {
let activeIds = this.contributions.mapBy('contributorId')
.map(id => id.toString())
.uniq();
return this.contributors.filter(c => {
return activeIds.includes(c.id.toString());
});
}),
contributionKinds: computed('contributions.[]', function() {
return this.contributions.mapBy('kind').uniq();
}),
contributionsFiltered: computed('contributions.[]', 'hideSmallContributions', 'contributorId', 'contributionKind', function() {
return this.contributions.filter(c => {
let included = true;
if (this.hideSmallContributions &&
c.amount <= 500) { included = false; }
if (isPresent(this.contributorId) &&
c.contributorId.toString() !== this.contributorId.toString()) { included = false; }
if (isPresent(this.contributionKind) &&
c.kind !== this.contributionKind) { included = false; }
return included;
});
}),
actions: {
veto (contributionId) {
if (this.contractInteractionEnabled) {
this.vetoContribution(contributionId);
} else {
window.alert('Only members can veto contributions. Please ask someone to set you up.');
}
}
}
});
@@ -0,0 +1,58 @@
{{#if showQuickFilter}}
<div class="quick-filter">
<p>
<label class="filter-contributor">
Contributor:
<select onchange={{action (mut contributorId) value="target.value"}}>
<option value="" selected>all</option>
{{#each contributorsActive as |contributor|}}
<option value={{contributor.id}} selected={{eq contributorId contributor.id}}>{{contributor.name}}</option>
{{/each}}
</select>
</label>
<label class="filter-contribution-kind">
Kind:
<select onchange={{action (mut contributionKind) value="target.value"}}>
<option value="" selected>all</option>
{{#each contributionKinds as |kind|}}
<option value={{kind}} selected={{eq contributionKind kind}}>{{capitalize-string kind}}</option>
{{/each}}
</select>
</label>
<label class="filter-contribution-size">
{{input type="checkbox" checked=hideSmallContributions}}
Hide small contributions
</label>
</p>
</div>
{{/if}}
<ul class="contribution-list">
{{#each contributionsFiltered as |contribution|}}
<li data-contribution-id={{contribution.id}} class="{{contribution-status contribution}} {{if contribution.vetoed "vetoed"}}">
<p class="meta">
<span class="recipient">{{user-avatar contributor=contribution.contributor}}</span>
<span class="category {{contribution.kind}}">({{contribution.kind}})</span>
<span class="title">
{{#if contribution.url}}
<a href={{contribution.url}} target="_blank" rel="noopener" title={{contribution.description}}>{{contribution.description}}</a>
{{else}}
{{contribution.description}}
{{/if}}
</span>
</p>
<p class="kredits-amount">
<span class="amount">{{contribution.amount}}</span><span class="symbol">₭S</span>
</p>
{{#unless contribution.vetoed}}
{{#unless (is-confirmed-contribution contribution)}}
<p class="voting">
<button {{action "veto" contribution.id}} class="small danger">veto</button>
</p>
{{/unless}}
{{/unless}}
</li>
{{/each}}
</ul>
+6 -8
View File
@@ -1,20 +1,18 @@
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service';
export default Component.extend({ export default Component.extend({
router: service(),
tagName: 'table', tagName: 'table',
classNames: 'contributor-list', classNames: 'contributor-list',
selectedContributor: null, selectedContributorId: null,
actions: { actions: {
toggleContributorInfo(contributor) { openContributorDetails(contributor) {
if (contributor.get('showMetadata')) { this.router.transitionTo('dashboard.contributors.show', contributor);
contributor.set('showMetadata', false);
} else {
this.contributors.setEach('showMetadata', false);
contributor.set('showMetadata', true);
}
} }
} }
+12 -18
View File
@@ -1,26 +1,20 @@
<tbody> <tbody>
{{#each contributors as |contributor|}} {{#each contributorList as |c|}}
<tr class="{{if contributor.isCurrentUser 'current-user'}}" {{action "toggleContributorInfo" contributor}}> <tr role="button"
class="{{if (is-current-user c.contributor) "current-user"}} {{if (eq c.contributor.id selectedContributorId) "selected"}}"
{{action "openContributorDetails" c.contributor}}>
<td class="person"> <td class="person">
<img class="avatar" src={{contributor.avatarURL}}> {{user-avatar contributor=c.contributor}} {{c.contributor.name}}
{{contributor.name}}
</td> </td>
<td class="kredits"> <td class="kredits">
<span class="amount">{{contributor.balance}}</span> <span class="amount">
<span class="symbol">₭S</span> {{#if showUnconfirmedKredits}}
</td> {{c.amountTotal}}
</tr> {{else}}
<tr class="metadata {{if contributor.isCurrentUser 'current-user'}} {{if contributor.showMetadata 'visible'}}"> {{c.amountConfirmed}}
<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}} {{/if}}
</ul> </span>
{{#if contributor.showMetadata}} <span class="symbol">₭S</span>
<pre>{{contributor.ipfsData}}</pre>
{{/if}}
</td> </td>
</tr> </tr>
{{/each}} {{/each}}
@@ -0,0 +1,15 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
export default Component.extend({
tagName: "",
account: null,
iconComponentName: computed('account.site', function() {
if (this.account.site.match(/github|gitea|wiki/)) {
return 'icon-account-' + this.account.site.replace(/\./g, '-');
} else {
return 'icon-web-globe';
}
})
});
@@ -0,0 +1,4 @@
<a href={{account.url}} target="_blank" rel="noopener" title="{{account.username}} on {{account.site}}">
<span class="site">{{account.site}}</span>
{{component iconComponentName}}
</a>
@@ -0,0 +1,5 @@
import Component from '@ember/component';
export default Component.extend({
tagName: ""
});
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.0 KiB

@@ -0,0 +1,5 @@
import Component from '@ember/component';
export default Component.extend({
tagName: ""
});
@@ -0,0 +1,8 @@
<svg id="svg825" class="icon-github" width="260" height="260" enable-background="new 0 0 212.4575 186.23" version="1.1" viewBox="0 0 260 260" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<g transform="translate(23.766 36.885)">
<path d="m151.79 86.234c-7.078 0.1229-14.154 0.5605-21.232 0.7069-12.318 0.248-24.64 0.5664-36.961 0.512-11.176-0.0472-22.358-0.9728-33.518-0.7052-7.3164 0.1776-14.373 2.2948-20.131 7.584-9.9472 9.1368-12.522 21-11.314 33.807 1.92 20.357 9.2716 31.449 29.115 37.064 15.793 4.4704 31.963 5.2088 48.199 4.9744 5.254 0 10.51 0.1367 15.758-0.0292 12.723-0.4064 25.264-2.2072 37.358-6.3596 10.846-3.7225 18.799-10.691 22.572-22.166 2.6736-8.1288 3.7148-16.388 2.5544-24.853-1.7772-12.959-13.275-30.86-32.4-30.535zm-81.576 59.996c-8.836 0-16-8.9551-16-20 0-11.045 7.164-20 16-20s16 8.9553 16 20c0 11.045-7.164 20-16 20zm76 0c-8.836 0-16-8.9551-16-20 0-11.045 7.164-20 16-20s16 8.9553 16 20c0 11.045-7.164 20-16 20z" fill="none"/>
<path class="fg" d="m200.25 59.188c-2.5076-3.4493-4.3632-6.3888-3.8748-11.44 0.6796-7.0664-0.3536-14.396-1.3204-21.518-1.2188-8.9843-3.5236-17.74-7.1776-26.23-9.0332 1.9356-17.981 3.49-25.963 8.0976-8.4648 4.8908-16.695 10.215-25.08 15.25-0.9532 0.5704-2.3124 0.8848-3.3848 0.7032-17.967-3.1368-35.92-3.0196-53.898-0.086-1.3632 0.2244-3.0724-0.2345-4.2948-0.9608-4.4436-2.6604-8.6564-5.7344-13.123-8.3516-11.656-6.844-23.66-12.822-37.494-14.59-0.3928 0.8597-0.7188 1.4785-0.9612 2.1253-4.164 11.22-6.6424 22.844-7.4276 34.82-0.2872 4.3516-0.6092 8.8048 0 13.072 0.3456 2.4296-4e-3 3.8788-1.3924 5.6132-4.8516 6.0488-8.6368 12.717-11.102 20.219-4.6972 14.295-4.4004 28.861-2.2756 43.475 1.7268 11.974 4.7344 23.537 11.324 33.873 13.186 20.682 32.892 29.774 55.572 31.705 19.308 1.6464 38.8 1.1992 58.213 1.2364 13.855 0.0251 27.57-1.4924 40.715-6.6389 20.375-7.9727 33.859-22.604 40.275-44.166 3.7344-12.545 5.2324-25.396 4.8144-38.498-0.4412-13.81-4.0504-26.566-12.144-37.711zm-18.609 82.435c-3.7736 11.475-11.727 18.444-22.572 22.166-12.094 4.1524-24.635 5.9532-37.358 6.3596-5.248 0.1659-10.504 0.0292-15.758 0.0292-16.236 0.2344-32.406-0.504-48.199-4.9744-19.844-5.6152-27.195-16.707-29.115-37.064-1.2072-12.807 1.3672-24.67 11.314-33.807 5.7576-5.2892 12.814-7.4064 20.131-7.584 11.16-0.2676 22.342 0.658 33.518 0.7052 12.32 0.0544 24.642-0.264 36.961-0.512 7.0784-0.1464 14.154-0.584 21.232-0.7069 19.125-0.3243 30.623 17.576 32.4 30.535 1.1604 8.4647 0.1192 16.724-2.5544 24.853z"/>
<ellipse class="fg" cx="70.214" cy="126.23" rx="16" ry="20"/>
<ellipse class="fg" cx="146.21" cy="126.23" rx="16" ry="20"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

@@ -0,0 +1,5 @@
import Component from '@ember/component';
export default Component.extend({
tagName: ""
});
@@ -0,0 +1,8 @@
<svg class="icon-wiki" width="120" height="120" version="1.1" viewBox="0 0 120 120" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<g transform="translate(18.1,3)">
<polygon class="fg" transform="translate(-6.2,9)" points="6.2 5 45.7 20.5 45.7 91 6.2 75.5"/>
<polygon class="fg" transform="translate(-6.2,9)" points="90 5 50.5 20.5 50.5 91 90 75.5"/>
<polygon class="fg" transform="translate(-6.2,9)" points="87 88 87 81.8 60.4 91"/>
<polygon class="fg" transform="translate(-6.2,9)" points="9.2 88 9.2 81.8 34.9 91"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 703 B

@@ -0,0 +1,5 @@
import Component from '@ember/component';
export default Component.extend({
tagName: ""
});
@@ -0,0 +1,3 @@
<svg class="icon-web-globe" width="60" height="60" enable-background="new 0 0 64 64" version="1.1" viewBox="0 0 60 60" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<path class="fg" d="m48.1 14.3c-0.4-0.5-0.9-1-1.4-1.5-4.3-4.2-10.2-6.8-16.7-6.8s-12.4 2.6-16.7 6.8c-0.5 0.5-1 1-1.4 1.5-3.7 4.2-5.9 9.7-5.9 15.7s2.2 11.5 5.8 15.7c0.4 0.5 0.9 1 1.4 1.5 4.3 4.2 10.3 6.8 16.8 6.8s12.5-2.6 16.8-6.9c0.5-0.5 0.9-0.9 1.4-1.5 3.6-4.1 5.8-9.6 5.8-15.6s-2.2-11.5-5.9-15.7zm3.9 14.7h-8.1c-0.1-3-1-7.3-2.4-10.5 1.8-0.8 3.5-1.8 5.1-3 3.1 3.6 5.1 8.5 5.4 13.5zm-21-20.1c3 2.3 5.8 5.2 7.6 8.5-2.4 0.9-4.6 1.3-7.6 1.4zm-2 9.9c-3-0.1-5.2-0.6-7.6-1.4 1.8-3.3 4.6-6.3 7.6-8.5zm0 2v8.2h-10.8c0.1-3 0.9-6.8 2.3-9.8 2.7 1 5.5 1.5 8.5 1.6zm0 10.2v8c-3 0.1-5.9 0.7-8.6 1.7-1.4-3-2.1-5.7-2.3-9.7zm0 10v10.1c-3-2.3-5.8-5.3-7.7-8.6 2.4-0.9 4.7-1.4 7.7-1.5zm2 0c3 0.1 5.3 0.6 7.7 1.5-1.9 3.4-4.7 6.3-7.7 8.6zm0-2v-8h10.8c-0.1 4-0.9 6.7-2.3 9.7-2.6-1-5.5-1.5-8.5-1.7zm0-10v-8.2c3-0.1 5.8-0.7 8.5-1.6 1.4 3 2.2 6.8 2.3 9.8zm9.5-12.3c-1.7-3.2-4.1-6-6.9-8.3 4.4 0.6 8.4 2.7 11.5 5.6-1.4 1.1-3 1.9-4.6 2.7zm-14.1-8.4c-2.9 2.3-5.2 5.2-6.9 8.3-1.6-0.7-3.2-1.6-4.6-2.6 3.1-2.9 7.1-5 11.5-5.7zm-12.9 7.2c1.6 1.2 3.3 2.2 5.1 3-1.5 3.3-2.3 7.5-2.4 10.5h-8.2c0.3-5 2.3-9.9 5.5-13.5zm-5.5 15.5h8.1c0.1 4 1 7.2 2.4 10.5-1.8 0.8-3.5 1.8-5.1 3-3.2-3.7-5.1-8.5-5.4-13.5zm11.4 12.2c1.7 3.2 4.1 6.1 7 8.4-4.5-0.7-8.5-2.8-11.6-5.8 1.5-1 3-1.8 4.6-2.6zm14.2 8.5c2.9-2.4 5.3-5.2 7-8.4 1.6 0.7 3.2 1.6 4.6 2.6-3.1 3-7.2 5-11.6 5.8zm13-7.3c-1.6-1.2-3.3-2.2-5.1-3 1.4-3.2 2.3-6.5 2.4-10.5h8.1c-0.3 5.1-2.2 9.9-5.4 13.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+1 -1
View File
@@ -10,5 +10,5 @@
</svg> </svg>
</p> </p>
<p> <p>
Loading data from Ethereum... Loading data...
</p> </p>
+17 -9
View File
@@ -1,12 +1,20 @@
{{#each proposals as |proposal|}} {{#each proposals as |proposal|}}
<li data-proposal-id={{proposal.id}} title="({{proposal.kind}}) {{proposal.description}}"> <li data-proposal-id={{proposal.id}} class={{if proposal.isExecuted "confirmed" "unconfirmed"}}>
<span class="category {{proposal.kind}}">♥</span> <p class="meta">
<span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span> <span class="category {{proposal.kind}}">♥ ({{proposal.kind}})</span>
for <span class="recipient">{{proposal.contributor.name}}</span> <span class="recipient">{{proposal.contributor.name}}:</span>
<span class="votes">({{proposal.votesCount}}/{{proposal.votesNeeded}} votes)</span> </p>
<p class="kredits-amount">
{{#unless proposal.isExecuted}} <span class="amount">{{proposal.amount}}</span><span class="symbol">₭S</span>
<button {{action "confirm" proposal.id}}>+1</button> </p>
{{/unless}} <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>
<button {{action "confirm" proposal.id}}>+1</button>
{{/unless}}
</p>
</li> </li>
{{/each}} {{/each}}
+30
View File
@@ -0,0 +1,30 @@
import Component from '@ember/component';
import { alias } from '@ember/object/computed';
import { computed } from '@ember/object';
const SIZES = {
'small': '128', // pixels
'medium': '256',
'large': '512'
}
export default Component.extend({
contributor: null,
tagName: 'img',
classNames: ['avatar'],
classNameBindings: ['size'],
attributeBindings: ['src', 'title'],
size: 'small',
src: alias('avatarURL'),
title: alias('contributor.name'),
avatarURL: computed('contributor.github_uid', 'size', function() {
const github_uid = this.contributor.github_uid;
if (github_uid) {
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=${SIZES[this.size]}`;
}
})
});
+2
View File
@@ -1,4 +1,6 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({ export default Controller.extend({
kredits: service(),
}); });
+30
View File
@@ -0,0 +1,30 @@
import Controller from '@ember/controller';
import { alias, filterBy, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
contributors: alias('kredits.contributors'),
minedContributors: filterBy('contributors', 'id'),
contributorsSorting: Object.freeze(['name:asc']),
sortedContributors: sort('minedContributors', 'contributorsSorting'),
actions: {
save (contribution) {
const contributor = this.contributors.findBy('id', contribution.contributorId);
contribution.contributorIpfsHash = contributor.ipfsHash;
return this.kredits.addContribution(contribution)
.then(contribution => {
this.transitionToRoute('index');
return contribution;
});
}
}
});
+18
View File
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
actions: {
save (attributes) {
return this.kredits
.updateContributor(this.model.id, attributes)
.then(() => this.transitionToRoute('index'))
}
}
});
+18
View File
@@ -0,0 +1,18 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
actions: {
save (contributor) {
return this.kredits
.addContributor(contributor)
.then(() => this.transitionToRoute('index'))
}
}
});
+58
View File
@@ -0,0 +1,58 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { alias, not, sort } from '@ember/object/computed';
import { inject as service } from '@ember/service';
export default Controller.extend({
kredits: service(),
showDetailsPane: false,
selectedContributorId: null,
currentBlock: alias('kredits.currentBlock'),
contributions: alias('kredits.contributions'),
contributionsConfirmed: alias('kredits.contributionsConfirmed'),
contributionsUnconfirmed: alias('kredits.contributionsUnconfirmed'),
contributionsSorting: Object.freeze(['date:desc', 'time:desc', 'id:desc']),
contributionsUnconfirmedSorted: sort('contributionsUnconfirmed', 'contributionsSorting'),
contributionsConfirmedSorted: sort('contributionsConfirmed', 'contributionsSorting'),
kreditsByContributor: alias('kredits.kreditsByContributor'),
kreditsToplistSorting: computed('showUnconfirmedKredits', function(){
return this.showUnconfirmedKredits ? ['amountTotal:desc'] : ['amountConfirmed:desc'];
}),
kreditsToplist: sort('kreditsByContributor', 'kreditsToplistSorting'),
showUnconfirmedKredits: true,
hideUnconfirmedKredits: not('showUnconfirmedKredits'),
showQuickFilterUnconfirmed: false,
showQuickFilterConfirmed: false,
actions: {
vetoContribution (contributionId) {
this.kredits.veto(contributionId).then(transaction => {
console.debug('[controllers:index] Veto submitted to Ethereum blockhain: '+transaction.hash);
});
},
confirmProposal (proposalId) {
this.kredits.vote(proposalId).then(transaction => {
console.debug('[controllers:index] Vote submitted to Ethereum blockhain: '+transaction.hash);
});
},
toggleQuickFilterUnconfirmed () {
this.toggleProperty('showQuickFilterUnconfirmed');
},
toggleQuickFilterConfirmed () {
this.toggleProperty('showQuickFilterConfirmed');
}
}
});
@@ -0,0 +1,12 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
export default Controller.extend({
roleName: computed('model.isCore', 'totalKreditsEarned', function() {
if (this.model.isCore) return 'Core Contributor';
if (this.model.totalKreditsEarned <= 5000) return 'Newcomer';
return 'Contributor';
})
});
-37
View File
@@ -1,37 +0,0 @@
import Controller from '@ember/controller';
import { alias, filter, filterBy, sort } from '@ember/object/computed';
import { inject as injectService } from '@ember/service';
export default Controller.extend({
kredits: injectService(),
contributors: alias('kredits.contributors'),
contributorsWithKredits: filter('contributors', function(contributor) {
return contributor.get('balance') !== 0;
}),
contributorsSorting: ['balance:desc'],
contributorsSorted: sort('contributorsWithKredits', 'contributorsSorting'),
proposals: alias('kredits.proposals'),
proposalsOpen: filterBy('proposals', 'isExecuted', false),
proposalsClosed: filterBy('proposals', 'isExecuted', true),
proposalsSorting: ['id:desc'],
proposalsClosedSorted: sort('proposalsClosed', 'proposalsSorting'),
proposalsOpenSorted: sort('proposalsOpen', 'proposalsSorting'),
actions: {
confirmProposal(proposalId) {
this.kredits.vote(proposalId).then(transaction => {
window.confirm('Vote submitted to Ethereum blockhain: '+transaction.hash);
});
},
save(contributor) {
return this.kredits.addContributor(contributor)
.then((contributor) => {
this.contributors.pushObject(contributor);
return contributor;
});
}
}
});
+2 -2
View File
@@ -1,9 +1,9 @@
import Controller from '@ember/controller'; import Controller from '@ember/controller';
import { alias, filterBy } from '@ember/object/computed'; import { alias, filterBy } from '@ember/object/computed';
import { inject as injectService } from '@ember/service'; import { inject as service } from '@ember/service';
export default Controller.extend({ export default Controller.extend({
kredits: injectService(), kredits: service(),
contributors: alias('kredits.contributors'), contributors: alias('kredits.contributors'),
minedContributors: filterBy('contributors', 'id'), minedContributors: filterBy('contributors', 'id'),
+7
View File
@@ -0,0 +1,7 @@
import Helper from '@ember/component/helper';
export default Helper.extend({
compute([string]) {
return string.charAt(0).toUpperCase() + string.slice(1);
}
});
+20
View File
@@ -0,0 +1,20 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Helper.extend({
kredits: service(),
currentBlock: alias('kredits.currentBlock'),
compute([contribution]) {
if (contribution.vetoed) {
return 'vetoed';
} else if (contribution.confirmedAt > this.currentBlock) {
return 'unconfirmed';
} else {
return 'confirmed'
}
}
});
+15
View File
@@ -0,0 +1,15 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
export default Helper.extend({
kredits: service(),
currentBlock: alias('kredits.currentBlock'),
compute([contribution]) {
return !contribution.vetoed &&
(contribution.confirmedAt <= this.currentBlock);
}
});
+16
View File
@@ -0,0 +1,16 @@
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
import { alias } from '@ember/object/computed';
import { isPresent } from '@ember/utils';
export default Helper.extend({
kredits: service(),
currentUser: alias('kredits.currentUser'),
compute([contributor]) {
return isPresent(contributor) && isPresent(this.currentUser) &&
contributor.account === this.currentUser.account;
}
});
+28
View File
@@ -0,0 +1,28 @@
import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({
// Contract
id: null,
contributorId: null,
amount: null,
confirmedAt: bignumber('confirmedAtBlock', 'toNumber'),
vetoed: null,
ipfsHash: null,
creatorAccount: null,
// IPFS
kind: null,
description: null,
details: null,
url: null,
ipfsData: '',
init () {
this._super(...arguments);
this.set('details', {});
}
});
+5 -12
View File
@@ -1,12 +1,14 @@
import { computed } from '@ember/object';
import EmberObject from '@ember/object'; import EmberObject from '@ember/object';
import bignumber from 'kredits-web/utils/cps/bignumber'; import bignumber from 'kredits-web/utils/cps/bignumber';
import kreditsValue from 'kredits-web/utils/cps/kredits';
export default EmberObject.extend({ export default EmberObject.extend({
// Contract // Contract
id: bignumber('idRaw', 'toString'), id: bignumber('idRaw', 'toString'),
account: null, account: null,
balance: bignumber('balanceRaw', 'toNumber'), balance: kreditsValue('balanceRaw'),
totalKreditsEarned: bignumber('totalKreditsEarnedRaw', 'toNumber'),
contributionsCount: bignumber('contributionsCountRaw', 'toNumber'),
isCore: false, isCore: false,
ipfsHash: null, ipfsHash: null,
@@ -17,15 +19,6 @@ export default EmberObject.extend({
github_username: null, github_username: null,
github_uid: null, github_uid: null,
wiki_username: null, wiki_username: null,
ipfsData: '', ipfsData: ''
// Deprecated
isCurrentUser: false,
avatarURL: computed('github_uid', function() {
let github_uid = this.github_uid;
if (github_uid) {
return `https://avatars2.githubusercontent.com/u/${github_uid}?v=3&s=128`;
}
}),
}); });
+8 -1
View File
@@ -3,6 +3,7 @@ import { alias } from '@ember/object/computed';
import bignumber from 'kredits-web/utils/cps/bignumber'; import bignumber from 'kredits-web/utils/cps/bignumber';
export default EmberObject.extend({ export default EmberObject.extend({
// Contract // Contract
id: bignumber('idRaw', 'toString'), id: bignumber('idRaw', 'toString'),
creatorAccount: null, creatorAccount: null,
@@ -19,7 +20,13 @@ export default EmberObject.extend({
// IPFS // IPFS
kind: null, kind: null,
description: null, description: null,
details: {}, details: null,
url: null, url: null,
ipfsData: '', ipfsData: '',
init () {
this._super(...arguments);
this.set('details', {});
}
}); });
+12 -1
View File
@@ -7,10 +7,21 @@ const Router = EmberRouter.extend({
}); });
Router.map(function() { Router.map(function() {
this.route('spinner'); this.route('dashboard', function() {
this.route('contributors', function() {
this.route('show', { path: ':id' });
});
});
this.route('proposals', function() { this.route('proposals', function() {
this.route('new'); this.route('new');
}); });
this.route('contributions', function() {
this.route('new');
});
this.route('contributors', function() {
this.route('new');
this.route('edit', { path: ':id/edit' });
});
}); });
export default Router; export default Router;
+7 -3
View File
@@ -1,13 +1,17 @@
import { inject as injectService } from '@ember/service'; import { inject as service } from '@ember/service';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
export default Route.extend({ export default Route.extend({
kredits: injectService(), kredits: service(),
beforeModel(transition) { beforeModel(transition) {
const kredits = this.kredits; const kredits = this.kredits;
return kredits.setup().then(() => { return kredits.setup().then(() => {
kredits.get('kredits').preflightChecks().catch((error) => {
console.error('Kredits preflight check failed!');
console.error(error);
});
if (kredits.get('accountNeedsUnlock')) { if (kredits.get('accountNeedsUnlock')) {
if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) { if (confirm('It looks like you have an Ethereum wallet available. Please unlock your account.')) {
transition.retry(); transition.retry();
@@ -19,7 +23,7 @@ export default Route.extend({
}, },
afterModel() { afterModel() {
return this.kredits.loadContributorsAndProposals() return this.kredits.loadInitialData()
.then(() => { .then(() => {
this.kredits.addContractEventHandlers(); this.kredits.addContractEventHandlers();
}); });
+29
View File
@@ -0,0 +1,29 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { alias } from '@ember/object/computed';
export default Route.extend({
kredits: service(),
contributors: alias('kredits.contributors'),
model(params) {
return this.kredits.contributors.findBy('id', params.id);
},
setupController (controller, model) {
this._super(controller, model);
controller.set('attributes', {
account: model.account,
name: model.name,
kind: model.kind,
url: model.url,
github_username: model.github_username,
github_uid: model.github_uid,
gitea_username: model.gitea_username,
wiki_username: model.wiki_username
});
}
});
+32
View File
@@ -0,0 +1,32 @@
import { inject as service } from '@ember/service';
import Route from '@ember/routing/route';
import { alias } from '@ember/object/computed';
export default Route.extend({
kredits: service(),
contributors: alias('kredits.contributors'),
model (params) {
return this.contributors.findBy('id', params.id);
},
setupController (controller, model) {
this._super(controller, model);
this.controllerFor('dashboard')
.setProperties({
showDetailsPane: true,
selectedContributorId: model.id
});
},
deactivate () {
this.controllerFor('dashboard')
.setProperties({
showDetailsPane: false,
selectedContributorId: null
});
}
});
@@ -1,4 +1,9 @@
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
export default Route.extend({ export default Route.extend({
redirect () {
this.transitionTo('dashboard');
}
}); });
+239 -60
View File
@@ -1,100 +1,213 @@
import ethers from 'npm:ethers'; import ethers from 'ethers';
import Kredits from 'npm:kredits-contracts'; import Kredits from 'kredits-contracts';
import RSVP from 'rsvp'; import RSVP from 'rsvp';
import Service from '@ember/service'; import Service from '@ember/service';
import EmberObject from '@ember/object';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { alias, notEmpty } from '@ember/object/computed'; import { alias, notEmpty } from '@ember/object/computed';
import { isEmpty } from '@ember/utils'; import { isEmpty } from '@ember/utils';
import groupBy from 'kredits-web/utils/group-by';
import formatKredits from 'kredits-web/utils/format-kredits';
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 Contribution from 'kredits-web/models/contribution'
export default Service.extend({ export default Service.extend({
currentBlock: null,
currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded. currentUserAccounts: null, // default to not having an account. this is the wen web3 is loaded.
currentUser: null, currentUser: null,
contributors: null,
contributions: null,
proposals: null,
currentUserIsContributor: notEmpty('currentUser'), currentUserIsContributor: notEmpty('currentUser'),
currentUserIsCore: alias('currentUser.isCore'), currentUserIsCore: alias('currentUser.isCore'),
hasAccounts: notEmpty('currentUserAccounts'), hasAccounts: notEmpty('currentUserAccounts'),
accountNeedsUnlock: computed('currentUserAccounts', function() { accountNeedsUnlock: computed('currentUserAccounts', function() {
return this.currentUserAccounts && isEmpty(this.currentUserAccounts); return this.currentUserAccounts && isEmpty(this.currentUserAccounts);
}), }),
// this is called in the routes beforeModel(). So it is initialized before everything else contributionsUnconfirmed: computed('contributions.[]', 'currentBlock', function() {
// and we can rely on the ethProvider and the potential currentUserAccounts to be available return this.contributions.filter(contribution => {
getEthProvider: function() { return contribution.confirmedAt > this.currentBlock;
return new RSVP.Promise((resolve) => { });
let ethProvider; }),
let networkId;
let networkName; contributionsConfirmed: computed('contributions.[]', 'currentBlock', function() {
if (typeof window.web3 !== 'undefined') { return this.contributions
console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask'); .filterBy('vetoed', false)
networkId = parseInt(window.web3.version.network); .filter(contribution => {
ethProvider = new ethers.providers.Web3Provider( return contribution.confirmedAt <= this.currentBlock;
window.web3.currentProvider, });
{ chainId: networkId } }),
);
ethProvider.listAccounts().then((accounts) => { kreditsByContributor: computed('contributionsUnconfirmed.@each.vetoed', 'contributors.[]', function() {
this.set('currentUserAccounts', accounts); const contributionsUnconfirmed = this.contributionsUnconfirmed.filterBy('vetoed', false);
resolve(ethProvider, ethProvider.getSigner()); const contributionsGrouped = groupBy(contributionsUnconfirmed, 'contributorId');
}); const contributorsWithUnconfirmed = contributionsGrouped.map(c => c.value.toString());
} else { const contributorsWithOnlyConfirmed = this.contributors.reject(c => contributorsWithUnconfirmed.includes(c.id))
const kreditsByContributor = contributionsGrouped.map(c => {
const amountUnconfirmed = c.items.mapBy('amount').reduce((a, b) => a + b);
const contributor = this.contributors.findBy('id', c.value.toString());
return EmberObject.create({
contributor: contributor,
amountUnconfirmed: amountUnconfirmed,
amountConfirmed: contributor.totalKreditsEarned,
amountTotal: contributor.totalKreditsEarned + amountUnconfirmed
})
});
contributorsWithOnlyConfirmed.forEach(c => {
kreditsByContributor.push(EmberObject.create({
contributor: c,
amountUnconfirmed: 0,
amountConfirmed: c.totalKreditsEarned,
amountTotal: c.totalKreditsEarned
}));
})
return kreditsByContributor;
}),
init () {
this._super(...arguments);
this.set('contributors', []);
this.set('proposals', []);
this.set('contributions', []);
},
// This is called in the application route's beforeModel(). So it is
// initialized before everything else, and we can rely on the ethProvider and
// the potential currentUserAccounts to be available
getEthProvider () {
let ethProvider;
return new RSVP.Promise(async (resolve) => {
function instantiateWithoutAccount () {
console.debug('[kredits] Creating new instance from npm module class'); console.debug('[kredits] Creating new instance from npm module class');
networkId = parseInt(config.contractMetadata.networkId); console.debug(`[kredits] providerURL: ${config.web3ProviderUrl}`);
networkName = config.contractMetadata.networkName; ethProvider = new ethers.providers.JsonRpcProvider(config.web3ProviderUrl);
console.log(networkName); resolve({
ethProvider = new ethers.providers.InfuraProvider({ chainId: networkId, name: networkName }); ethProvider: ethProvider,
resolve(ethProvider, null); ethSigner: null
});
}
async function instantiateWithAccount (web3Provider, context) {
console.debug('[kredits] Using user-provided instance, e.g. from Mist browser or Metamask');
ethProvider = new ethers.providers.Web3Provider(web3Provider);
// const network = await ethProvider.getNetwork();
ethProvider.listAccounts().then(accounts => {
context.set('currentUserAccounts', accounts);
const ethSigner = accounts.length === 0 ? null : ethProvider.getSigner();
resolve({
ethProvider,
ethSigner
});
});
}
if (window.ethereum) {
try {
// Request account access if needed
await window.ethereum.enable();
// Acccounts now exposed
instantiateWithAccount(window.ethereum, this);
} catch (error) {
instantiateWithoutAccount();
}
}
// Legacy dapp browsers...
else if (window.web3) {
instantiateWithAccount(window.web3.currentProvider, this);
}
// Non-dapp browsers...
else {
instantiateWithoutAccount();
} }
}); });
}, },
setup() { setup () {
return this.getEthProvider().then((ethProvider, ethSigner) => { return this.getEthProvider().then((providerAndSigner) => {
let kredits = new Kredits(ethProvider, ethSigner); let kredits = new Kredits(providerAndSigner.ethProvider, providerAndSigner.ethSigner, {
addresses: { Kernel: config.kreditsKernelAddress },
apm: config.kreditsApmDomain,
ipfsConfig: config.ipfs
});
return kredits return kredits
.init() .init()
.then((kredits) => { .then(async (kredits) => {
this.set('kredits', kredits); this.set('kredits', kredits);
this.set('currentBlock', await kredits.provider.getBlockNumber());
if (this.currentUserAccounts && this.currentUserAccounts.length > 0) { if (this.currentUserAccounts && this.currentUserAccounts.length > 0) {
this.getCurrentUser.then((contributorData) => { this.getCurrentUser.then((contributorData) => {
this.set('currentUser', contributorData); this.set('currentUser', contributorData);
}); });
} }
return kredits; return kredits;
}); });
}); });
}, },
totalSupply: computed(function() { totalSupply: computed(function() {
return this.kredits.Token.functions.totalSupply(); return this.kredits.Token.functions.totalSupply().then(total => {
return formatKredits(total);
})
}), }),
contributors: [], totalKreditsEarned: computed(function() {
proposals: [], return this.kredits.Contribution.functions.totalKreditsEarned(true)
.then(total => total.toNumber());
}),
loadContributorsAndProposals() {
loadInitialData () {
return this.getContributors() return this.getContributors()
.then(contributors => this.contributors.pushObjects(contributors)) .then(contributors => this.contributors.pushObjects(contributors))
.then(() => this.getProposals()) .then(() => this.getContributions())
.then(proposals => this.proposals.pushObjects(proposals)) .then(contributions => this.contributions.pushObjects(contributions))
}, },
addContributor(attributes) { addContributor (attributes) {
if (attributes.github_uid) {
const uidInt = parseInt(attributes.github_uid);
attributes.github_uid = uidInt;
}
console.debug('[kredits] add contributor', attributes); console.debug('[kredits] add contributor', attributes);
return this.kredits.Contributor.add(attributes) return this.kredits.Contributor.add(attributes, { gasLimit: 350000 })
.then((data) => { .then(data => {
console.debug('[kredits] add contributor response', data); console.debug('[kredits] add contributor response', data);
return Contributor.create(attributes);
}); });
}, },
getContributors() { updateContributor (id, attributes) {
if (attributes.github_uid) {
const uidInt = parseInt(attributes.github_uid);
attributes.github_uid = uidInt;
}
console.debug('[kredits] update contributor', attributes);
return this.kredits.Contributor.updateProfile(id, attributes, { gasLimit: 350000 })
.then(data => {
console.debug('[kredits] updateProfile response', data);
});
},
getContributors () {
return this.kredits.Contributor.all() return this.kredits.Contributor.all()
.then((contributors) => { .then((contributors) => {
return contributors.map((contributor) => { return contributors.map((contributor) => {
@@ -103,10 +216,25 @@ export default Service.extend({
}); });
}, },
addProposal(attributes) { addContribution (attributes) {
console.debug('[kredits] add contribution', attributes);
return this.kredits.Contribution.addContribution(attributes, { gasLimit: 300000 })
.then(data => {
console.debug('[kredits] add contribution response', data);
attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
const contribution = Contribution.create(attributes);
// TODO receive from wrapper
contribution.set('confirmedAtBlock', data.blockNumber + 40320);
this.contributions.pushObject(contribution);
return contribution;
});
},
addProposal (attributes) {
console.debug('[kredits] add proposal', attributes); console.debug('[kredits] add proposal', attributes);
return this.kredits.Operator.addProposal(attributes) return this.kredits.Proposal.addProposal(attributes)
.then((data) => { .then((data) => {
console.debug('[kredits] add proposal response', data); console.debug('[kredits] add proposal response', data);
attributes.contributor = this.contributors.findBy('id', attributes.contributorId); attributes.contributor = this.contributors.findBy('id', attributes.contributorId);
@@ -114,8 +242,8 @@ export default Service.extend({
}); });
}, },
getProposals() { getProposals () {
return this.kredits.Operator.all() return this.kredits.Proposal.all()
.then((proposals) => { .then((proposals) => {
return proposals.map((proposal) => { return proposals.map((proposal) => {
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString()); proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
@@ -124,16 +252,36 @@ export default Service.extend({
}); });
}, },
vote(proposalId) { getContributions () {
return this.kredits.Contribution.all({page: {size: 200}})
.then(contributions => {
return contributions.map(contribution => {
contribution.contributor = this.contributors.findBy('id', contribution.contributorId.toString());
return Contribution.create(contribution);
});
});
},
vote (proposalId) {
console.debug('[kredits] vote for', proposalId); console.debug('[kredits] vote for', proposalId);
return this.kredits.Operator.functions.vote(proposalId) return this.kredits.Proposal.functions.vote(proposalId)
.then((data) => { .then(data => {
console.debug('[kredits] vote response', data); console.debug('[kredits] vote response', data);
return data; return data;
}); });
}, },
veto (contributionId) {
console.debug('[kredits] veto against', contributionId);
return this.kredits.Contribution.functions.veto(contributionId, { gasLimit: 300000 })
.then(data => {
console.debug('[kredits] veto response', data);
return data;
});
},
getCurrentUser: computed('kredits.provider', function() { getCurrentUser: computed('kredits.provider', function() {
if (isEmpty(this.currentUserAccounts)) { if (isEmpty(this.currentUserAccounts)) {
return RSVP.resolve(); return RSVP.resolve();
@@ -141,7 +289,6 @@ export default Service.extend({
return this.kredits.Contributor return this.kredits.Contributor
.functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject')) .functions.getContributorIdByAddress(this.get('currentUserAccounts.firstObject'))
.then((id) => { .then((id) => {
id = id.toNumber();
// check if the user is a contributor or not // check if the user is a contributor or not
if (id === 0) { if (id === 0) {
return RSVP.resolve(); return RSVP.resolve();
@@ -156,19 +303,51 @@ export default Service.extend({
}, },
// Contract events // Contract events
addContractEventHandlers() { addContractEventHandlers () {
// Operator events this.kredits.Contributor
this.kredits.Operator .on('ContributorProfileUpdated', this.handleContributorChange.bind(this))
.on('ContributorAccountUpdated', this.handleContributorChange.bind(this))
.on('ContributorAdded', this.handleContributorChange.bind(this))
this.kredits.Contribution
.on('ContributionVetoed', this.handleContributionVetoed.bind(this))
this.kredits.Proposal
.on('ProposalCreated', this.handleProposalCreated.bind(this)) .on('ProposalCreated', this.handleProposalCreated.bind(this))
.on('ProposalVoted', this.handleProposalVoted.bind(this)) .on('ProposalVoted', this.handleProposalVoted.bind(this))
.on('ProposalExecuted', this.handleProposalExecuted.bind(this)); .on('ProposalExecuted', this.handleProposalExecuted.bind(this));
// Token events
this.kredits.Token this.kredits.Token
.on('Transfer', this.handleTransfer.bind(this)); .on('Transfer', this.handleTransfer.bind(this));
}, },
handleProposalCreated(proposalId) { async handleContributorChange (contributorId, ...args) {
console.debug('[kredits] Contributor add/update event received for ID', contributorId);
console.debug('[kredits] Event data:', args);
const contributorData = await this.kredits.Contributor.getById(contributorId);
const newContributor = Contributor.create(contributorData);
const oldContributor = this.contributors.findBy('id', contributorId.toString());
if (oldContributor) {
console.debug('[kredits] old contributor', oldContributor);
this.contributors.removeObject(oldContributor);
}
console.debug('[kredits] new contributor', newContributor);
this.contributors.pushObject(newContributor);
},
handleContributionVetoed (contributionId) {
console.debug('[kredits] ContributionVetoed event received for ', contributionId);
const contribution = this.contributions.findBy('id', contributionId);
console.debug('[kredits] contribution', contribution);
if (contribution) {
contribution.set('vetoed', true);
}
},
handleProposalCreated (proposalId) {
let proposal = this.findProposalById(proposalId); let proposal = this.findProposalById(proposalId);
if (proposal) { if (proposal) {
@@ -176,7 +355,7 @@ export default Service.extend({
return; return;
} }
this.kredits.Operator.getById(proposalId) this.kredits.Proposal.getById(proposalId)
.then((proposal) => { .then((proposal) => {
proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString()); proposal.contributor = this.contributors.findBy('id', proposal.contributorId.toString());
this.proposals.pushObject(Proposal.create(proposal)); this.proposals.pushObject(Proposal.create(proposal));
@@ -184,7 +363,7 @@ export default Service.extend({
}, },
// TODO: We may want to reload that proposal to show the voter as voted // TODO: We may want to reload that proposal to show the voter as voted
handleProposalVoted(proposalId, voterId, totalVotes) { handleProposalVoted (proposalId, voterId, totalVotes) {
let proposal = this.findProposalById(proposalId); let proposal = this.findProposalById(proposalId);
if (proposal) { if (proposal) {
@@ -192,7 +371,7 @@ export default Service.extend({
} }
}, },
handleProposalExecuted(proposalId, contributorId, amount) { handleProposalExecuted (proposalId, contributorId, amount) {
let proposal = this.findProposalById(proposalId); let proposal = this.findProposalById(proposalId);
if (proposal.get('isExecuted')) { if (proposal.get('isExecuted')) {
@@ -204,10 +383,10 @@ export default Service.extend({
this.contributors this.contributors
.findBy('id', contributorId.toString()) .findBy('id', contributorId.toString())
.incrementProperty('balance', amount.toNumber()); .incrementProperty('balance', amount);
}, },
handleTransfer(from, to, value) { handleTransfer (from, to, value) {
value = value.toNumber(); value = value.toNumber();
this.contributors this.contributors
+56
View File
@@ -0,0 +1,56 @@
button, input[type=submit], .button {
display: inline-block;
padding: 0.6rem 2rem;
background-color: rgba(22, 21, 40, 0.6);
border: 1px solid rgba(22, 21, 40, 1);
border-radius: 3px;
color: $primary-color;
font-weight: 500;
text-decoration: none;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 0.1em;
&+button, &+input[type=submit], &+.button {
margin-left: 0.5rem;
}
&:hover {
background-color: rgba(22, 21, 40, 0.8);
}
&:active, &.active {
border-color: $primary-color;
}
&.small {
font-size: 0.8rem;
padding: 0.2rem 0.8rem;
}
&.danger {
color: $red;
background-color: rgba(40, 21, 21, 0.6);
border-color: rgba(40, 21, 21, 1);
&:hover {
background-color: rgba(40, 21, 21, 0.8);
}
&:active, &.active {
border-color: $red;
}
}
&.green {
color: $green;
background-color: rgba(21, 40, 21, 0.6);
border-color: rgba(21, 40, 21, 1);
&:hover {
background-color: rgba(21, 40, 21, 0.8);
}
&:active, &.active {
border-color: $green;
}
}
}
+8 -1
View File
@@ -5,4 +5,11 @@ $green: #97fb68;
$yellow: #fbe468; $yellow: #fbe468;
$red: #fb6868; $red: #fb6868;
$primaryColor: $blue; $primary-color: $blue;
$body-text-color: #fff;
#topbar {
background-color: rgba(255, 0, 255, .2);
}
@@ -1,20 +1,28 @@
section#add-contributor, section#add-proposal { section#add-contributor,
section#add-contribution,
section#add-proposal {
form { form {
p { p {
margin-bottom: 1rem; margin-bottom: 1.5rem;
&.actions { &.actions {
padding-top: 1rem; padding-top: 1.5rem;
text-align: center; text-align: center;
a { a {
color: $primaryColor; color: $primary-color;
margin-left: 1rem; margin-left: 1rem;
} }
} }
} }
label {
display: block;
margin-bottom: 0.5rem;
opacity: 0.7;
}
input[type=text], select { input[type=text], select {
width: 100%; width: 100%;
padding: 1rem; padding: 1rem;
@@ -23,17 +31,22 @@ section#add-contributor, section#add-proposal {
background-color: rgba(22, 21, 40, 0.3); background-color: rgba(22, 21, 40, 0.3);
color: #fff; color: #fff;
font-size: 1.2rem; font-size: 1.2rem;
transition: border-color 0.1s linear;
&:focus, &.valid { &:focus, &.valid {
background-color: rgba(22, 21, 40, 0.6); background-color: rgba(22, 21, 40, 0.6);
} }
@include placeholder { &:focus {
border-color: $blue;
}
&::placeholder {
color: rgba(238, 238, 238, 0.5); color: rgba(238, 238, 238, 0.5);
} }
} }
select { select {
-webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
-webkit-appearance: none;
appearance: none; appearance: none;
border-radius: 0; border-radius: 0;
background-color: rgba(22, 21, 40, 0.6); background-color: rgba(22, 21, 40, 0.6);
+80 -22
View File
@@ -1,31 +1,89 @@
section { main {
@include outer-container; padding: 1rem 2rem;
margin-bottom: 8rem;
&:first-of-type { @include media-max(small) {
margin-top: 6rem; padding: 1rem;
} }
@include media($mobile) { &#dashboard {
margin-bottom: 5rem; width: 100%;
&:first-of-type { display: grid;
margin-top: 2rem; grid-row-gap: 2rem;
} grid-template-areas:
"stats"
"contributions";
} }
header { &.center-column {
@include span-columns(12); display: flex;
padding-bottom: 3rem; flex-direction: column;
text-align: center; align-items: center;
// background-color: purple;
@include media($mobile) { section {
@include span-columns(10); width: 600px;
@include shift(1); max-width: 100%;
padding-bottom: 2rem;
}
div.content { header {
@include span-columns(12); text-align: center;
}
}
}
}
@media (min-width: 550px) {
main {
&#dashboard {
grid-column-gap: 4rem;
grid-row-gap: 2rem;
grid-template-columns: 2fr 4fr;
grid-template-areas:
"stats contributions";
&.with-details {
grid-column-gap: 3rem;
grid-template-columns: 2fr 4fr 2fr;
grid-template-areas:
"stats contributions details";
}
}
}
}
main section {
margin-bottom: 5rem;
&:first-of-type {
margin-top: 2rem;
}
@include media-max(small) {
margin-bottom: 5rem;
}
header {
margin-bottom: 3rem;
&.with-nav {
display: grid;
grid-template-columns: auto 5rem;
grid-template-areas:
"title" "actions";
}
h2 {
display: inline-block;
// padding-left: 1.2rem;
}
nav {
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
}
@include media-max(small) {
margin-bottom: 2rem;
} }
} }
} }
+33
View File
@@ -0,0 +1,33 @@
$breakpoints-max: (
small: 600px,
medium: 960px,
large: 1280px
);
$breakpoints-min: (
small: 601px,
medium: 961px,
large: 1281px
);
@mixin media-max($screen-size) {
@if map-has-key($breakpoints-max, $screen-size) {
@media (max-width: map-get($breakpoints-max, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
@mixin media-min($screen-size) {
@if map-has-key($breakpoints-min, $screen-size) {
@media (min-width: map-get($breakpoints-min, $screen-size)) {
@content;
}
} @else {
// Debugging
@warn "'#{$screen-size}' has not been declared as a breakpoint."
}
}
+39 -37
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,23 +11,19 @@
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
@import "bourbon/bourbon"; @import "mediaqueries";
@import "settings/neat";
@import "neat/neat";
@import "settings/breakpoints";
@import "layout"; @import "layout";
@import "colors"; @import "colors";
$font-family-sans: 'Open Sans', sans-serif; $font-family-sans: 'Open Sans', sans-serif;
body { body {
background-image: url('/img/bg.jpg'); font-family: $font-family-sans;
font-weight: 300;
background-image: linear-gradient(to bottom, rgba(22, 21, 40, .4), rgba(0, 0, 0, .75)), url('/img/bg.jpg');
background-repeat: none; background-repeat: none;
background-attachment: fixed; background-attachment: fixed;
font-family: $font-family-sans; color: $body-text-color;
font-size: 14px;
font-weight: 300;
color: #fff;
} }
@media screen and (min-width: 900px) { @media screen and (min-width: 900px) {
@@ -43,26 +40,32 @@ h1, h2, h3, h4, h5, input, button {
section { section {
h2 { h2 {
font-size: 2.8rem; font-size: 1.4rem;
color: $primaryColor; color: $primary-color;
@include media($mobile) {
font-size: 2rem;
}
} }
&#contributors { &#people {
.content { .content {
p.stats { p.stats {
padding-top: 3rem; margin-bottom: 1rem;
font-size: 1rem; font-size: 1rem;
color: white; color: white;
text-align: center; text-align: center;
span.number { span.number {
font-weight: 600; font-weight: 600;
} }
@include media($mobile) { }
padding-top: 2rem; }
} }
&#contributions-by-type {
.chart {
width: 50%;
margin-left: auto;
margin-right: auto;
@include media-max(small) {
width: 75%;
} }
} }
} }
@@ -71,36 +74,35 @@ section {
.actions { .actions {
padding-top: 3rem; padding-top: 3rem;
font-size: 1rem; font-size: 1rem;
color: $primaryColor; color: $primary-color;
text-align: center; text-align: center;
@include media($mobile) { @include media-max(small) {
padding-top: 2rem; padding-top: 2rem;
} }
a { a {
color: $primaryColor; color: $primary-color;
} }
} }
} }
} }
button, input[type=submit] { @media (min-width: 550px) {
display: inline-block; section {
border: 1px solid rgba(22, 21, 40, 1); h2 {
background-color: rgba(22, 21, 40, 0.6); font-size: 2rem;
color: $primaryColor; }
border-radius: 3px;
font-weight: 500;
text-transform: uppercase;
cursor: pointer;
letter-spacing: 0.1em;
&:hover {
background-color: rgba(22, 21, 40, 0.8);
} }
} }
@import "components/loading-spinner"; @import "buttons";
@import "forms";
@import "components/contribution-list";
@import "components/contributor-list"; @import "components/contributor-list";
@import "components/add-contributor"; @import "components/contributor-profile";
@import "components/external-account-link";
@import "components/loading-spinner";
@import "components/proposal-list"; @import "components/proposal-list";
@import "components/topbar";
@import "components/user-avatar";
-19
View File
@@ -1,19 +0,0 @@
@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
@@ -1,425 +0,0 @@
// 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
@@ -1,90 +0,0 @@
// 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
@@ -1,26 +0,0 @@
@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
@@ -1,48 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,64 +0,0 @@
@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
@@ -1,25 +0,0 @@
@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
@@ -1,30 +0,0 @@
@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
@@ -1,31 +0,0 @@
@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
@@ -1,27 +0,0 @@
/// 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
@@ -1,26 +0,0 @@
@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
@@ -1,26 +0,0 @@
@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
@@ -1,48 +0,0 @@
@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
@@ -1,66 +0,0 @@
@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
@@ -1,27 +0,0 @@
@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
@@ -1,51 +0,0 @@
@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
@@ -1,113 +0,0 @@
@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
@@ -1,34 +0,0 @@
@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
@@ -1,63 +0,0 @@
@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
@@ -1,29 +0,0 @@
@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
@@ -1,61 +0,0 @@
// 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
@@ -1,5 +0,0 @@
@mixin appearance($value) {
@include _bourbon-deprecate-for-prefixing("appearance");
@include prefixer(appearance, $value, webkit moz ms o spec);
}
-5
View File
@@ -1,5 +0,0 @@
@mixin backface-visibility($visibility) {
@include _bourbon-deprecate-for-prefixing("backface-visibility");
@include prefixer(backface-visibility, $visibility, webkit spec);
}
-44
View File
@@ -1,44 +0,0 @@
//************************************************************************//
// 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
@@ -1,57 +0,0 @@
//************************************************************************//
// 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
@@ -1,61 +0,0 @@
@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
@@ -1,6 +0,0 @@
@mixin calc($property, $value) {
@include _bourbon-deprecate-for-prefixing("calc");
#{$property}: -webkit-calc(#{$value});
#{$property}: calc(#{$value});
}
-67
View File
@@ -1,67 +0,0 @@
@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
@@ -1,6 +0,0 @@
@mixin filter($function: none) {
@include _bourbon-deprecate-for-prefixing("filter");
// <filter-function> [<filter-function]* | none
@include prefixer(filter, $function, webkit spec);
}
-327
View File
@@ -1,327 +0,0 @@
// 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
@@ -1,24 +0,0 @@
@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
@@ -1,6 +0,0 @@
@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
@@ -1,12 +0,0 @@
// 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
@@ -1,6 +0,0 @@
@mixin hyphens($hyphenation: none) {
@include _bourbon-deprecate-for-prefixing("hyphens");
// none | manual | auto
@include prefixer(hyphens, $hyphenation, webkit moz ms spec);
}
-15
View File
@@ -1,15 +0,0 @@
@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
@@ -1,38 +0,0 @@
// 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
@@ -1,40 +0,0 @@
@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
@@ -1,12 +0,0 @@
@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);
}

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