block_editor 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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