actiontext 7.2.2.1 → 8.0.0.beta1

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.
@@ -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 = 2
15
- PRE = "1"
12
+ MAJOR = 8
13
+ MINOR = 0
14
+ TINY = 0
15
+ PRE = "beta1"
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",