actiontext 7.2.1.1 → 8.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,10 +24,10 @@ module ActionText
24
24
  #
25
25
  # #### Example
26
26
  #
27
- # rich_text_area_tag "content", message.content
27
+ # rich_textarea_tag "content", message.content
28
28
  # # <input type="hidden" name="content" id="trix_input_post_1">
29
29
  # # <trix-editor id="content" input="trix_input_post_1" class="trix-content" ...></trix-editor>
30
- def rich_text_area_tag(name, value = nil, options = {})
30
+ def rich_textarea_tag(name, value = nil, options = {})
31
31
  options = options.symbolize_keys
32
32
  form = options.delete(:form)
33
33
 
@@ -43,6 +43,7 @@ module ActionText
43
43
 
44
44
  input_tag + editor_tag
45
45
  end
46
+ alias_method :rich_text_area_tag, :rich_textarea_tag
46
47
  end
47
48
  end
48
49
 
@@ -56,7 +57,7 @@ module ActionView::Helpers
56
57
  options = @options.stringify_keys
57
58
  add_default_name_and_id(options)
58
59
  options["input"] ||= dom_id(object, [options["id"], :trix_input].compact.join("_")) if object
59
- html_tag = @template_object.rich_text_area_tag(options.delete("name"), options.fetch("value") { value }, options.except("value"))
60
+ html_tag = @template_object.rich_textarea_tag(options.delete("name"), options.fetch("value") { value }, options.except("value"))
60
61
  error_wrapping(html_tag)
61
62
  end
62
63
  end
@@ -76,28 +77,30 @@ module ActionView::Helpers
76
77
  #
77
78
  #
78
79
  # #### Example
79
- # rich_text_area :message, :content
80
+ # rich_textarea :message, :content
80
81
  # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1">
81
82
  # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
82
83
  #
83
- # rich_text_area :message, :content, value: "<h1>Default message</h1>"
84
+ # rich_textarea :message, :content, value: "<h1>Default message</h1>"
84
85
  # # <input type="hidden" name="message[content]" id="message_content_trix_input_message_1" value="<h1>Default message</h1>">
85
86
  # # <trix-editor id="content" input="message_content_trix_input_message_1" class="trix-content" ...></trix-editor>
86
- def rich_text_area(object_name, method, options = {})
87
+ def rich_textarea(object_name, method, options = {})
87
88
  Tags::ActionText.new(object_name, method, self, options).render
88
89
  end
90
+ alias_method :rich_text_area, :rich_textarea
89
91
  end
90
92
 
91
93
  class FormBuilder
92
- # Wraps ActionView::Helpers::FormHelper#rich_text_area for form builders:
94
+ # Wraps ActionView::Helpers::FormHelper#rich_textarea for form builders:
93
95
  #
94
96
  # <%= form_with model: @message do |f| %>
95
- # <%= f.rich_text_area :content %>
97
+ # <%= f.rich_textarea :content %>
96
98
  # <% end %>
97
99
  #
98
100
  # Please refer to the documentation of the base helper for details.
99
- def rich_text_area(method, options = {})
100
- @template.rich_text_area(@object_name, method, objectify_options(options))
101
+ def rich_textarea(method, options = {})
102
+ @template.rich_textarea(@object_name, method, objectify_options(options))
101
103
  end
104
+ alias_method :rich_text_area, :rich_textarea
102
105
  end
103
106
  end
@@ -1,4 +1,4 @@
1
- import { DirectUpload } from "@rails/activestorage"
1
+ import { DirectUpload, dispatchEvent } from "@rails/activestorage"
2
2
 
3
3
  export class AttachmentUpload {
4
4
  constructor(attachment, element) {
@@ -9,24 +9,29 @@ export class AttachmentUpload {
9
9
 
10
10
  start() {
11
11
  this.directUpload.create(this.directUploadDidComplete.bind(this))
12
+ this.dispatch("start")
12
13
  }
13
14
 
14
15
  directUploadWillStoreFileWithXHR(xhr) {
15
16
  xhr.upload.addEventListener("progress", event => {
16
17
  const progress = event.loaded / event.total * 100
17
18
  this.attachment.setUploadProgress(progress)
19
+ if (progress) {
20
+ this.dispatch("progress", { progress: progress })
21
+ }
18
22
  })
19
23
  }
20
24
 
21
25
  directUploadDidComplete(error, attributes) {
22
26
  if (error) {
23
- throw new Error(`Direct upload failed: ${error}`)
27
+ this.dispatchError(error)
28
+ } else {
29
+ this.attachment.setAttributes({
30
+ sgid: attributes.attachable_sgid,
31
+ url: this.createBlobUrl(attributes.signed_id, attributes.filename)
32
+ })
33
+ this.dispatch("end")
24
34
  }
25
-
26
- this.attachment.setAttributes({
27
- sgid: attributes.attachable_sgid,
28
- url: this.createBlobUrl(attributes.signed_id, attributes.filename)
29
- })
30
35
  }
31
36
 
32
37
  createBlobUrl(signedId, filename) {
@@ -35,6 +40,18 @@ export class AttachmentUpload {
35
40
  .replace(":filename", encodeURIComponent(filename))
36
41
  }
37
42
 
43
+ dispatch(name, detail = {}) {
44
+ detail.attachment = this.attachment
45
+ return dispatchEvent(this.element, `direct-upload:${name}`, { detail })
46
+ }
47
+
48
+ dispatchError(error) {
49
+ const event = this.dispatch("error", { error })
50
+ if (!event.defaultPrevented) {
51
+ alert(error);
52
+ }
53
+ }
54
+
38
55
  get directUploadUrl() {
39
56
  return this.element.dataset.directUploadUrl
40
57
  }
@@ -41,13 +41,16 @@ module ActionText
41
41
  # `strict_loading:` will be set to the value of the
42
42
  # `strict_loading_by_default` class attribute (false by default).
43
43
  #
44
+ # * `:store_if_blank` - Pass false to not create RichText records with empty values,
45
+ # if a blank value is provided. Default: true.
46
+ #
44
47
  #
45
48
  # Note: Action Text relies on polymorphic associations, which in turn store
46
49
  # class names in the database. When renaming classes that use `has_rich_text`,
47
50
  # make sure to also update the class names in the
48
51
  # `action_text_rich_texts.record_type` polymorphic type column of the
49
52
  # corresponding rows.
50
- def has_rich_text(name, encrypted: false, strict_loading: strict_loading_by_default)
53
+ def has_rich_text(name, encrypted: false, strict_loading: strict_loading_by_default, store_if_blank: true)
51
54
  class_eval <<-CODE, __FILE__, __LINE__ + 1
52
55
  def #{name}
53
56
  rich_text_#{name} || build_rich_text_#{name}
@@ -56,12 +59,29 @@ module ActionText
56
59
  def #{name}?
57
60
  rich_text_#{name}.present?
58
61
  end
59
-
60
- def #{name}=(body)
61
- self.#{name}.body = body
62
- end
63
62
  CODE
64
63
 
64
+ if store_if_blank
65
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
66
+ def #{name}=(body)
67
+ self.#{name}.body = body
68
+ end
69
+ CODE
70
+ else
71
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
72
+ def #{name}=(body)
73
+ if body.present?
74
+ self.#{name}.body = body
75
+ else
76
+ if #{name}?
77
+ self.#{name}.body = body
78
+ self.#{name}.mark_for_destruction
79
+ end
80
+ end
81
+ end
82
+ CODE
83
+ end
84
+
65
85
  rich_text_class_name = encrypted ? "ActionText::EncryptedRichText" : "ActionText::RichText"
66
86
  has_one :"rich_text_#{name}", -> { where(name: name) },
67
87
  class_name: rich_text_class_name, as: :record, inverse_of: :record, autosave: true, dependent: :destroy,
@@ -9,10 +9,10 @@ module ActionText
9
9
  end
10
10
 
11
11
  module VERSION
12
- MAJOR = 7
13
- MINOR = 2
14
- TINY = 1
15
- PRE = "1"
12
+ MAJOR = 8
13
+ MINOR = 0
14
+ TINY = 0
15
+ PRE = "rc1"
16
16
 
17
17
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
18
18
  end
@@ -65,12 +65,7 @@ module ActionText
65
65
 
66
66
  def plain_text_for_blockquote_node(node, index)
67
67
  text = plain_text_for_block(node)
68
- return "“”" if text.blank?
69
-
70
- text = text.dup
71
- text.insert(text.rindex(/\S/) + 1, "”")
72
- text.insert(text.index(/\S/), "“")
73
- text
68
+ text.sub(/\A(\s*)(.+?)(\s*)\Z/m, '\1“\2”\3')
74
69
  end
75
70
 
76
71
  def plain_text_for_li_node(node, index)
@@ -17,42 +17,45 @@ module ActionText
17
17
  # Examples:
18
18
  #
19
19
  # # <trix-editor id="message_content" ...></trix-editor>
20
- # fill_in_rich_text_area "message_content", with: "Hello <em>world!</em>"
20
+ # fill_in_rich_textarea "message_content", with: "Hello <em>world!</em>"
21
21
  #
22
22
  # # <trix-editor placeholder="Your message here" ...></trix-editor>
23
- # fill_in_rich_text_area "Your message here", with: "Hello <em>world!</em>"
23
+ # fill_in_rich_textarea "Your message here", with: "Hello <em>world!</em>"
24
24
  #
25
25
  # # <label for="message_content">Message content</label>
26
26
  # # <trix-editor id="message_content" ...></trix-editor>
27
- # fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
27
+ # fill_in_rich_textarea "Message content", with: "Hello <em>world!</em>"
28
28
  #
29
29
  # # <trix-editor aria-label="Message content" ...></trix-editor>
30
- # fill_in_rich_text_area "Message content", with: "Hello <em>world!</em>"
30
+ # fill_in_rich_textarea "Message content", with: "Hello <em>world!</em>"
31
31
  #
32
32
  # # <input id="trix_input_1" name="message[content]" type="hidden">
33
33
  # # <trix-editor input="trix_input_1"></trix-editor>
34
- # fill_in_rich_text_area "message[content]", with: "Hello <em>world!</em>"
35
- def fill_in_rich_text_area(locator = nil, with:)
36
- find(:rich_text_area, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
34
+ # fill_in_rich_textarea "message[content]", with: "Hello <em>world!</em>"
35
+ def fill_in_rich_textarea(locator = nil, with:)
36
+ find(:rich_textarea, locator).execute_script("this.editor.loadHTML(arguments[0])", with.to_s)
37
37
  end
38
+ alias_method :fill_in_rich_text_area, :fill_in_rich_textarea
38
39
  end
39
40
  end
40
41
 
41
- Capybara.add_selector :rich_text_area do
42
- label "rich-text area"
43
- xpath do |locator|
44
- if locator.nil?
45
- XPath.descendant(:"trix-editor")
46
- else
47
- input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
48
- input_located_by_label = XPath.anywhere(:label).where(XPath.string.n.is(locator)).attr(:for)
42
+ %i[rich_textarea rich_text_area].each do |rich_textarea|
43
+ Capybara.add_selector rich_textarea do
44
+ label "rich-text area"
45
+ xpath do |locator|
46
+ if locator.nil?
47
+ XPath.descendant(:"trix-editor")
48
+ else
49
+ input_located_by_name = XPath.anywhere(:input).where(XPath.attr(:name) == locator).attr(:id)
50
+ input_located_by_label = XPath.anywhere(:label).where(XPath.string.n.is(locator)).attr(:for)
49
51
 
50
- XPath.descendant(:"trix-editor").where \
51
- XPath.attr(:id).equals(locator) |
52
- XPath.attr(:placeholder).equals(locator) |
53
- XPath.attr(:"aria-label").equals(locator) |
54
- XPath.attr(:input).equals(input_located_by_name) |
55
- XPath.attr(:id).equals(input_located_by_label)
52
+ XPath.descendant(:"trix-editor").where \
53
+ XPath.attr(:id).equals(locator) |
54
+ XPath.attr(:placeholder).equals(locator) |
55
+ XPath.attr(:"aria-label").equals(locator) |
56
+ XPath.attr(:input).equals(input_located_by_name) |
57
+ XPath.attr(:id).equals(input_located_by_label)
58
+ end
56
59
  end
57
60
  end
58
61
  end
@@ -36,20 +36,8 @@ module ActionText
36
36
  end
37
37
 
38
38
  def create_actiontext_files
39
- destination = Pathname(destination_root)
40
-
41
39
  template "actiontext.css", "app/assets/stylesheets/actiontext.css"
42
40
 
43
- unless destination.join("app/assets/application.css").exist?
44
- if (stylesheets = Dir.glob "#{destination_root}/app/assets/stylesheets/application.*.{scss,css}").length > 0
45
- insert_into_file stylesheets.first.to_s, %(@import 'actiontext.css';)
46
- else
47
- say <<~INSTRUCTIONS, :green
48
- To use the Trix editor, you must require 'app/assets/stylesheets/actiontext.css' in your base stylesheet.
49
- INSTRUCTIONS
50
- end
51
- end
52
-
53
41
  gem_root = "#{__dir__}/../../../.."
54
42
 
55
43
  copy_file "#{gem_root}/app/views/active_storage/blobs/_blob.html.erb",