trestle 0.10.0 → 0.10.1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +4 -0
  3. data/app/assets/bundle/trestle/admin.css +3 -3
  4. data/app/assets/bundle/trestle/admin.js +13 -13
  5. data/app/assets/bundle/trestle/photoswipe-2d522a3abaa59f8a8f73.digested.js +6 -0
  6. data/app/helpers/trestle/avatar_helper.rb +20 -14
  7. data/app/helpers/trestle/card_helper.rb +27 -9
  8. data/app/helpers/trestle/container_helper.rb +37 -6
  9. data/app/helpers/trestle/display_helper.rb +11 -0
  10. data/app/helpers/trestle/flash_helper.rb +1 -10
  11. data/app/helpers/trestle/form_helper.rb +32 -18
  12. data/app/helpers/trestle/format_helper.rb +47 -17
  13. data/app/helpers/trestle/gravatar_helper.rb +48 -0
  14. data/app/helpers/trestle/grid_helper.rb +12 -14
  15. data/app/helpers/trestle/headings_helper.rb +2 -23
  16. data/app/helpers/trestle/i18n_helper.rb +1 -0
  17. data/app/helpers/trestle/icon_helper.rb +16 -3
  18. data/app/helpers/trestle/layout_helper.rb +1 -0
  19. data/app/helpers/trestle/modal_helper.rb +21 -2
  20. data/app/helpers/trestle/navigation_helper.rb +1 -0
  21. data/app/helpers/trestle/pagination_helper.rb +1 -0
  22. data/app/helpers/trestle/params_helper.rb +32 -0
  23. data/app/helpers/trestle/sort_helper.rb +38 -7
  24. data/app/helpers/trestle/status_helper.rb +19 -3
  25. data/app/helpers/trestle/tab_helper.rb +42 -7
  26. data/app/helpers/trestle/table_helper.rb +23 -23
  27. data/app/helpers/trestle/timestamp_helper.rb +18 -25
  28. data/app/helpers/trestle/title_helper.rb +2 -0
  29. data/app/helpers/trestle/toolbars_helper.rb +2 -1
  30. data/app/helpers/trestle/turbo/frame_helper.rb +25 -14
  31. data/app/helpers/trestle/url_helper.rb +124 -54
  32. data/app/views/kaminari/trestle/_first_page.html.erb +1 -2
  33. data/app/views/kaminari/trestle/_gap.html.erb +0 -1
  34. data/app/views/kaminari/trestle/_last_page.html.erb +1 -2
  35. data/app/views/kaminari/trestle/_page.html.erb +1 -2
  36. data/app/views/kaminari/trestle/_paginator.html.erb +0 -1
  37. data/app/views/layouts/trestle/admin.html.erb +3 -3
  38. data/app/views/layouts/trestle/modal.html.erb +2 -2
  39. data/app/views/trestle/application/_layout.html.erb +22 -18
  40. data/app/views/trestle/application/_tabs.html.erb +1 -1
  41. data/app/views/trestle/flash/_alert.html.erb +3 -1
  42. data/app/views/trestle/flash/_flash.html.erb +7 -4
  43. data/app/views/trestle/resource/_scopes.html.erb +3 -3
  44. data/app/views/trestle/resource/create.turbo_stream.erb +1 -0
  45. data/app/views/trestle/resource/destroy.turbo_stream.erb +2 -0
  46. data/app/views/trestle/resource/index.html.erb +10 -12
  47. data/app/views/trestle/resource/update.turbo_stream.erb +1 -0
  48. data/app/views/trestle/shared/_sidebar.html.erb +8 -8
  49. data/app/views/trestle/table/_table.html.erb +2 -2
  50. data/config/locales/pt-BR.yml +13 -13
  51. data/frontend/css/components/_scopes.scss +6 -7
  52. data/frontend/css/core/_theme.scss +3 -3
  53. data/frontend/css/layout/_sidebar.scss +2 -0
  54. data/frontend/js/controllers/flatpickr_controller.js +2 -2
  55. data/frontend/js/controllers/lightbox_controller.js +3 -3
  56. data/frontend/js/controllers/modal_trigger_controller.js +2 -2
  57. data/frontend/js/controllers/sidebar_controller.js +13 -3
  58. data/frontend/js/controllers/tab_errors_controller.js +2 -2
  59. data/frontend/js/core/backdrop.js +30 -28
  60. data/frontend/js/core/error_modal.js +7 -9
  61. data/frontend/js/core/fetch.js +2 -0
  62. data/lib/trestle/admin.rb +9 -2
  63. data/lib/trestle/configuration.rb +3 -0
  64. data/lib/trestle/engine.rb +1 -1
  65. data/lib/trestle/evaluation_context.rb +2 -4
  66. data/lib/trestle/form/automatic.rb +1 -1
  67. data/lib/trestle/form/field.rb +1 -1
  68. data/lib/trestle/form/fields/check_box.rb +1 -1
  69. data/lib/trestle/form/fields/collection_check_boxes.rb +1 -1
  70. data/lib/trestle/form/fields/collection_radio_buttons.rb +1 -1
  71. data/lib/trestle/form/fields/date_select.rb +1 -1
  72. data/lib/trestle/form/fields/datetime_select.rb +1 -1
  73. data/lib/trestle/form/fields/form_control.rb +2 -2
  74. data/lib/trestle/form/fields/form_group.rb +4 -4
  75. data/lib/trestle/form/fields/radio_button.rb +1 -1
  76. data/lib/trestle/form/fields/static_field.rb +1 -1
  77. data/lib/trestle/form/fields/time_select.rb +1 -1
  78. data/lib/trestle/form/renderer.rb +2 -4
  79. data/lib/trestle/hook/helpers.rb +21 -0
  80. data/lib/trestle/navigation/block.rb +8 -15
  81. data/lib/trestle/navigation/group.rb +2 -2
  82. data/lib/trestle/navigation/item.rb +21 -4
  83. data/lib/trestle/registry.rb +14 -11
  84. data/lib/trestle/resource/builder.rb +9 -6
  85. data/lib/trestle/resource/toolbar.rb +4 -4
  86. data/lib/trestle/resource.rb +7 -5
  87. data/lib/trestle/scopes/block.rb +8 -12
  88. data/lib/trestle/scopes/definition.rb +6 -2
  89. data/lib/trestle/scopes/scope.rb +13 -10
  90. data/lib/trestle/tab.rb +2 -2
  91. data/lib/trestle/table/column.rb +4 -3
  92. data/lib/trestle/table/row.rb +1 -1
  93. data/lib/trestle/toolbar/builder.rb +6 -6
  94. data/lib/trestle/toolbar/context.rb +2 -4
  95. data/lib/trestle/toolbar/item.rb +10 -19
  96. data/lib/trestle/toolbar/menu.rb +9 -9
  97. data/lib/trestle/version.rb +1 -1
  98. data/lib/trestle.rb +2 -2
  99. data/package.json +7 -7
  100. data/yarn.lock +517 -564
  101. metadata +5 -5
  102. data/app/assets/bundle/trestle/photoswipe-063ce7be40e10b3e6848.digested.js +0 -6
  103. data/app/views/layouts/trestle/admin.turbo_stream.erb +0 -4
@@ -1,27 +1,6 @@
1
1
  module Trestle
2
2
  module HeadingsHelper
3
- def h1(text, options={})
4
- content_tag(:h1, text, options)
5
- end
6
-
7
- def h2(text, options={})
8
- content_tag(:h2, text, options)
9
- end
10
-
11
- def h3(text, options={})
12
- content_tag(:h3, text, options)
13
- end
14
-
15
- def h4(text, options={})
16
- content_tag(:h4, text, options)
17
- end
18
-
19
- def h5(text, options={})
20
- content_tag(:h5, text, options)
21
- end
22
-
23
- def h6(text, options={})
24
- content_tag(:h6, text, options)
25
- end
3
+ # These methods are delegated to the ActionView::Helpers::TagHelper proxy object for convenience.
4
+ delegate :h1, :h2, :h3, :h4, :h5, :h6, to: :tag
26
5
  end
27
6
  end
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module I18nHelper
3
4
  FLATPICKR_LOCALE_CONVERSIONS = { ca: "cat", el: "gr", nb: "no", vi: "vn" }.stringify_keys
4
5
 
@@ -1,8 +1,21 @@
1
1
  module Trestle
2
2
  module IconHelper
3
- def icon(*classes)
4
- options = classes.extract_options!
5
- content_tag(:i, "", options.merge(class: classes))
3
+ # Renders an icon (as an <i> tag).
4
+ #
5
+ # Trestle includes the FontAwesome icon library but other font
6
+ # libraries can be included via custom CSS.
7
+ #
8
+ # classes - List of font name classes to add to the <i> tag
9
+ # attributes - Additional HTML attributes to add to the <i> tag
10
+ #
11
+ # Examples
12
+ #
13
+ # <%= icon("fas fa-star") %>
14
+ # <%= icon("fas", "fa-star", class: "fa-fw text-muted")
15
+ #
16
+ # Return the HTML i tag for the icon.
17
+ def icon(*classes, **attributes)
18
+ tag.i("", **attributes.merge(class: [*classes, attributes[:class]]))
6
19
  end
7
20
  end
8
21
  end
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module LayoutHelper
3
4
  SIDEBAR_CLASSES = {
4
5
  "expanded" => "sidebar-expanded",
@@ -1,16 +1,34 @@
1
1
  module Trestle
2
2
  module ModalHelper
3
+ # Merges the given options with the existing hash of defined modal options.
4
+ #
5
+ # Trestle uses Bootstrap modal markup (https://getbootstrap.com/docs/5.3/components/modal/)
6
+ # to render modals, which consist of an outer .modal wrapper div with an inner
7
+ # .modal-dialog div.
8
+ #
9
+ # The `class` attribute (e.g. `"modal-lg"`) is applied to the inner element, and all
10
+ # other attributes are applied to the outer element. A class can be applied to the
11
+ # wrapper element using the `wrapper_class` option.
12
+ #
13
+ # Examples
14
+ #
15
+ # <% modal_options! class: "modal-lg", controller: "modal-stimulus" %>
16
+ #
17
+ # <% modal_options! wrapper_class: "custom-modal-animation" %>
18
+ #
3
19
  def modal_options!(options)
4
20
  modal_options.merge!(options)
5
21
  end
6
22
 
23
+ # Returns a hash of the currently defined modal options
7
24
  def modal_options
8
25
  @_modal_options ||= {}
9
26
  end
10
27
 
28
+ # Returns the HTML attributes to apply to the modal wrapper (.modal) <div>
11
29
  def modal_wrapper_attributes
12
30
  {
13
- class: ["modal", "fade", modal_options[:wrapper_class]],
31
+ class: ["modal", "fade", modal_options[:wrapper_class]].compact,
14
32
  tabindex: "-1",
15
33
  role: "dialog",
16
34
  data: {
@@ -19,9 +37,10 @@ module Trestle
19
37
  }.deep_merge(modal_options.except(:class, :wrapper_class, :controller))
20
38
  end
21
39
 
40
+ # Returns the HTML attributes to apply to the inner modal dialog (.modal-dialog) <div>
22
41
  def modal_dialog_attributes
23
42
  {
24
- class: ["modal-dialog", modal_options[:class]],
43
+ class: ["modal-dialog", modal_options[:class]].compact,
25
44
  role: "document"
26
45
  }
27
46
  end
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module NavigationHelper
3
4
  def current_navigation_item?(item)
4
5
  current_page?(item.path) || (item.admin && current_admin?(item.admin))
@@ -1,4 +1,5 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module PaginationHelper
3
4
  # Custom version of Kaminari's page_entries_info helper to use a
4
5
  # Trestle-scoped I18n key and add a delimiter to the total count.
@@ -1,10 +1,42 @@
1
1
  module Trestle
2
2
  module ParamsHelper
3
+ # Returns a subset of the params "hash" (an instance of ActionController::Parameters)
4
+ # limited to only those keys that should be considered persistent throughout
5
+ # reordering and pagination.
6
+ #
7
+ # This could be a scope or the current order, but this may be extended by Trestle
8
+ # plugins and the application itself in `Trestle.config.persistent_params` to include
9
+ # search queries, filters, etc.
10
+ #
11
+ # By default this list is set to: [:sort, :order, :scope]
12
+ #
13
+ # Returns an instance of ActionController::Parameters.
3
14
  def persistent_params
4
15
  flat, nested = Trestle.config.persistent_params.partition { |p| !p.is_a?(Hash) }
5
16
  nested = nested.inject({}) { |result, param| result.merge(param) }
6
17
 
7
18
  params.slice(*(flat + nested.keys)).permit(*(flat << nested))
8
19
  end
20
+
21
+ # Converts the current set of persistent params into hidden form fields, suitable
22
+ # for inclusion within a form tag (e.g for search or filtering).
23
+ #
24
+ # except - List of param keys to exclude
25
+ # only - List of param keys to only include
26
+ #
27
+ # Returns a HTML-safe String.
28
+ def serialize_persistent_params(except: [], only: [])
29
+ params = persistent_params
30
+
31
+ if Array(only).any?
32
+ params = params.slice(*only)
33
+ elsif Array(except).any?
34
+ params = params.except(*except)
35
+ end
36
+
37
+ safe_join(to_form_params(params).map { |param|
38
+ tag.input(type: "hidden", name: param[:name], value: param[:value], autocomplete: "off")
39
+ }, "\n")
40
+ end
9
41
  end
10
42
  end
@@ -1,20 +1,47 @@
1
1
  module Trestle
2
2
  module SortHelper
3
- def sort_link(text, field, options={})
4
- sort_link = SortLink.new(field, persistent_params, options)
3
+ # Renders a sort link for a table column header.
4
+ #
5
+ # The `sort` and `order` params are used to determine whether the sort link is
6
+ # active, and which is the current sort direction (asc or desc). CSS classes are
7
+ # applied accordingly which are used to render the appropriate icons alongside the link.
8
+ #
9
+ # The current set of `persistent_params` is merged with the new `sort`/`order` params
10
+ # to build the target link URL.
11
+ #
12
+ # text - Text or HTML content to render as the link label
13
+ # field - The name of the current field, which should match the `sort` param
14
+ # when the collection is being sorted by this field
15
+ # options - Hash of options (default: {}):
16
+ # :default - (Boolean) Set to true if the field is considered to be active
17
+ # even when the `sort` param is blank (default: false)
18
+ # :default_order - (String/Symbol) Specify the default collection order when
19
+ # the `order` param is blank (default: "asc")
20
+ #
21
+ # Examples
22
+ #
23
+ # <%= sort_link "Title", :title, default: true %>
24
+ #
25
+ # <%= sort_link "Created", :created_at, default_order: "desc" %>
26
+ #
27
+ # Returns a HTML-safe String.
28
+ def sort_link(text, field, **options)
29
+ sort_link = SortLink.new(field, persistent_params, **options)
5
30
  link_to text, sort_link.params, class: sort_link.classes
6
31
  end
7
32
 
8
33
  class SortLink
9
34
  attr_reader :field
10
35
 
11
- def initialize(field, params, options)
12
- @field, @params, @options = field, params, options
36
+ def initialize(field, params, default: false, default_order: "asc")
37
+ @field, @params = field, params
38
+
39
+ @default = default
40
+ @default_order = default_order.to_s
13
41
  end
14
42
 
15
43
  def active?
16
- @params[:sort] == field.to_s ||
17
- (@options[:default] && !@params.key?(:sort))
44
+ @params[:sort] == field.to_s || (default? && !@params.key?(:sort))
18
45
  end
19
46
 
20
47
  def params
@@ -33,8 +60,12 @@ module Trestle
33
60
  @params[:order] || default_order
34
61
  end
35
62
 
63
+ def default?
64
+ @default
65
+ end
66
+
36
67
  def default_order
37
- @options.fetch(:default_order, "asc").to_s
68
+ @default_order
38
69
  end
39
70
 
40
71
  def classes
@@ -1,8 +1,24 @@
1
1
  module Trestle
2
2
  module StatusHelper
3
- def status_tag(label, status=:primary, options={})
4
- options[:class] ||= ["badge", "badge-#{status}"]
5
- content_tag(:span, label, options)
3
+ # Renders a status indicator as a Bootstrap badge.
4
+ # (https://getbootstrap.com/docs/5.3/components/badge/)
5
+ #
6
+ # label - Status badge text or HTML content
7
+ # status - Status class (as .badge-{status}) to apply to the badge (default: :primary)
8
+ # attributes - Additional HTML attributes to add to the <span> tag
9
+ #
10
+ # Examples
11
+ #
12
+ # <%= status_tag("Status Text") %>
13
+ #
14
+ # <%= status_tag(icon("fas fa-check"), :success) %>
15
+ #
16
+ # <%= status_tag(safe_join([icon("fas fa-warning"), "Error"], " "), :danger,
17
+ # data: { controller: "tooltip" }, title: "Full error message") %>
18
+ #
19
+ # Returns a HTML-safe String.
20
+ def status_tag(label, status=:primary, **attributes)
21
+ tag.span(label, **attributes.merge(class: ["badge", "badge-#{status}", attributes[:class]]))
6
22
  end
7
23
  end
8
24
  end
@@ -1,13 +1,34 @@
1
1
  module Trestle
2
2
  module TabHelper
3
- def tabs
4
- @_trestle_tabs ||= {}
5
- end
3
+ # Creates a tab pane using content via the given block, :partial option or partial
4
+ # template automatically inferred from the tab name.
5
+ #
6
+ # It also appends a Trestle::Tab object to the list of declared tabs that is
7
+ # accessible via the #tabs helper (e.g. for rendering the tab links).
8
+ #
9
+ # name - (Symbol) Internal name for the tab
10
+ # options - Hash of options (default: {}):
11
+ # :label - Optional tab label. If not provided, will be inferred by the
12
+ # admin-translated tab name (`admin.tabs.{name}` i18n scope)
13
+ # :badge - Optional badge to show next to the tab label (e.g. a counter)
14
+ # :partial - Optional partial template name to use when a block is not provided
15
+ #
16
+ # Examples
17
+ #
18
+ # <%= tab :details %>
19
+ # => Automatically renders the 'details' partial (e.g. "_details.html.erb") as the tab content
20
+ #
21
+ # <%= tab :metadata, partial: "meta" %>
22
+ # => Renders the 'meta' partial (e.g. "_meta.html.erb") as the tab content
23
+ #
24
+ # <%= tab :comments do %> ...
25
+ # => Renders the given block as the tab content
26
+ #
27
+ # Returns a HTML-safe String.
28
+ def tab(name, **options)
29
+ tabs[name] = tab = Tab.new(name, **options)
6
30
 
7
- def tab(name, options={})
8
- tabs[name] = tab = Tab.new(name, options)
9
-
10
- content_tag(:div, id: tab.id(("modal" if modal_request?)), class: ["tab-pane", ('active' if name == tabs.keys.first)], role: "tabpanel") do
31
+ tag.div(id: tab.id(("modal" if modal_request?)), class: ["tab-pane", ('active' if name == tabs.keys.first)], role: "tabpanel") do
11
32
  if block_given?
12
33
  yield
13
34
  elsif options[:partial]
@@ -17,5 +38,19 @@ module Trestle
17
38
  end
18
39
  end
19
40
  end
41
+
42
+ # Returns a hash (name => Trestle::Tab) of the currently declared tabs.
43
+ def tabs
44
+ @_trestle_tabs ||= {}
45
+ end
46
+
47
+ # Captures the given block (using `content_for`) as the sidebar content.
48
+ def sidebar(&block)
49
+ content_for(:sidebar, &block)
50
+ end
51
+
52
+ def render_sidebar_as_tab?
53
+ modal_request? && content_for?(:sidebar)
54
+ end
20
55
  end
21
56
  end
@@ -2,15 +2,17 @@ module Trestle
2
2
  module TableHelper
3
3
  # Renders an existing named table or builds and renders a custom table if a block is provided.
4
4
  #
5
- # name - The (optional) name of the table to render (as a Symbol), or the actual Trestle::Table instance itself.
6
- # options - Hash of options that will be passed to the table builder (default: {}):
7
- # :collection - The collection that should be rendered within the table. It should be an
8
- # Array-like object, but will most likely be an ActiveRecord scope. It can
9
- # also be a callable object (i.e. a Proc) in which case the result of calling
10
- # the block will be used as the collection.
11
- # See Trestle::Table::Builder for additional options.
12
- # block - An optional block that is passed to Trestle::Table::Builder to define a custom table.
13
- # One of either the name or block must be provided, but not both.
5
+ # One of either the name or block must be provided, but not both.
6
+ #
7
+ # name - The (optional) name of the table to render (as a Symbol), or the
8
+ # actual Trestle::Table instance itself
9
+ # collection - The collection that should be rendered within the table.
10
+ # It should be an Array-like object, but will most likely be an
11
+ # ActiveRecord scope. It can also be a callable object (i.e. a Proc) in
12
+ # which case the result of calling the block will be used as the collection
13
+ # options - Hash of options that will be passed to the table builder (default: {}).
14
+ # See Trestle::Table::Builder for additional options
15
+ # block - An optional block that is passed to Trestle::Table::Builder to define a custom table
14
16
  #
15
17
  # Examples
16
18
  #
@@ -23,26 +25,23 @@ module Trestle
23
25
  # <%= table :accounts %>
24
26
  #
25
27
  # Returns the HTML representation of the table as a HTML-safe String.
26
- def table(name=nil, options={}, &block)
28
+ def table(name=nil, collection: nil, **options, &block)
27
29
  if block_given?
28
- if name.is_a?(Hash)
29
- options = name
30
- else
31
- collection = name
32
- end
33
-
34
- table = Table::Builder.build(options, &block)
30
+ table = Table::Builder.build(**options, &block)
35
31
  else
36
32
  if name.is_a?(Trestle::Table)
37
33
  table = name
38
34
  else
39
- table = admin.tables.fetch(name) { raise ArgumentError, "Unable to find table named #{name.inspect}" }
35
+ table = admin.tables.fetch(name) {
36
+ raise ArgumentError, "Unable to find table named #{name.inspect}"
37
+ }
40
38
  end
41
39
 
42
- table = table.with_options(options.reverse_merge(sortable: false))
40
+ table = table.with_options(sortable: false, **options)
43
41
  end
44
42
 
45
- collection ||= options[:collection] || table.options[:collection]
43
+ collection ||= name if block_given?
44
+ collection ||= table.options[:collection]
46
45
  collection = collection.call if collection.respond_to?(:call)
47
46
 
48
47
  render "trestle/table/table", table: table, collection: collection
@@ -50,12 +49,13 @@ module Trestle
50
49
 
51
50
  # Renders the pagination controls for a collection.
52
51
  #
53
- # collection - The paginated Kaminari collection to render controls for (required).
54
- # options - Hash of options that will be passed to the Kaminari #paginate method (default: {}):
52
+ # collection - The paginated Kaminari collection to render controls for (required)
53
+ # entry_name - Custom item name passed to the Kaminari #page_entries_info helper
54
+ # options - Hash of options that will be passed to the Kaminari #paginate method (default: {})
55
55
  #
56
56
  # Examples
57
57
  #
58
- # <%= pagination collection: Account.page(params[:page]), remote: true %>
58
+ # <%= pagination collection: Account.page(params[:page]) %>
59
59
  #
60
60
  # Returns the HTML representation of the pagination controls as a HTML-safe String.
61
61
  def pagination(collection:, entry_name: nil, **options)
@@ -1,13 +1,13 @@
1
1
  module Trestle
2
2
  module TimestampHelper
3
- # Renders a Time object as a formatted timestamp (using a <time> tag)
3
+ # Renders a Time object as a formatted timestamp (using a <time> tag).
4
4
  #
5
- # time - The Time object to format.
6
- # options - Hash of options (default: {}):
7
- # :class - Additional HTML classes to add to the <time> tag.
8
- # :precision - Time precision, either :minutes or :seconds (default: :minutes).
9
- # :date_format - I18n date format to use for the date (default: :trestle_date).
10
- # :time_format - I18n time format to use for the time (default: :trestle_time).
5
+ # time - The Time object to format
6
+ # precision - Time precision, either :minutes or :seconds (default: :minutes)
7
+ # date_format - I18n date format to use for the date (default: :trestle_date)
8
+ # time_format - I18n time format to use for the time (default: :trestle_time,
9
+ # or :trestle_time_with_seconds if precision is :seconds)
10
+ # attributes - Additional HTML attributes to add to the <time> tag
11
11
  #
12
12
  # Examples
13
13
  #
@@ -16,28 +16,24 @@ module Trestle
16
16
  # <%= timestamp(Time.current, class: "timestamp-inline", precision: :seconds) %>
17
17
  #
18
18
  # Returns the HTML representation of the given Time.
19
- def timestamp(time, options={})
19
+ def timestamp(time, precision: Trestle.config.timestamp_precision, date_format: :trestle_date, time_format: nil, **attributes)
20
20
  return unless time
21
21
 
22
- classes = ["timestamp", options[:class]].compact
23
- precision = options.fetch(:precision) { Trestle.config.timestamp_precision }
24
- date_format = options.fetch(:date_format) { :trestle_date }
25
- time_format = options.fetch(:time_format) { precision == :seconds ? :trestle_time_with_seconds : :trestle_time }
22
+ time_format ||= (precision == :seconds) ? :trestle_time_with_seconds : :trestle_time
26
23
 
27
- time_tag(time, class: classes) do
24
+ time_tag(time, **attributes.merge(class: ["timestamp", attributes[:class]])) do
28
25
  safe_join([
29
- l(time, format: date_format, default: proc { |date| "#{date.day.ordinalize} %b %Y" }),
30
- content_tag(:small, l(time, format: time_format, default: "%l:%M:%S %p"))
26
+ tag.span(l(time, format: date_format, default: proc { |date| "#{date.day.ordinalize} %b %Y" })),
27
+ tag.small(l(time, format: time_format, default: "%l:%M:%S %p"))
31
28
  ], "\n")
32
29
  end
33
30
  end
34
31
 
35
- # Renders a Date object as formatted datestamp (using a <time> tag)
32
+ # Renders a Date object as formatted datestamp (using a <time> tag).
36
33
  #
37
- # date - The Date object to format.
38
- # options - Hash of options (default: {}):
39
- # :class - Additional HTML classes to add to the <time> tag.
40
- # :format - I18n date format to use (default: :trestle_calendar).
34
+ # date - The Date object to format
35
+ # format - I18n date format to use (default: :trestle_calendar)
36
+ # attributes - Additional HTML attributes to add to the <time> tag
41
37
  #
42
38
  # Examples
43
39
  #
@@ -46,13 +42,10 @@ module Trestle
46
42
  # <%= datestamp(article.created_at, format: :trestle_date, class: "custom-datestamp") %>
47
43
  #
48
44
  # Returns the HTML representation of the given Date.
49
- def datestamp(date, options={})
45
+ def datestamp(date, format: :trestle_calendar, **attributes)
50
46
  return unless date
51
47
 
52
- classes = ["datestamp", options[:class]].compact
53
- format = options.fetch(:format) { :trestle_calendar}
54
-
55
- time_tag(date, class: classes) do
48
+ time_tag(date, **attributes.merge(class: ["datestamp", attributes[:class]])) do
56
49
  l(date, format: format, default: "%-m/%-d/%Y")
57
50
  end
58
51
  end
@@ -1,5 +1,7 @@
1
1
  module Trestle
2
2
  module TitleHelper
3
+ # Returns the page title (if set using content_for), falling back to
4
+ # the titleized action name as a default if not set.
3
5
  def title
4
6
  content_for(:title) || default_title
5
7
  end
@@ -1,9 +1,10 @@
1
1
  module Trestle
2
+ # [Internal]
2
3
  module ToolbarsHelper
3
4
  def render_toolbar(toolbar, *args)
4
5
  result = toolbar.groups(self, *args).map do |items|
5
6
  if items.many?
6
- content_tag(:div, class: "btn-group", role: "group") do
7
+ tag.div(class: "btn-group", role: "group") do
7
8
  safe_join(items, "\n")
8
9
  end
9
10
  else
@@ -1,19 +1,20 @@
1
1
  module Trestle
2
2
  module Turbo
3
3
  module FrameHelper
4
- def index_turbo_frame(options={}, &block)
5
- defaults = {
6
- id: "index",
7
- data: {
8
- controller: "reloadable",
9
- turbo_action: "advance"
10
- }
11
- }
12
-
13
- content_tag("turbo-frame", defaults.merge(options), &block)
14
- end
15
-
16
- def resource_turbo_frame(instance, options={}, &block)
4
+ # Renders a <turbo-frame> container for an instance/resource view. A resource
5
+ # turbo frame sets its DOM id from the given instance and has a default target of
6
+ # "_top" (except for modal requests).
7
+ #
8
+ # attributes - Additional HTML attributes to add to the <turbo-frame> tag
9
+ #
10
+ # Examples
11
+ #
12
+ # <%= resource_turbo_frame(article) do %> ...
13
+ #
14
+ # <%= resource_turbo_frame(article, id: dom_id(article, "comment")) %> ...
15
+ #
16
+ # Returns a HTML-safe String.
17
+ def resource_turbo_frame(instance, **attributes, &block)
17
18
  defaults = {
18
19
  id: dom_id(instance),
19
20
  target: ("_top" unless modal_request?),
@@ -22,7 +23,17 @@ module Trestle
22
23
  }
23
24
  }
24
25
 
25
- content_tag("turbo-frame", defaults.merge(options), &block)
26
+ tag.turbo_frame(**defaults.merge(attributes), &block)
27
+ end
28
+
29
+ # [DEPRECATED]
30
+ #
31
+ # The #content turbo-frame found in app/views/trestle/application/_layout.html.erb
32
+ # is now used as a common top-level hook for the 'reloadable' Stimulus controller.
33
+ def index_turbo_frame(**attributes, &block)
34
+ Trestle.deprecator.warn("The index_turbo_frame helper is deprecated and will be removed in future versions of Trestle.")
35
+ yield
36
+ nil
26
37
  end
27
38
  end
28
39
  end