block_editor 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +147 -0
  4. data/Rakefile +18 -0
  5. data/app/assets/config/block_editor_manifest.js +1 -0
  6. data/app/assets/stylesheets/block_editor/backend.scss +4 -0
  7. data/app/assets/stylesheets/block_editor/backend/blocks.scss +0 -0
  8. data/app/assets/stylesheets/block_editor/frontend.scss +1 -0
  9. data/app/assets/stylesheets/block_editor/frontend/blocks.scss +0 -0
  10. data/app/controllers/block_editor/application_controller.rb +4 -0
  11. data/app/helpers/block_editor/application_helper.rb +11 -0
  12. data/app/javascript/block_editor/blocks/button/edit.js +240 -0
  13. data/app/javascript/block_editor/blocks/column/edit.js +93 -0
  14. data/app/javascript/block_editor/blocks/image/edit.js +656 -0
  15. data/app/javascript/block_editor/blocks/index.js +263 -0
  16. data/app/javascript/block_editor/components/block-editor/index.js +88 -0
  17. data/app/javascript/block_editor/components/block-editor/styles.scss +39 -0
  18. data/app/javascript/block_editor/components/header/index.js +45 -0
  19. data/app/javascript/block_editor/components/header/redo.js +36 -0
  20. data/app/javascript/block_editor/components/header/styles.scss +14 -0
  21. data/app/javascript/block_editor/components/header/undo.js +36 -0
  22. data/app/javascript/block_editor/components/media-upload/index.js +37 -0
  23. data/app/javascript/block_editor/components/notices/index.js +26 -0
  24. data/app/javascript/block_editor/components/notices/styles.scss +9 -0
  25. data/app/javascript/block_editor/components/sidebar/index.js +31 -0
  26. data/app/javascript/block_editor/components/sidebar/styles.scss +43 -0
  27. data/app/javascript/block_editor/stores/action-types.js +4 -0
  28. data/app/javascript/block_editor/stores/actions.js +41 -0
  29. data/app/javascript/block_editor/stores/controls.js +21 -0
  30. data/app/javascript/block_editor/stores/index.js +30 -0
  31. data/app/javascript/block_editor/stores/reducer.js +20 -0
  32. data/app/javascript/block_editor/stores/resolvers.js +10 -0
  33. data/app/javascript/block_editor/stores/selectors.js +13 -0
  34. data/app/javascript/controllers/block_editor_controller.jsx +42 -0
  35. data/app/javascript/controllers/index.js +6 -0
  36. data/app/javascript/packs/block_editor/application.js +2 -0
  37. data/app/javascript/packs/block_editor/application.scss +108 -0
  38. data/app/jobs/block_editor/application_job.rb +4 -0
  39. data/app/mailers/block_editor/application_mailer.rb +6 -0
  40. data/app/models/block_editor/application_record.rb +5 -0
  41. data/app/models/block_editor/block_list.rb +7 -0
  42. data/app/models/concerns/block_editor/listable.rb +24 -0
  43. data/app/views/layouts/block_editor/application.html.erb +15 -0
  44. data/config/initializers/webpacker_extension.rb +12 -0
  45. data/config/routes.rb +2 -0
  46. data/config/webpack/development.js +5 -0
  47. data/config/webpack/environment.js +3 -0
  48. data/config/webpack/production.js +5 -0
  49. data/config/webpack/test.js +5 -0
  50. data/config/webpacker.yml +92 -0
  51. data/db/migrate/20210312032114_create_block_lists.rb +11 -0
  52. data/lib/block_editor.rb +26 -0
  53. data/lib/block_editor/block_list_renderer.rb +43 -0
  54. data/lib/block_editor/blocks/base.rb +32 -0
  55. data/lib/block_editor/engine.rb +34 -0
  56. data/lib/block_editor/instance.rb +19 -0
  57. data/lib/block_editor/version.rb +3 -0
  58. data/lib/tasks/block_editor_tasks.rake +59 -0
  59. metadata +131 -0
@@ -0,0 +1,4 @@
1
+ module BlockEditor
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module BlockEditor
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module BlockEditor
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module BlockEditor
2
+ # Represents a block list
3
+ class BlockList < ApplicationRecord
4
+ # Associations
5
+ belongs_to :listable, polymorphic: true, touch: true
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ module BlockEditor
2
+ module Listable
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ has_many :block_lists, as: :listable, class_name: 'BlockEditor::BlockList'
7
+ has_one :active_block_list, -> { where active: true }, class_name: 'BlockEditor::BlockList', as: :listable
8
+
9
+ validates :active_block_list, presence: true
10
+
11
+ accepts_nested_attributes_for :active_block_list
12
+
13
+ after_initialize :set_block_list_defaults
14
+ end
15
+
16
+ private
17
+
18
+ def set_block_list_defaults
19
+ return if self.persisted?
20
+
21
+ self.active_block_list ||= self.build_active_block_list(listable: self)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Block editor</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "block_editor/application", media: "all" %>
9
+ </head>
10
+ <body>
11
+
12
+ <%= yield %>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,12 @@
1
+ module Webpacker::DynamicTag
2
+ def javascript_pack_tag(*names, **options)
3
+ return super unless options[:webpacker]
4
+ new_helper = self.dup
5
+ new_helper.define_singleton_method(:current_webpacker_instance) do
6
+ options[:webpacker].constantize.webpacker
7
+ end
8
+ new_helper.javascript_pack_tag(*names, **options.except(:webpacker))
9
+ end
10
+ end
11
+
12
+ Webpacker::Helper.prepend Webpacker::DynamicTag
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ BlockEditor::Engine.routes.draw do
2
+ end
@@ -0,0 +1,5 @@
1
+ process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2
+
3
+ const environment = require('./environment')
4
+
5
+ module.exports = environment.toWebpackConfig()
@@ -0,0 +1,3 @@
1
+ const { environment } = require('@rails/webpacker')
2
+
3
+ module.exports = environment
@@ -0,0 +1,5 @@
1
+ process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2
+
3
+ const environment = require('./environment')
4
+
5
+ module.exports = environment.toWebpackConfig()
@@ -0,0 +1,5 @@
1
+ process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2
+
3
+ const environment = require('./environment')
4
+
5
+ module.exports = environment.toWebpackConfig()
@@ -0,0 +1,92 @@
1
+ # Note: You must restart bin/webpack-dev-server for changes to take effect
2
+
3
+ default: &default
4
+ source_path: app/javascript
5
+ source_entry_path: packs
6
+ public_root_path: public
7
+ public_output_path: packs
8
+ cache_path: tmp/cache/webpacker
9
+ webpack_compile_output: true
10
+
11
+ # Additional paths webpack should lookup modules
12
+ # ['app/assets', 'engine/foo/app/assets']
13
+ additional_paths: []
14
+
15
+ # Reload manifest.json on all requests so we reload latest compiled packs
16
+ cache_manifest: false
17
+
18
+ # Extract and emit a css file
19
+ extract_css: false
20
+
21
+ static_assets_extensions:
22
+ - .jpg
23
+ - .jpeg
24
+ - .png
25
+ - .gif
26
+ - .tiff
27
+ - .ico
28
+ - .svg
29
+ - .eot
30
+ - .otf
31
+ - .ttf
32
+ - .woff
33
+ - .woff2
34
+
35
+ extensions:
36
+ - .mjs
37
+ - .js
38
+ - .sass
39
+ - .scss
40
+ - .css
41
+ - .module.sass
42
+ - .module.scss
43
+ - .module.css
44
+ - .png
45
+ - .svg
46
+ - .gif
47
+ - .jpeg
48
+ - .jpg
49
+
50
+ development:
51
+ <<: *default
52
+ compile: true
53
+
54
+ # Reference: https://webpack.js.org/configuration/dev-server/
55
+ dev_server:
56
+ https: false
57
+ host: localhost
58
+ port: 3035
59
+ public: localhost:3035
60
+ hmr: false
61
+ # Inline should be set to true if using HMR
62
+ inline: true
63
+ overlay: true
64
+ compress: true
65
+ disable_host_check: true
66
+ use_local_ip: false
67
+ quiet: false
68
+ pretty: false
69
+ headers:
70
+ 'Access-Control-Allow-Origin': '*'
71
+ watch_options:
72
+ ignored: '**/node_modules/**'
73
+
74
+
75
+ test:
76
+ <<: *default
77
+ compile: true
78
+
79
+ # Compile test packs to a separate directory
80
+ public_output_path: packs-test
81
+
82
+ production:
83
+ <<: *default
84
+
85
+ # Production depends on precompilation of packs prior to booting for performance.
86
+ compile: false
87
+
88
+ # Extract and emit a css file
89
+ extract_css: true
90
+
91
+ # Cache manifest.json for performance
92
+ cache_manifest: true
@@ -0,0 +1,11 @@
1
+ class CreateBlockLists < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :block_editor_block_lists do |t|
4
+ t.string :name
5
+ t.text :content
6
+ t.boolean :active, default: false
7
+ t.references :listable, polymorphic: true
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ require "block_editor/version"
2
+ require "block_editor/engine"
3
+
4
+ require "block_editor/instance"
5
+ require 'block_editor/blocks/base'
6
+ require 'block_editor/block_list_renderer'
7
+
8
+ module BlockEditor
9
+ ROOT_PATH = Pathname.new(File.join(__dir__, ".."))
10
+
11
+ class << self
12
+ def webpacker
13
+ @webpacker ||= ::Webpacker::Instance.new(
14
+ root_path: ROOT_PATH,
15
+ config_path: ROOT_PATH.join("config/webpacker.yml")
16
+ )
17
+ end
18
+ end
19
+
20
+ mattr_accessor :dynamic_blocks
21
+ @@dynamic_blocks = []
22
+
23
+ mattr_accessor :frontend_parent_controller
24
+ @@frontend_parent_controller = 'ApplicationController'
25
+ end
26
+
@@ -0,0 +1,43 @@
1
+ module BlockEditor
2
+ # Handles the rendering of a block list including dynamic blocks and removing HTML comments
3
+ class BlockListRenderer
4
+ # Renders dynamic blocks within the HTML snippet then strips all HTML comments (including Gutenberg markup)
5
+ #
6
+ # @param raw_html [String]
7
+ #
8
+ # @return [String] Parsed content
9
+ def self.render(raw_html)
10
+ html = Nokogiri::HTML(raw_html)
11
+
12
+ # # Find & render all instances of a dynamic block
13
+ BlockEditor.dynamic_blocks.each do |dynamic_block|
14
+ html.xpath('//comment()').select {|comment| comment.inner_text.starts_with?(" wp:#{dynamic_block.name}") }.each do |block_instance|
15
+ block_attributes = block_instance.inner_text.split(" wp:#{dynamic_block.name}")[1][0...-1]
16
+ block_attributes = block_attributes.blank? ? {} : JSON.parse(block_attributes)
17
+ block_instance.replace(render_block(dynamic_block, block_attributes))
18
+ end
19
+ end
20
+
21
+ html.xpath('//comment()').remove
22
+ html.css('body').inner_html.html_safe
23
+ end
24
+
25
+ # Renders a specific block using the provided options
26
+ #
27
+ # @param block [String] name of block
28
+ # @param options [Hash] block options to use when rendering
29
+ #
30
+ # @return [String] block content (HTML)
31
+ def self.render_block(block, options)
32
+ block.render(options)
33
+ rescue StandardError => e
34
+ respond_with_block_error(e)
35
+ end
36
+
37
+ # Handles block errors
38
+ def self.respond_with_block_error(error)
39
+ Rails.logger.error("Error rendering block - #{error.message}")
40
+ ''
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ module BlockEditor
2
+ # Blocks used to render dynamic content
3
+ module Blocks
4
+ # Base for dynamic blocks
5
+ class Base
6
+ def self.name
7
+ raise NotImplementedError, 'Must specify block name'
8
+ end
9
+
10
+ # Render the block
11
+ def self.render(options = {})
12
+ options = options.reverse_merge(default_options.with_indifferent_access)
13
+
14
+ controller.render(
15
+ partial: "block_editor/blocks/#{name}/block",
16
+ locals: { collection: options },
17
+ layout: false
18
+ )
19
+ end
20
+
21
+ # Frontend controller used to render views
22
+ def self.controller
23
+ BlockEditor.frontend_parent_controller.constantize
24
+ end
25
+
26
+ # Default widget options
27
+ def self.default_options
28
+ {}
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module BlockEditor
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace BlockEditor
4
+
5
+ initializer "webpacker.proxy" do |app|
6
+ insert_middleware = begin
7
+ BlockEditor.webpacker.config.dev_server.present?
8
+ rescue
9
+ nil
10
+ end
11
+ next unless insert_middleware
12
+
13
+ app.middleware.insert_before(
14
+ 0, Webpacker::DevServerProxy,
15
+ ssl_verify_none: true,
16
+ webpacker: BlockEditor.webpacker
17
+ )
18
+ end
19
+
20
+ # Initializer to combine this engines static assets with the static assets of the host application
21
+ initializer 'static assets' do |app|
22
+ app.middleware.insert_before(::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/public")
23
+ end
24
+
25
+ initializer 'block_editor.assets.precompile' do |app|
26
+ assets_for_precompile = [
27
+ 'block_editor/frontend.css',
28
+ 'block_editor/backend.css'
29
+ ]
30
+
31
+ app.config.assets.precompile.concat assets_for_precompile
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ module BlockEditor
2
+ class Instance
3
+ include ActionView::Helpers::TagHelper
4
+ include ActionView::Helpers::FormTagHelper
5
+
6
+ attr_accessor :output_buffer
7
+
8
+ def self.render(form_builder)
9
+ self.new.render(form_builder)
10
+ end
11
+
12
+ def render(form_builder)
13
+ content_tag(:div, data: { controller: 'block-editor' }) do
14
+ form_builder.hidden_field(:content, data: { 'block-editor-target' => 'input' }) +
15
+ content_tag('div', nil, { class: 'block-editor', data: { 'block-editor-target' => 'output' } })
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module BlockEditor
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,59 @@
1
+ def ensure_log_goes_to_stdout
2
+ old_logger = Webpacker.logger
3
+ Webpacker.logger = ActiveSupport::Logger.new(STDOUT)
4
+ yield
5
+ ensure
6
+ Webpacker.logger = old_logger
7
+ end
8
+
9
+
10
+ namespace :block_editor do
11
+ namespace :webpacker do
12
+ desc "Install deps with yarn"
13
+ task :yarn_install do
14
+ Dir.chdir(File.join(__dir__, "../..")) do
15
+ system "yarn install --no-progress --production"
16
+ end
17
+ end
18
+
19
+ desc "Compile JavaScript packs using webpack for production with digests"
20
+ task compile: [:yarn_install, :environment] do
21
+ Webpacker.with_node_env("production") do
22
+ ensure_log_goes_to_stdout do
23
+ if BlockEditor.webpacker.commands.compile
24
+ # Successful compilation!
25
+ else
26
+ # Failed compilation
27
+ exit!
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def yarn_install_available?
36
+ rails_major = Rails::VERSION::MAJOR
37
+ rails_minor = Rails::VERSION::MINOR
38
+
39
+ rails_major > 5 || (rails_major == 5 && rails_minor >= 1)
40
+ end
41
+
42
+ def enhance_assets_precompile
43
+ # yarn:install was added in Rails 5.1
44
+ deps = yarn_install_available? ? [] : ["block_editor:webpacker:yarn_install"]
45
+ Rake::Task["assets:precompile"].enhance(deps) do
46
+ Rake::Task["block_editor:webpacker:compile"].invoke
47
+ end
48
+ end
49
+
50
+ # Compile packs after we've compiled all other assets during precompilation
51
+ skip_webpacker_precompile = %w(no false n f).include?(ENV["WEBPACKER_PRECOMPILE"])
52
+
53
+ unless skip_webpacker_precompile
54
+ if Rake::Task.task_defined?("assets:precompile")
55
+ enhance_assets_precompile
56
+ else
57
+ Rake::Task.define_task("assets:precompile" => "block_editor:webpacker:compile")
58
+ end
59
+ end