promethee 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +164 -19
  3. data/Rakefile +33 -2
  4. data/app/assets/images/icon-promethee.png +0 -0
  5. data/app/assets/images/logo-promethee-horizontal.svg +14 -0
  6. data/app/assets/images/logo-promethee-vertical.svg +14 -0
  7. data/app/assets/javascripts/promethee.js +59 -0
  8. data/app/assets/javascripts/promethee/controller.js +22 -0
  9. data/app/assets/stylesheets/promethee-editor.sass +105 -0
  10. data/app/assets/stylesheets/promethee-editor/_mixins.sass +19 -0
  11. data/app/assets/stylesheets/promethee-editor/_variables.sass +4 -0
  12. data/app/assets/stylesheets/promethee.sass +4 -0
  13. data/app/views/promethee/_edit.html.erb +172 -0
  14. data/app/views/promethee/_show.html.erb +6 -0
  15. data/app/views/promethee/components/_column.html.erb +8 -0
  16. data/app/views/promethee/components/_column_edit.html.erb +40 -0
  17. data/app/views/promethee/components/_image.html.erb +7 -0
  18. data/app/views/promethee/components/_image_edit.html.erb +60 -0
  19. data/app/views/promethee/components/_index.html.erb +3 -0
  20. data/app/views/promethee/components/_index_edit.html.erb +7 -0
  21. data/app/views/promethee/components/_row.html.erb +6 -0
  22. data/app/views/promethee/components/_row_edit.html.erb +53 -0
  23. data/app/views/promethee/components/_show.html.erb +4 -0
  24. data/app/views/promethee/components/_show_edit.html.erb +3 -0
  25. data/app/views/promethee/components/_text.html.erb +6 -0
  26. data/app/views/promethee/components/_text_edit.html.erb +59 -0
  27. data/app/views/promethee/components/_video.html.erb +16 -0
  28. data/app/views/promethee/components/_video_edit.html.erb +63 -0
  29. data/app/views/promethee/partials/_toolbar_buttons.html.erb +4 -0
  30. data/lib/promethee.rb +37 -2
  31. data/lib/promethee/component.rb +28 -0
  32. data/lib/promethee/component/attribute.rb +42 -0
  33. data/lib/promethee/component/attribute/boolean.rb +4 -0
  34. data/lib/promethee/component/attribute/float.rb +3 -0
  35. data/lib/promethee/component/attribute/integer.rb +3 -0
  36. data/lib/promethee/component/attribute/string.rb +3 -0
  37. data/lib/promethee/component/attributes.rb +50 -0
  38. data/lib/promethee/component/attributes/definer.rb +13 -0
  39. data/lib/promethee/component/base.rb +68 -0
  40. data/lib/promethee/component/collection.rb +17 -0
  41. data/lib/promethee/component/column.rb +8 -0
  42. data/lib/promethee/component/image.rb +6 -0
  43. data/lib/promethee/component/row.rb +3 -0
  44. data/lib/promethee/component/text.rb +5 -0
  45. data/lib/promethee/component/video.rb +5 -0
  46. data/lib/promethee/core_ext/form_builder.rb +6 -0
  47. data/lib/promethee/core_ext/form_helper.rb +6 -0
  48. data/lib/promethee/core_ext/tags.rb +18 -0
  49. data/lib/promethee/grid.rb +52 -0
  50. data/lib/promethee/rails/engine.rb +7 -0
  51. data/lib/promethee/rails/helper.rb +26 -0
  52. data/lib/promethee/rails/version.rb +5 -0
  53. data/lib/tasks/promethee/promethee.rake +4 -0
  54. metadata +202 -20
  55. data/.gitignore +0 -9
  56. data/CODE_OF_CONDUCT.md +0 -74
  57. data/Gemfile +0 -6
  58. data/LICENSE +0 -21
  59. data/bin/console +0 -14
  60. data/bin/setup +0 -8
  61. data/lib/promethee/version.rb +0 -3
  62. data/promethee.gemspec +0 -24
@@ -0,0 +1,16 @@
1
+ <%
2
+ url = component[:attributes][:url].to_s
3
+
4
+ if 'vimeo'.in? url
5
+ video_id = url.to_s.gsub(/\A(?:https?:)?\/\/(?:(?:www|player)\.)?vimeo\.com\/(?:video\/)?(\d+).*?\z/, '\1')
6
+ iframe_src = "https://player.vimeo.com/video/#{video_id}?color=ffffff&title=0&byline=0&portrait=0"
7
+ elsif 'youtube'.in? url
8
+ video_id = url.to_s.split('watch?v=').last
9
+ iframe_src = "http://www.youtube.com/embed/#{video_id}"
10
+ end
11
+ %>
12
+ <div class="embed-responsive embed-responsive-16by9 <%= promethee_class_for component %>">
13
+ <% if iframe_src.present? %>
14
+ <iframe frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen="allowfullscreen" src="<%= iframe_src %>"></iframe>
15
+ <% end %>
16
+ </div>
@@ -0,0 +1,63 @@
1
+ <script type="text/ng-template" id="promethee/components/video">
2
+ <div ng-controller="VideoController">
3
+ <div dnd-draggable="component"
4
+ dnd-moved="remove()"
5
+ dnd-type="component.type"
6
+ class="promethee-editor__component promethee-editor__component--video promethee-editor__component--final">
7
+ <div class="promethee-editor__toolbar">
8
+ Video
9
+ <%= render 'promethee/partials/toolbar_buttons' %>
10
+ </div>
11
+ <div ng-show="editing" class="promethee-editor__wrapper">
12
+ <div class="form-group">
13
+ <label class="label-control">Url</label>
14
+ <input ng-model="component.attributes.url" class="form-control" type="text"/>
15
+ </div>
16
+ </div>
17
+ <div class="embed-responsive embed-responsive-16by9">
18
+ <iframe ng-if="embed" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen="allowfullscreen" ng-src="{{embed | urlSafe}}"></iframe>
19
+ </div>
20
+ </div>
21
+ </div>
22
+ </script>
23
+
24
+ <script>
25
+ angular.injector(['ng', '<%= promethee_id %>']).get('definitions').push({
26
+ name: 'Video',
27
+ thumb: 'http://via.placeholder.com/300x200',
28
+ data: {
29
+ type: 'video',
30
+ attributes: {
31
+ url: 'https://vimeo.com/115082758'
32
+ }
33
+ }
34
+ });
35
+
36
+ promethee.controller('VideoController', ['$scope', function($scope) {
37
+
38
+ $scope.editing = false;
39
+
40
+ $scope.remove = function() {
41
+ this.components.splice(this.components.indexOf(this.component), 1);
42
+ };
43
+
44
+ Object.defineProperty($scope, 'embed', {
45
+ get: function() {
46
+ var embed = null;
47
+ var url = this.component.attributes.url + '';
48
+
49
+ if(url.includes('vimeo')) {
50
+ var id = url.replace(/^(?:https?:)?\/\/(?:(?:www|player)\.)?vimeo\.com\/(?:video\/)?(\d+).*?$/, '$1');
51
+ embed = 'https://player.vimeo.com/video/' + id + '?color=ffffff&title=0&byline=0&portrait=0';
52
+ }
53
+ else if(url.includes('youtube')) {
54
+ var parts = url.split('watch?v=');
55
+ var id = parts[parts.length - 1];
56
+ embed = 'http://www.youtube.com/embed/' + id;
57
+ }
58
+
59
+ return embed;
60
+ }
61
+ })
62
+ }]);
63
+ </script>
@@ -0,0 +1,4 @@
1
+ <div class="pull-right">
2
+ <span ng-click="editing = !editing" class="btn btn-default btn-xs"><%= fa_icon :edit %></span>
3
+ <span ng-click="remove()" class="btn btn-default btn-xs"><%= fa_icon :close %></span>
4
+ </div>
data/lib/promethee.rb CHANGED
@@ -1,5 +1,40 @@
1
- require "promethee/version"
1
+ require 'bootstrap-sass'
2
+ require 'font-awesome-rails'
3
+ require 'jquery-rails'
4
+ require 'jquery-ui-rails'
5
+ require 'tinymce-rails'
6
+ require 'angularjs-rails'
7
+ require 'angular-ui-tinymce/rails'
8
+ require 'material_icons'
2
9
 
3
10
  module Promethee
4
- # Your code goes here...
11
+ module Rails
12
+ require 'promethee/rails/helper'
13
+ require 'promethee/rails/engine'
14
+ require 'promethee/rails/version'
15
+ end
16
+
17
+ require 'promethee/component'
18
+ require 'promethee/component/attribute'
19
+ require 'promethee/component/attribute/boolean'
20
+ require 'promethee/component/attribute/integer'
21
+ require 'promethee/component/attribute/float'
22
+ require 'promethee/component/attribute/string'
23
+
24
+ require 'promethee/component/attributes'
25
+ require 'promethee/component/attributes/definer'
26
+
27
+ require 'promethee/component/base'
28
+ require 'promethee/component/collection'
29
+ require 'promethee/component/row'
30
+ require 'promethee/component/column'
31
+ require 'promethee/component/text'
32
+ require 'promethee/component/image'
33
+ require 'promethee/component/video'
34
+
35
+ require 'promethee/grid'
36
+
37
+ require 'promethee/core_ext/tags'
38
+ require 'promethee/core_ext/form_helper'
39
+ require 'promethee/core_ext/form_builder'
5
40
  end
@@ -0,0 +1,28 @@
1
+ module Promethee::Component
2
+ def self.types
3
+ Base.descendants.map &:type
4
+ end
5
+
6
+ def self.as type
7
+ begin
8
+ class_name = "::Promethee::Component::#{type.to_s.classify}"
9
+ require "promethee/component/#{type}" unless Object.const_defined? class_name
10
+ class_name.constantize
11
+ rescue LoadError
12
+ raise "Unknown Prométhée component type \"#{type}\". Available types: \"#{types.join '", "'}\"."
13
+ end
14
+ end
15
+
16
+ def self.from data
17
+ raise "Invalid data provided, expected a Hash got a #{data.class}." unless data.is_a? Hash
18
+
19
+ data = data.deep_dup
20
+
21
+ data[:attributes] = {} unless data[:attributes].is_a? Hash
22
+ data[:children] = [] unless data[:children].is_a? Array
23
+
24
+ data[:children].map! { |data| from data }
25
+
26
+ as(data[:type]).new attributes: data[:attributes], children: data[:children]
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ module Promethee::Component::Attribute
2
+ class Base
3
+ attr_reader :name, :value
4
+
5
+ def initialize name, value = default
6
+ @name = name.to_s.to_sym
7
+ self.value = value
8
+ end
9
+
10
+ def value= value
11
+ @value = value == default ? default : cast(value)
12
+ end
13
+
14
+ def default
15
+ self.class.default
16
+ end
17
+
18
+ def cast value
19
+ self.class.cast.call value
20
+ end
21
+
22
+ def dup
23
+ self.class.new name, (value.dup rescue value)
24
+ end
25
+
26
+ private
27
+
28
+ def self.type
29
+ to_s.split('::').last.underscore
30
+ end
31
+
32
+ def self.default value = nil
33
+ @value = value unless value.nil?
34
+ @value
35
+ end
36
+
37
+ def self.cast &block
38
+ @cast = block if block_given?
39
+ @cast ||= -> (value) { value }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,4 @@
1
+ class Promethee::Component::Attribute::Boolean < Promethee::Component::Attribute::Base
2
+ default false
3
+ cast { |value| !!value }
4
+ end
@@ -0,0 +1,3 @@
1
+ class Promethee::Component::Attribute::Float < Promethee::Component::Attribute::Base
2
+ cast { |value| value&.to_f or default }
3
+ end
@@ -0,0 +1,3 @@
1
+ class Promethee::Component::Attribute::Integer < Promethee::Component::Attribute::Base
2
+ cast { |value| value&.to_i or default }
3
+ end
@@ -0,0 +1,3 @@
1
+ class Promethee::Component::Attribute::String < Promethee::Component::Attribute::Base
2
+ cast { |value| value&.to_s or default }
3
+ end
@@ -0,0 +1,50 @@
1
+ class Promethee::Component::Attributes
2
+ def update hash
3
+ (names & hash.keys.map(&:to_sym)).each { |name| attributes[name].value = hash[name] }
4
+
5
+ self
6
+ end
7
+
8
+ def to_hash
9
+ attributes.map{ |name, attribute| [name, attribute.value] }.to_h
10
+ end
11
+ alias_method :to_h, :to_hash
12
+ alias_method :as_json, :to_hash
13
+
14
+ def [](method)
15
+ send method
16
+ end
17
+
18
+ def keys
19
+ attributes.keys
20
+ end
21
+ alias_method :names, :keys
22
+
23
+ def each &block
24
+ attributes.values.each &block
25
+ end
26
+
27
+ def << attribute
28
+ attributes[attribute.name] = attribute
29
+
30
+ define_singleton_method(attribute.name) { attribute.value }
31
+ define_singleton_method("#{attribute.name}=") { |value| attribute.value = value }
32
+ end
33
+
34
+ def dup
35
+ duplicate = self.class.new
36
+ each { |attribute| duplicate << attribute.dup }
37
+
38
+ duplicate
39
+ end
40
+
41
+ def copy hash
42
+ dup.update hash
43
+ end
44
+
45
+ private
46
+
47
+ def attributes
48
+ @attributes ||= {}
49
+ end
50
+ end
@@ -0,0 +1,13 @@
1
+ class Promethee::Component::Attributes::Definer
2
+ attr_reader :component
3
+
4
+ def initialize(component)
5
+ @component = component
6
+ end
7
+
8
+ ::Promethee::Component::Attribute::Base.descendants.each do |attribute|
9
+ define_method attribute.type do |name, default: nil|
10
+ component.attributes << attribute.new(name, default)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,68 @@
1
+ class Promethee::Component::Base
2
+ attr_reader :attributes, :children
3
+
4
+ def initialize(attributes: {}, children: [])
5
+ @attributes = self.class.attributes.copy attributes
6
+ @children = Promethee::Component::Collection.new children
7
+ end
8
+
9
+ def to_hash *options
10
+ {
11
+ type: type,
12
+ attributes: attributes.to_hash,
13
+ children: children.to_ary
14
+ }
15
+ end
16
+ alias_method :to_h, :to_hash
17
+ alias_method :as_json, :to_hash
18
+
19
+ def to_html
20
+ ApplicationController.renderer.render partial: "promethee/components/#{type}", locals: { component: self }
21
+ end
22
+
23
+ def [](method)
24
+ send method
25
+ end
26
+
27
+ def class_name
28
+ "promethee__component promethee__component--#{type}"
29
+ end
30
+
31
+ def type
32
+ self.class.type
33
+ end
34
+
35
+ def self.attributes
36
+ @attributes ||= Promethee::Component::Attributes.new
37
+ end
38
+
39
+ def self.children
40
+ @children ||= []
41
+ end
42
+
43
+ def self.type
44
+ to_s.split('::').last.underscore.to_sym
45
+ end
46
+
47
+ def self.final?
48
+ children.empty?
49
+ end
50
+
51
+ # def self.class_name
52
+ # "promethee-editor__component promethee__component--#{type}#{' promethee__component--final' if final?}"
53
+ # end
54
+
55
+ private
56
+
57
+ def self.has_attributes
58
+ yield Promethee::Component::Attributes::Definer.new(self) if block_given?
59
+ end
60
+
61
+ def self.has_children *components
62
+ children.concat(components.flatten.map{ |component| component.is_a?(Class) && component < self ? component : Promethee::Component.as(component) }).uniq!
63
+ end
64
+
65
+ class << self
66
+ alias_method :has_child, :has_children
67
+ end
68
+ end
@@ -0,0 +1,17 @@
1
+ class Promethee::Component::Collection < Array
2
+ def to_ary
3
+ map &:to_hash
4
+ end
5
+ alias_method :to_a, :to_ary
6
+ alias_method :as_json, :to_ary
7
+
8
+ def to_html
9
+ map(&:to_html).join.html_safe
10
+ end
11
+
12
+ def self.from data
13
+ raise "Invalid data provided, expected a Array got a #{data.class}." unless data.is_a? Array
14
+
15
+ new data.map{ |component| Promethee::Component.from component }
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ class Promethee::Component::Column < Promethee::Component::Base
2
+ has_attributes do |a|
3
+ a.integer :size, default: 4
4
+ a.integer :offset, default: 0
5
+ end
6
+
7
+ has_children :row, :text, :image, :video
8
+ end
@@ -0,0 +1,6 @@
1
+ class Promethee::Component::Image < Promethee::Component::Base
2
+ has_attributes do |a|
3
+ a.string :src
4
+ a.string :alt
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ class Promethee::Component::Row < Promethee::Component::Base
2
+ has_child :column
3
+ end
@@ -0,0 +1,5 @@
1
+ class Promethee::Component::Text < Promethee::Component::Base
2
+ has_attributes do |a|
3
+ a.string :body, default: ''
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Promethee::Component::Video < Promethee::Component::Base
2
+ has_attributes do |a|
3
+ a.string :url
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ ActionView::Helpers::FormBuilder.class_eval do
2
+ # https://github.com/rails/rails/blob/13c5aa818e9284fe30f83469b340e579195bda3f/actionview/lib/action_view/helpers/form_helper.rb#L2148
3
+ def promethee(method, options = {})
4
+ @template.promethee_editor(@object_name, method, objectify_options(options))
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ ActionView::Helpers::FormHelper.class_eval do
2
+ # https://github.com/rails/rails/blob/13c5aa818e9284fe30f83469b340e579195bda3f/actionview/lib/action_view/helpers/form_helper.rb#L1193
3
+ def promethee_editor(object_name, method, options = {})
4
+ ActionView::Helpers::Tags::PrometheeEditor.new(object_name, method, self, options).render
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ require 'promethee'
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module Tags
6
+ # https://github.com/rails/rails/blob/bdc581616b760d1e2be3795c6f0f3ab4b1e125a5/actionview/lib/action_view/helpers/tags/text_field.rb
7
+ class PrometheeEditor < Base
8
+ def render
9
+ options = @options.stringify_keys
10
+ add_default_name_and_id(options)
11
+ data = options.fetch("value") { value_before_type_cast(object) }
12
+
13
+ Promethee::Grid.new(data, name: options["name"]).edit
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end