material_raingular-d3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +62 -0
  7. data/Rakefile +2 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/lib/assets/javascripts/material_raingular/d3/.keep +0 -0
  11. data/lib/assets/javascripts/material_raingular/d3/bar/directive.coffee +8 -0
  12. data/lib/assets/javascripts/material_raingular/d3/bar/linkmodel.coffee +15 -0
  13. data/lib/assets/javascripts/material_raingular/d3/bar_chart/controller.coffee +34 -0
  14. data/lib/assets/javascripts/material_raingular/d3/bar_chart/directive.coffee +21 -0
  15. data/lib/assets/javascripts/material_raingular/d3/extensions.coffee +8 -0
  16. data/lib/assets/javascripts/material_raingular/d3/horizontal_bar_chart/controller.coffee +33 -0
  17. data/lib/assets/javascripts/material_raingular/d3/horizontal_bar_chart/directive.coffee +21 -0
  18. data/lib/assets/javascripts/material_raingular/d3/pie_chart/directive.coffee +7 -0
  19. data/lib/assets/javascripts/material_raingular/d3/pie_chart/linkmodel.coffee +39 -0
  20. data/lib/assets/javascripts/material_raingular/d3/ring_chart/directive.coffee +9 -0
  21. data/lib/assets/javascripts/material_raingular/d3/ring_chart/linkmodel.coffee +159 -0
  22. data/lib/assets/javascripts/material_raingular/d3/ring_chart/widget_controller.coffee +14 -0
  23. data/lib/assets/javascripts/material_raingular/d3/ring_chart/widget_directive.coffee +15 -0
  24. data/lib/assets/javascripts/material_raingular/d3/stacked_bar/controller.coffee +54 -0
  25. data/lib/assets/javascripts/material_raingular/d3/stacked_bar/directive.coffee +9 -0
  26. data/lib/assets/javascripts/material_raingular/d3/x_axis/directive.coffee +7 -0
  27. data/lib/assets/javascripts/material_raingular/d3/x_axis/linkmodel.coffee +25 -0
  28. data/lib/assets/javascripts/material_raingular/d3/y_axis/directive.coffee +7 -0
  29. data/lib/assets/javascripts/material_raingular/d3/y_axis/linkmodel.coffee +26 -0
  30. data/lib/assets/javascripts/material_raingular-d3.coffee +6 -0
  31. data/lib/assets/stylesheets/material_raingular/d3/barChart.sass +4 -0
  32. data/lib/assets/stylesheets/material_raingular/d3/ringWidget.css +51 -0
  33. data/lib/assets/stylesheets/material_raingular-d3.sass +2 -0
  34. data/lib/assets/templates/material_raingular/d3/ring_chart/ring.html +7 -0
  35. data/lib/assets/templates/material_raingular/d3/ring_chart/ringWidget.html +1 -0
  36. data/lib/material_raingular/d3/version.rb +5 -0
  37. data/lib/material_raingular/d3.rb +10 -0
  38. data/material_raingular-d3.gemspec +26 -0
  39. data/vendor/assets/javascripts/d3.min.js +8 -0
  40. metadata +124 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0075ac4a54d06c4290a26475164d8a97c976db50
4
+ data.tar.gz: 20bf12b579c0ab844fdaadd5459f0fafc5f4b1e5
5
+ SHA512:
6
+ metadata.gz: ed78e9a1dedae5d716490c6ef9d92efaf905aabf5859cc26986f4ddbd29820c7369eb276878bfb9a5b8da0fa77a1aaa8253395fea97af5b0234fa3d58bcb8607
7
+ data.tar.gz: 568a7533f494b9d79a8cf4b5f52448c6fccf05aebceb05d6d3464f441f49d5a007c1a3a8fe2a17fb254b28533525a07e8aa0ddfc8c0e63021441cc96f2873b8c
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ .DS_Store
15
+ mkmf.log
16
+ # Ignore Sublime Text's profile files
17
+ *.sublime*
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at cmoody@transcon.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in material_raingular-d3.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Chris Moody
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # MaterialRaingular::D3
2
+
3
+ MaterialRaingular::D3 adds d3 v4.4 directives to your MaterialRaingular application
4
+ ## Installation
5
+
6
+ Add this line to your application's Gemfile:
7
+
8
+ ```ruby
9
+ gem 'material_raingular-d3'
10
+ ```
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install material_raingular-d3
19
+
20
+ ## Usage
21
+
22
+ ```slim
23
+ mr-d3-ring-widget
24
+ h3
25
+ | Bio Billability
26
+ mr-d3-ring ring-data="[50,10]"
27
+ mr-d3-pie-chart d3-data="{bio: 10, planning: 30, cr: 12}"
28
+ mr-d3-horizontal-bar-chart.test ng-init="dataSet = [{l: 'a',v: 23},{l: 'b',v: 13},{l: 'c',v: 21},{l: 'd',v: 14},{l: 'e',v: 37},{l: 'f',v: 15},{l: 'g',v: 18},{l: 'h',v: 34},{l: 'i',v: 30}]"
29
+ mr-d3-bar.thingy ng-repeat="data in dataSet" mr-d3-label="data.l" mr-d3-value="data.v"
30
+ mr-d3-bar mr-d3-label="'this'" mr-d3-value="'200'"
31
+ mr-d3-x-axis ng-init="domain = [0,37]" mr-d3-label="'Value'" mr-d3-domain='domain'
32
+
33
+ mr-d3-stacked-bar mr-d3-label="'CA'"
34
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
35
+ mr-d3-stacked-bar mr-d3-label="'AZ'"
36
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
37
+ mr-d3-y-axis mr-d3-label="'Arbitrary'"
38
+
39
+ mr-d3-bar-chart.test ng-init="dataSet = [{l: 'a',v: 23},{l: 'b',v: 13},{l: 'c',v: 21},{l: 'd',v: 14},{l: 'e',v: 37},{l: 'f',v: 15},{l: 'g',v: 18},{l: 'h',v: 34},{l: 'i',v: 30}]"
40
+ mr-d3-bar.thingy ng-repeat="data in dataSet" mr-d3-label="data.l" mr-d3-value="data.v"
41
+ mr-d3-bar mr-d3-label="'this'" mr-d3-value="'200'"
42
+ mr-d3-y-axis ng-init="domain = [37,0]" mr-d3-label="'Value'" mr-d3-domain='domain'
43
+
44
+ mr-d3-stacked-bar mr-d3-label="'CA'"
45
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
46
+ mr-d3-stacked-bar mr-d3-label="'AZ'"
47
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
48
+ mr-d3-x-axis mr-d3-label="'Arbitrary'"
49
+ ```
50
+
51
+ ## Development
52
+
53
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
54
+
55
+ ## Contributing
56
+
57
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/material_raingular-d3. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
58
+
59
+
60
+ ## License
61
+
62
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "material_raingular/d3"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,8 @@
1
+ class MaterialRaingular.d3.Directives.MrD3Bar extends AngularDirective
2
+ @register(MaterialRaingular.d3.app)
3
+ restrict: "E"
4
+ require: ['?^^mrD3StackedBar','?^^mrD3HorizontalBarChart','?^^mrD3BarChart']
5
+ transclude: true
6
+ replace: true
7
+ template: '<rect></rect>'
8
+
@@ -0,0 +1,15 @@
1
+ class BarModel extends AngularLinkModel
2
+ @inject('$parse')
3
+ initialize: ->
4
+ @parent = @$controller.compact()[0]
5
+ @bar = d3.select(@$element[0])
6
+ @label = @$parse @$attrs.mrD3Label
7
+ @size = @$parse @$attrs.mrD3Value
8
+ @$scope.$watch @size.bind(@), @changeBar.bind(@)
9
+ @$scope.$watch @label.bind(@), @changeBar.bind(@)
10
+ @changeBar()
11
+ changeBar: ->
12
+ @bar.attr('raw-size',@size(@$scope))
13
+ @bar.attr('label', @label(@$scope))
14
+ @parent.adjustBars()
15
+ @register(MaterialRaingular.d3.Directives.MrD3Bar)
@@ -0,0 +1,34 @@
1
+ class MaterialRaingular.d3.Directives.MrD3BarChartModel extends AngularDirectiveModel
2
+ @inject('$timeout','$element','$attrs')
3
+ initialize: ->
4
+ @options = @$scope.$eval(@$attrs.d3Options || '{}')
5
+ @options.margins = Object.merge({top: 20,right: 20, left: 70, bottom: 50},@options.margins || {})
6
+ @svg = d3.select(@$element[0])
7
+ @holder = @svg.select('g')
8
+ @holder.attr('transform',"translate(#{@options.margins.left},#{@options.margins.right})")
9
+ @xAxis = d3.scaleBand().rangeRound([0,@width()]).padding(0.1)
10
+ @yAxis = d3.scaleLinear().range([0,@height()])
11
+ indexOf: (bar) ->
12
+ @bars().nodes().indexOf(bar)
13
+ bars: ->
14
+ nodes = @holder.selectAll('svg > g > rect').nodes()
15
+ d3.selectAll(nodes.concat(@holder.selectAll('g.stacked').nodes()))
16
+ adjustBars: ->
17
+ @xDomain = []
18
+ for bar in @bars().nodes()
19
+ rect = d3.select(bar)
20
+ @xDomain[@indexOf(bar)] = rect.attr('label')
21
+ height = (rect.attr('raw-size') / @maxValue()) * @width()
22
+ rect.attr('y',@height() - height)
23
+ rect.attr('height',height)
24
+ @xAxis.domain(@xDomain)
25
+ for rect in @bars().nodes()
26
+ d3.select(rect).attr('width',@xAxis.bandwidth())
27
+ d3.select(rect).attr('x',@xAxis(@xDomain[@indexOf(rect)]))
28
+ @_xAxis?.call(d3.axisBottom(@xAxis))
29
+ height: -> @$element[0].clientHeight - @options.margins.top - @options.margins.bottom
30
+ width: -> @$element[0].clientWidth - @options.margins.left - @options.margins.right
31
+ maxValue: ->
32
+ val = @yAxis?.domain().max()
33
+ val ||= (@bars().nodes().map (rect) -> d3.select(rect).attr('raw-size')).max()
34
+ val
@@ -0,0 +1,21 @@
1
+ # //= require material_raingular/d3/horizontal_bar_chart/controller
2
+ class MrD3BarChart extends AngularDirective
3
+ @register(MaterialRaingular.d3.app)
4
+ restrict: "E"
5
+ controller: MaterialRaingular.d3.Directives.MrD3BarChartModel
6
+ transclude: true
7
+ replace: true
8
+ template: "<svg height='100%' width: 100%><g ng-transclude></g></svg>"
9
+ ###
10
+ Usage Slim syntax
11
+ mr-d3-bar-chart.test ng-init="dataSet = [{l: 'a',v: 23},{l: 'b',v: 13},{l: 'c',v: 21},{l: 'd',v: 14},{l: 'e',v: 37},{l: 'f',v: 15},{l: 'g',v: 18},{l: 'h',v: 34},{l: 'i',v: 30}]"
12
+ mr-d3-bar.thingy ng-repeat="data in dataSet" mr-d3-label="data.l" mr-d3-value="data.v"
13
+ mr-d3-bar mr-d3-label="'this'" mr-d3-value="'200'"
14
+ mr-d3-y-axis ng-init="domain = [37,0]" mr-d3-label="'Value'" mr-d3-domain='domain'
15
+
16
+ mr-d3-stacked-bar mr-d3-label="'CA'"
17
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
18
+ mr-d3-stacked-bar mr-d3-label="'AZ'"
19
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
20
+ mr-d3-x-axis mr-d3-label="'Arbitrary'"
21
+ ###
@@ -0,0 +1,8 @@
1
+ Object.map = (obj) ->
2
+ (func) ->
3
+ func(key,val) for key,val of obj
4
+ Object.merge = (a,b,force) ->
5
+ c = {}
6
+ c[key] = val for key,val of a
7
+ c[key] = val for key,val of b
8
+ c
@@ -0,0 +1,33 @@
1
+ class MaterialRaingular.d3.Directives.MrD3HorizontalBarChartModel extends AngularDirectiveModel
2
+ @inject('$timeout','$element','$attrs')
3
+ initialize: ->
4
+ @options = @$scope.$eval(@$attrs.d3Options || '{}')
5
+ @options.margins = Object.merge({top: 20,right: 20, left: 70, bottom: 50},@options.margins || {})
6
+ @svg = d3.select(@$element[0])
7
+ @holder = @svg.select('g')
8
+ @holder.attr('transform',"translate(#{@options.margins.left},#{@options.margins.right})")
9
+ @yAxis = d3.scaleBand().rangeRound([0,@height()]).padding(0.1)
10
+ @xAxis = d3.scaleLinear().range([0,@width()])
11
+ indexOf: (bar) ->
12
+ @bars().nodes().indexOf(bar)
13
+ bars: ->
14
+ nodes = @holder.selectAll('svg > g > rect').nodes()
15
+ d3.selectAll(nodes.concat(@holder.selectAll('g.stacked').nodes()))
16
+ adjustBars: ->
17
+ @yDomain = []
18
+ for bar in @bars().nodes()
19
+ rect = d3.select(bar)
20
+ @yDomain[@indexOf(bar)] = rect.attr('label')
21
+ width = (rect.attr('raw-size') / @maxValue()) * @width()
22
+ rect.attr('width',width)
23
+ @yAxis.domain(angular.copy(@yDomain).reverse())
24
+ for rect in @bars().nodes()
25
+ d3.select(rect).attr('height',@yAxis.bandwidth())
26
+ d3.select(rect).attr('y',@yAxis(@yDomain[@indexOf(rect)]))
27
+ @_yAxis?.call(d3.axisLeft(@yAxis))
28
+ height: -> @$element[0].clientHeight - @options.margins.top - @options.margins.bottom
29
+ width: -> @$element[0].clientWidth - @options.margins.left - @options.margins.right
30
+ maxValue: ->
31
+ val = @xAxis?.domain().max()
32
+ val ||= (@bars().nodes().map (rect) -> d3.select(rect).attr('raw-size')).max()
33
+ val
@@ -0,0 +1,21 @@
1
+ # //= require material_raingular/d3/horizontal_bar_chart/controller
2
+ class MrD3HorizontalBarChart extends AngularDirective
3
+ @register(MaterialRaingular.d3.app)
4
+ restrict: "E"
5
+ controller: MaterialRaingular.d3.Directives.MrD3HorizontalBarChartModel
6
+ transclude: true
7
+ replace: true
8
+ template: "<svg height='100%' width: 100%><g ng-transclude></g></svg>"
9
+ ###
10
+ Usage Slim Syntax
11
+ mr-d3-horizontal-bar-chart.test ng-init="dataSet = [{l: 'a',v: 23},{l: 'b',v: 13},{l: 'c',v: 21},{l: 'd',v: 14},{l: 'e',v: 37},{l: 'f',v: 15},{l: 'g',v: 18},{l: 'h',v: 34},{l: 'i',v: 30}]"
12
+ mr-d3-bar.thingy ng-repeat="data in dataSet" mr-d3-label="data.l" mr-d3-value="data.v"
13
+ mr-d3-bar mr-d3-label="'this'" mr-d3-value="'200'"
14
+ mr-d3-x-axis ng-init="domain = [0,37]" mr-d3-label="'Value'" mr-d3-domain='domain'
15
+
16
+ mr-d3-stacked-bar mr-d3-label="'CA'"
17
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
18
+ mr-d3-stacked-bar mr-d3-label="'AZ'"
19
+ mr-d3-bar ng-repeat="data in [10,4,7]" mr-d3-label="data" mr-d3-value="data"
20
+ mr-d3-y-axis mr-d3-label="'Arbitrary'"
21
+ ###
@@ -0,0 +1,7 @@
1
+ class MaterialRaingular.d3.Directives.MrD3PieChart extends AngularDirective
2
+ restrict: "E"
3
+ @register(MaterialRaingular.d3.app)
4
+ ###
5
+ Usage Slim Syntax
6
+ mr-d3-pie-chart d3-data="{bio: 10, planning: 30, cr: 12}"
7
+ ###
@@ -0,0 +1,39 @@
1
+ class PieChartModel extends AngularLinkModel
2
+ @inject('$timeout')
3
+ initialize: ->
4
+ @svg = d3.select(@$element[0]).append('svg')
5
+ @chartLayer = @svg.append('g').classed('chartLayer', true)
6
+ data = Object.map(@$scope.$eval(@$attrs.d3Data))(@cast.bind(@))
7
+ @setSize(data)
8
+ @drawChart(data)
9
+ @$scope.$watchCollection @$attrs.d3Data, @redrawChart.bind(@)
10
+ cast: (key,val) -> {name: key.titleize(), value: val}
11
+ redrawChart: (obj) ->
12
+ data = Object.map(@$scope.$eval(@$attrs.d3Data))(@cast.bind(@))
13
+ @drawChart(data)
14
+
15
+ setSize: (data) ->
16
+ @options = @$scope.$eval(@$attrs.d3Options) || {}
17
+ @width = @options.width || 960
18
+ @height = @options.height || 500
19
+ @color = @options.color || '6b486b'
20
+ @margin = @options.margin || {top: 40,left: 0,bottom: 40,right: 0}
21
+ @chartWidth = @width - (@margin.left + @margin.right)
22
+ @chartHeight = @height - (@margin.top + @margin.bottom)
23
+ @svg.attr('width', @width).attr 'height', @height
24
+ @chartLayer.attr('width', @chartWidth).attr('height', @chartHeight).attr 'transform', "translate(#{[@margin.left,@margin.top]})"
25
+
26
+ drawChart: (data) ->
27
+ @chartLayer.selectAll('g').remove()
28
+ arcs = d3.pie().sort(null).value((d) -> d.value)(data)
29
+ arc = d3.arc().outerRadius(@chartHeight / 2).innerRadius(@chartHeight / 4).padAngle(0.03).cornerRadius(8) #/ <- I hate atom right now
30
+ pieG = @chartLayer.selectAll('g').data([ data ]).enter().append('g').attr('transform', "translate(#{[@chartWidth / 2,@chartHeight / 2]})")
31
+ block = pieG.selectAll('.arc').data(arcs)
32
+ newBlock = block.enter().append('g').classed('arc', true)
33
+ newBlock.append('path').attr('d', arc).attr('id', (d, i) -> 'arc-' + i)
34
+ .attr('stroke', 'gray').attr 'fill', (d, i) -> d3.interpolateCool Math.random()
35
+ newBlock.append('text').attr('dx', 55).attr('dy', -5).append('textPath').attr('xlink:href', (d, i) ->'#arc-' + i)
36
+ .text (d) -> d.data.name
37
+ return
38
+
39
+ @register(MaterialRaingular.d3.Directives.MrD3PieChart)
@@ -0,0 +1,9 @@
1
+ class MaterialRaingular.d3.Directives.MrD3Ring extends AngularDirective
2
+ restrict: "E"
3
+ require: "^^mrD3RingWidget"
4
+ templateUrl: "assets/material_raingular/d3/ring_chart/ring.html"
5
+ scope: {
6
+ ringData: "@"
7
+ title: "@"
8
+ }
9
+ @register(MaterialRaingular.d3.app)
@@ -0,0 +1,159 @@
1
+ # //= require material_raingular/d3/ring_chart/directive
2
+
3
+ ### TODO: Massive refactor required after change to coffeescropt.
4
+ The watch function is really more of a hack than an actual
5
+ fix to the fact that we aren't responsive to data changes
6
+ ###
7
+ ###
8
+ ************************************************************************
9
+ Custom D3 ring chart
10
+
11
+ Most everything here is going to be happening outside the AngularJS
12
+ context so scope.$apply() will be necessary for forcing angular to go
13
+ into it's digest loop when changes are made to the scope.
14
+
15
+ dataset: Constructed from a data service passed-in by directive caller.
16
+ Result is
17
+ ************************************************************************
18
+ ###
19
+
20
+
21
+
22
+ class RingChartModel extends AngularLinkModel
23
+ # Default attributes to use in template if attribute not set.
24
+ # Currently this only affects the internal relative positioning.
25
+ defaults:
26
+ width: 200
27
+ height: 200
28
+
29
+ initialize: ->
30
+ @d3 = window.d3
31
+ @$mrD3RingWidgetController = @$controller
32
+ @$scope.title ||= "Some title from data-set."
33
+ @_addRings()
34
+ @$scope.$watchCollection @_dataset, (newVal,oldVal) =>
35
+ @_addRings() if newVal != oldVal
36
+ #TODO: Replace with data factory that generates JSON objects.
37
+ _dataset: => @$scope.$eval(@$scope.ringData)
38
+ _addRings: ->
39
+ # Add ring to ring list on RingWidgetController
40
+ @$mrD3RingWidgetController.refresh()
41
+ @$mrD3RingWidgetController.addRing(@$scope)
42
+
43
+ @w = @defaults.width
44
+ @h = @defaults.height
45
+
46
+ # Get sum and calc percent.
47
+ @toPercent = 1/@_dataset().reduce(@_getSum)*100
48
+ @outerRadius = Math.min(@w,@h)/2
49
+ @innerRadius = Math.min(@w,@h)/3
50
+
51
+ @cornerRadius = @outerRadius*0.05
52
+ arc = @d3.arc()
53
+ .innerRadius(@innerRadius)
54
+ .outerRadius(@outerRadius)
55
+ .cornerRadius(@cornerRadius)
56
+ .padAngle(0.03)
57
+
58
+ @arcDataset = @d3.pie()(@_dataset()) # Convert array of number into arc object data
59
+
60
+ # Normalize arc dataset
61
+ @_rotateArcs(@arcDataset, 0)
62
+ # Make first data element centered on rhs of ring graph
63
+ @rotateAngle = @_calcCenterRotation(@arcDataset[0])
64
+ # Go through and rotate all arcs.
65
+ @_rotateArcs(@arcDataset, @rotateAngle)
66
+ # Easy colors accessible via a 10-step ordinal scale
67
+ @color = @d3.scaleOrdinal(@d3.schemeCategory10)
68
+
69
+ # Select SVG element from template
70
+ @svg = @d3.select(@$element[0])
71
+ .select("div")
72
+ .select("svg")
73
+
74
+ #Set up groups
75
+ @svg.selectAll("g").remove()
76
+ @arcs = @svg.append("g")
77
+ .attr("class", "rings")
78
+ .selectAll("g.arc")
79
+ .data(@arcDataset)
80
+ .enter()
81
+ .append("g")
82
+ .attr("class", "arc")
83
+ .classed("selected", (d, i) -> i==0)
84
+ .attr("transform", "translate(" + @outerRadius + "," + @outerRadius + ")")
85
+ .on("dblclick", @_doubleClickArc)
86
+ .on("mouseover", @_percentOnHover)
87
+ .on("mouseout", @_setInitialCenterText)
88
+
89
+ @arcs.append("title")
90
+ .text (d,i) ->
91
+ d.value + " Hours " + (if i == 0 then "Billable" else "Non-billable")
92
+
93
+ @_setInitialCenterText()
94
+
95
+ #Draw arc paths
96
+ @arcs.append("path")
97
+ .attr "fill", (d, i) => @color(i)
98
+ .attr("d", arc)
99
+
100
+
101
+
102
+ _calcCenterRotation: (datum) ->
103
+ # Make selected data element centered on rhs of ring graph
104
+ elementAngle = datum["endAngle"] - datum["startAngle"]
105
+ elementCenterAngle = datum["startAngle"] + elementAngle/2
106
+ rotateAngle = 2*Math.PI - elementCenterAngle + Math.PI/2
107
+ return rotateAngle
108
+ ### rotateArcs: Takes a positive angle in radians and and d3ArcDataset and
109
+ mutates the data. ###
110
+ _rotateArcs: (d3ArcData, radRotation) ->
111
+ radRotation = Math.abs(radRotation)
112
+ for data in d3ArcData
113
+ dTheta = data["endAngle"] - data["startAngle"]
114
+ data["startAngle"] = (data["startAngle"] + radRotation)%(Math.PI*2)
115
+ data["endAngle"] = data["startAngle"] + dTheta
116
+
117
+ _doubleClickArc: (datum) =>
118
+ rings = @d3.select(@$element[0])
119
+ .select("g.rings")
120
+ @svg.selectAll("g.arc")
121
+ .classed("selected", false)
122
+ @d3.select(this)
123
+ .classed("selected", true)
124
+ rings.transition().duration(1000)
125
+ .attrTween("transform", tween)
126
+
127
+ tween = =>
128
+ rotateAngle = @_calcCenterRotation(datum)*180/Math.PI
129
+ interpolateFrom = this.getAttribute("transform") || "rotate(0, 100, 100)";
130
+ return @d3.interpolateString(interpolateFrom, "rotate(" + rotateAngle + ", 100, 100)")
131
+
132
+ # Add initial center percent text
133
+ _setInitialCenterText: =>
134
+ @svg.select("text.center")
135
+ .remove()
136
+ data = @svg.select("g.arc.selected").datum();
137
+ @svg.append("text")
138
+ .attr("class", "center")
139
+ .attr("x", @outerRadius)
140
+ .attr("y", @outerRadius)
141
+ .attr("dy", "0.3em")
142
+ .text((Math.round(data.value*@toPercent) || 0) + "%")
143
+
144
+ # percent
145
+ _percentOnHover: (d) =>
146
+ @svg.select("text.center")
147
+ .remove()
148
+ @svg.append("text")
149
+ .attr("class", "center")
150
+ .attr("x", @outerRadius)
151
+ .attr("y", @outerRadius)
152
+ .attr("dy", "0.3em")
153
+ .text(Math.round(d.value * @toPercent) + "%")
154
+
155
+
156
+ _getSum: (total, num) ->
157
+ return total + num
158
+
159
+ @register(MaterialRaingular.d3.Directives.MrD3Ring)
@@ -0,0 +1,14 @@
1
+ class MaterialRaingular.d3.Directives.MrD3RingWidget extends AngularDirectiveModel
2
+ @inject(
3
+ '$scope'
4
+ )
5
+ # Use "this" for the controller object (sharred between directives)
6
+ # and use $scope for the view of RingWidget
7
+
8
+ # All the ring-charts contained by widget
9
+ initialize: ->
10
+ @$scope.rings = []
11
+
12
+ # Used by RingDirective linker to add rings to widget controller
13
+ addRing: (ring) -> @$scope.rings.push ring
14
+ refresh: -> @$scope.rings = []
@@ -0,0 +1,15 @@
1
+ # //= require material_raingular/d3/ring_chart/widget_controller
2
+ class MrD3RingWidget extends AngularDirective
3
+ restrict: 'E'
4
+ transclude: true
5
+ templateUrl: 'assets/material_raingular/d3/ring_chart/ringWidget.html'
6
+ scope: {}
7
+ controller: MaterialRaingular.d3.Directives.MrD3RingWidget
8
+ @register(MaterialRaingular.d3.app)
9
+ ###
10
+ Usage Slim Syntax
11
+ mr-d3-ring-widget
12
+ h3
13
+ | Bio Billability
14
+ mr-d3-ring ring-data="[50,10]"
15
+ ###