trestle 0.8.4 → 0.8.11

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 (164) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/.travis.yml +14 -1
  4. data/Gemfile +6 -0
  5. data/README.md +15 -9
  6. data/app/assets/javascripts/trestle/admin.js +11 -10
  7. data/app/assets/javascripts/trestle/components/_confirmation.js +40 -0
  8. data/app/assets/javascripts/trestle/components/_datepicker.js +37 -0
  9. data/app/assets/javascripts/trestle/components/_dialog.js +136 -0
  10. data/app/assets/javascripts/trestle/{_errors.js → components/_errors.js} +2 -2
  11. data/app/assets/javascripts/trestle/components/_form.js +75 -0
  12. data/app/assets/javascripts/trestle/{_gallery.js → components/_gallery.js} +3 -3
  13. data/app/assets/javascripts/trestle/{_select.js → components/_select.js} +2 -2
  14. data/app/assets/javascripts/trestle/components/_sidebar.js +77 -0
  15. data/app/assets/javascripts/trestle/{_table.js → components/_table.js} +1 -1
  16. data/app/assets/javascripts/trestle/components/_tabs.js +25 -0
  17. data/app/assets/javascripts/trestle/components/_tooltips.js +19 -0
  18. data/app/assets/javascripts/trestle/core/_contexts.js +13 -0
  19. data/app/assets/javascripts/trestle/core/_cookies.js +24 -0
  20. data/app/assets/javascripts/trestle/core/_events.js +39 -0
  21. data/app/assets/javascripts/trestle/core/_l10n.js +23 -0
  22. data/app/assets/javascripts/trestle/core/_visit.js +10 -0
  23. data/app/assets/stylesheets/trestle/components/_alerts.scss +28 -0
  24. data/app/assets/stylesheets/trestle/components/_avatar.scss +23 -1
  25. data/app/assets/stylesheets/trestle/components/_breadcrumbs.scss +12 -0
  26. data/app/assets/stylesheets/trestle/components/_buttons.scss +111 -1
  27. data/app/assets/stylesheets/trestle/components/_content.scss +53 -21
  28. data/app/assets/stylesheets/trestle/components/_datepicker.scss +28 -0
  29. data/app/assets/stylesheets/trestle/components/_fields.scss +17 -0
  30. data/app/assets/stylesheets/trestle/components/_input-group.scss +26 -1
  31. data/app/assets/stylesheets/trestle/components/_modal.scss +108 -0
  32. data/app/assets/stylesheets/trestle/components/_navigation.scss +104 -14
  33. data/app/assets/stylesheets/trestle/components/_pagination.scss +3 -3
  34. data/app/assets/stylesheets/trestle/components/_scopes.scss +11 -2
  35. data/app/assets/stylesheets/trestle/components/_sidebar.scss +3 -2
  36. data/app/assets/stylesheets/trestle/components/_table.scss +34 -10
  37. data/app/assets/stylesheets/trestle/components/_tags.scss +9 -0
  38. data/app/assets/stylesheets/trestle/components/_timestamp.scss +4 -1
  39. data/app/assets/stylesheets/trestle/components/_toolbars.scss +55 -0
  40. data/app/assets/stylesheets/trestle/components/_wells.scss +9 -1
  41. data/app/assets/stylesheets/trestle/core/_defaults.scss +21 -6
  42. data/app/assets/stylesheets/trestle/core/_layout.scss +8 -0
  43. data/app/assets/stylesheets/trestle/core/_mixins.scss +11 -0
  44. data/app/assets/stylesheets/trestle/core/_typography.scss +39 -0
  45. data/app/controllers/concerns/trestle/controller/breadcrumbs.rb +21 -0
  46. data/app/controllers/concerns/trestle/controller/callbacks.rb +21 -0
  47. data/app/controllers/concerns/trestle/controller/dialog.rb +16 -0
  48. data/app/controllers/concerns/trestle/controller/helpers.rb +18 -0
  49. data/app/controllers/concerns/trestle/controller/layout.rb +16 -0
  50. data/app/controllers/concerns/trestle/controller/location.rb +15 -0
  51. data/app/controllers/trestle/application_controller.rb +6 -30
  52. data/app/helpers/trestle/avatar_helper.rb +9 -2
  53. data/app/helpers/trestle/flash_helper.rb +15 -0
  54. data/app/helpers/trestle/form_helper.rb +8 -5
  55. data/app/helpers/trestle/format_helper.rb +7 -3
  56. data/app/helpers/trestle/headings_helper.rb +27 -0
  57. data/app/helpers/trestle/hook_helper.rb +13 -4
  58. data/app/helpers/trestle/i18n_helper.rb +14 -0
  59. data/app/helpers/trestle/panel_helper.rb +24 -0
  60. data/app/helpers/trestle/tab_helper.rb +2 -2
  61. data/app/helpers/trestle/table_helper.rb +41 -2
  62. data/app/helpers/trestle/timestamp_helper.rb +49 -7
  63. data/app/helpers/trestle/toolbars_helper.rb +34 -0
  64. data/app/helpers/trestle/url_helper.rb +72 -8
  65. data/app/views/layouts/trestle/admin.html.erb +3 -5
  66. data/app/views/trestle/application/_dialog.html.erb +34 -0
  67. data/app/views/trestle/application/_header.html.erb +22 -20
  68. data/app/views/trestle/application/_layout.html.erb +1 -1
  69. data/app/views/trestle/application/_tabs.html.erb +8 -2
  70. data/app/views/trestle/{application → flash}/_alert.html.erb +7 -2
  71. data/app/views/trestle/flash/_debug.html.erb +8 -0
  72. data/app/views/trestle/flash/_flash.html.erb +7 -0
  73. data/app/views/trestle/resource/_scopes.html.erb +1 -1
  74. data/app/views/trestle/resource/edit.html.erb +8 -10
  75. data/app/views/trestle/resource/index.html.erb +16 -4
  76. data/app/views/trestle/resource/new.html.erb +6 -6
  77. data/app/views/trestle/resource/show.html.erb +8 -10
  78. data/app/views/trestle/shared/_sidebar.html.erb +10 -7
  79. data/app/views/trestle/shared/_title.html.erb +14 -0
  80. data/app/views/trestle/table/_table.html.erb +4 -6
  81. data/bower.json +1 -1
  82. data/config/locales/cs.rb +18 -0
  83. data/config/locales/cs.yml +95 -0
  84. data/config/locales/en.yml +65 -36
  85. data/config/locales/es-MX.yml +94 -0
  86. data/config/locales/es.yml +94 -0
  87. data/config/locales/fr.rb +18 -0
  88. data/config/locales/fr.yml +84 -0
  89. data/config/locales/lv.rb +18 -0
  90. data/config/locales/lv.yml +94 -0
  91. data/config/locales/nl.rb +18 -0
  92. data/config/locales/nl.yml +82 -0
  93. data/config/locales/pl.rb +18 -0
  94. data/config/locales/pl.yml +85 -0
  95. data/config/locales/pt-BR.rb +18 -0
  96. data/config/locales/pt-BR.yml +84 -0
  97. data/config/locales/zh-CN.rb +18 -0
  98. data/config/locales/zh-CN.yml +94 -0
  99. data/gemfiles/rails-4.2.gemfile +16 -0
  100. data/gemfiles/rails-5.0.gemfile +16 -0
  101. data/gemfiles/rails-5.1.gemfile +16 -0
  102. data/gemfiles/rails-5.2.gemfile +16 -0
  103. data/gemfiles/rails-edge.gemfile +17 -0
  104. data/lib/generators/trestle/install/templates/trestle.rb.erb +18 -2
  105. data/lib/generators/trestle/resource/templates/admin.rb.erb +2 -2
  106. data/lib/trestle/adapters/active_record_adapter.rb +2 -6
  107. data/lib/trestle/adapters/adapter.rb +40 -15
  108. data/lib/trestle/adapters/sequel_adapter.rb +2 -6
  109. data/lib/trestle/admin/builder.rb +42 -12
  110. data/lib/trestle/admin/controller.rb +3 -1
  111. data/lib/trestle/admin.rb +84 -3
  112. data/lib/trestle/breadcrumb.rb +16 -1
  113. data/lib/trestle/configurable.rb +6 -0
  114. data/lib/trestle/configuration.rb +28 -5
  115. data/lib/trestle/display.rb +1 -1
  116. data/lib/trestle/engine.rb +10 -4
  117. data/lib/trestle/form/automatic.rb +6 -3
  118. data/lib/trestle/form/builder.rb +5 -1
  119. data/lib/trestle/form/field.rb +1 -1
  120. data/lib/trestle/form/fields/date_picker.rb +2 -2
  121. data/lib/trestle/form/fields/form_control.rb +14 -4
  122. data/lib/trestle/form/fields/form_group.rb +14 -3
  123. data/lib/trestle/form/fields/select.rb +5 -1
  124. data/lib/trestle/form/fields/tag_select.rb +1 -1
  125. data/lib/trestle/form/renderer.rb +2 -2
  126. data/lib/trestle/form.rb +7 -3
  127. data/lib/trestle/hook.rb +27 -0
  128. data/lib/trestle/model_name.rb +64 -0
  129. data/lib/trestle/navigation/group.rb +16 -0
  130. data/lib/trestle/navigation/item.rb +11 -1
  131. data/lib/trestle/navigation.rb +24 -6
  132. data/lib/trestle/options.rb +7 -5
  133. data/lib/trestle/reloader.rb +1 -1
  134. data/lib/trestle/resource/adapter_methods.rb +62 -0
  135. data/lib/trestle/resource/builder.rb +43 -17
  136. data/lib/trestle/resource/collection.rb +68 -0
  137. data/lib/trestle/resource/controller.rb +70 -33
  138. data/lib/trestle/resource.rb +97 -105
  139. data/lib/trestle/scope.rb +13 -3
  140. data/lib/trestle/tab.rb +4 -0
  141. data/lib/trestle/table/actions_column.rb +41 -31
  142. data/lib/trestle/table/automatic.rb +1 -2
  143. data/lib/trestle/table/builder.rb +2 -2
  144. data/lib/trestle/table/column.rb +40 -9
  145. data/lib/trestle/table/row.rb +18 -2
  146. data/lib/trestle/table.rb +18 -2
  147. data/lib/trestle/toolbar/builder.rb +52 -0
  148. data/lib/trestle/toolbar/context.rb +39 -0
  149. data/lib/trestle/toolbar.rb +43 -0
  150. data/lib/trestle/version.rb +1 -1
  151. data/lib/trestle.rb +9 -4
  152. data/trestle.gemspec +5 -3
  153. data/vendor/assets/bower_components/trestle/select2/dist/js/select2.full.js +90 -69
  154. data/vendor/assets/javascripts/trestle/flatpickr.js.erb +2 -0
  155. data/vendor/assets/stylesheets/trestle/magnific-popup.scss +13 -1
  156. metadata +99 -22
  157. data/app/assets/javascripts/trestle/_confirmation.js +0 -23
  158. data/app/assets/javascripts/trestle/_datepicker.js +0 -22
  159. data/app/assets/javascripts/trestle/_form.js +0 -6
  160. data/app/assets/javascripts/trestle/_sidebar.js +0 -52
  161. data/app/assets/javascripts/trestle/_tabs.js +0 -13
  162. data/app/assets/javascripts/trestle/_tooltips.js +0 -3
  163. data/app/helpers/trestle/title_helper.rb +0 -26
  164. data/app/views/trestle/application/_flash.html.erb +0 -11
@@ -1,34 +1,10 @@
1
1
  class Trestle::ApplicationController < ActionController::Base
2
2
  protect_from_forgery
3
3
 
4
- layout 'trestle/admin'
5
-
6
- # Global helpers
7
- self.helpers_path += Rails.application.helpers_paths
8
- helper Trestle.config.helper_module
9
- helper *Trestle.config.helpers
10
-
11
- # Global callbacks
12
- Trestle.config.before_actions.each do |action|
13
- before_action(action.options, &action.block)
14
- end
15
-
16
- Trestle.config.after_actions.each do |action|
17
- after_action(action.options, &action.block)
18
- end
19
-
20
- Trestle.config.around_actions.each do |action|
21
- around_action(action.options, &action.block)
22
- end
23
-
24
- protected
25
- def breadcrumbs
26
- @breadcrumbs ||= Trestle::Breadcrumb::Trail.new(Trestle.config.root_breadcrumbs)
27
- end
28
- helper_method :breadcrumbs
29
-
30
- def breadcrumb(label, path=nil)
31
- breadcrumbs.append(label, path)
32
- end
33
- helper_method :breadcrumb
4
+ include Trestle::Controller::Breadcrumbs
5
+ include Trestle::Controller::Callbacks
6
+ include Trestle::Controller::Dialog
7
+ include Trestle::Controller::Helpers
8
+ include Trestle::Controller::Layout
9
+ include Trestle::Controller::Location
34
10
  end
@@ -1,10 +1,17 @@
1
1
  module Trestle
2
2
  module AvatarHelper
3
- def avatar(&block)
4
- content_tag(:div, class: "avatar", &block)
3
+ def avatar(options={})
4
+ fallback = options.delete(:fallback) if options[:fallback]
5
+
6
+ content_tag(:div, options.reverse_merge(class: "avatar")) do
7
+ concat content_tag(:span, fallback, class: "avatar-fallback") if fallback
8
+ concat yield if block_given?
9
+ end
5
10
  end
6
11
 
7
12
  def gravatar(email, options={})
13
+ options = { d: "mm" }.merge(options)
14
+
8
15
  url = "https://www.gravatar.com/avatar/#{Digest::MD5.hexdigest(email.to_s.downcase)}.png"
9
16
  url << "?#{options.to_query}" if options.any?
10
17
 
@@ -0,0 +1,15 @@
1
+ module Trestle
2
+ module FlashHelper
3
+ def normalize_flash_alert(flash)
4
+ flash.is_a?(Hash) ? flash.with_indifferent_access : { message: flash }
5
+ end
6
+
7
+ def debug_form_errors?
8
+ Trestle.config.debug_form_errors && instance_has_errors?
9
+ end
10
+
11
+ def instance_has_errors?
12
+ instance.errors.any? rescue false
13
+ end
14
+ end
15
+ end
@@ -2,7 +2,10 @@ module Trestle
2
2
  module FormHelper
3
3
  def trestle_form_for(instance, options={}, &block)
4
4
  options[:builder] ||= Form::Builder
5
- options[:as] ||= admin.admin_name.singularize
5
+ options[:as] ||= admin.parameter_name
6
+
7
+ options[:data] ||= {}
8
+ options[:data].reverse_merge!(remote: true, type: :html, behavior: "trestle-form", turbolinks: false)
6
9
 
7
10
  form_for(instance, options) do |f|
8
11
  with_form(f) { yield f }
@@ -20,12 +23,12 @@ module Trestle
20
23
  @_trestle_form
21
24
  end
22
25
 
23
- def toolbar(name, &block)
24
- content_for(:"#{name}_toolbar", &block)
25
- end
26
-
27
26
  def sidebar(&block)
28
27
  content_for(:sidebar, &block)
29
28
  end
29
+
30
+ def render_sidebar_as_tab?
31
+ dialog_request? && content_for?(:sidebar)
32
+ end
30
33
  end
31
34
  end
@@ -13,7 +13,7 @@ module Trestle
13
13
  when :currency
14
14
  number_to_currency(value)
15
15
  when :tags
16
- safe_join(value.map { |tag| content_tag(:span, tag, class: "tag") })
16
+ safe_join(Array(value).map { |tag| content_tag(:span, tag, class: "tag") })
17
17
  else
18
18
  value
19
19
  end
@@ -30,8 +30,12 @@ module Trestle
30
30
  when TrueClass, FalseClass
31
31
  status_tag(icon("fa fa-check"), :success) if value
32
32
  when NilClass
33
- text = options.key?(:blank) ? options[:blank] : I18n.t("admin.format.blank")
34
- content_tag(:span, text, class: "blank")
33
+ blank = options.key?(:blank) ? options[:blank] : I18n.t("admin.format.blank")
34
+ if blank.respond_to?(:call)
35
+ instance_exec(&blank)
36
+ else
37
+ content_tag(:span, blank, class: "blank")
38
+ end
35
39
  when String
36
40
  if value.html_safe? || options[:truncate] == false
37
41
  value
@@ -0,0 +1,27 @@
1
+ module Trestle
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
26
+ end
27
+ end
@@ -1,13 +1,22 @@
1
1
  module Trestle
2
2
  module HookHelper
3
3
  def hook(name)
4
- safe_join(Trestle.config.hooks[name.to_s].map { |hook|
5
- instance_exec(&hook)
6
- }, "\n")
4
+ if hook?(name)
5
+ safe_join(hooks(name).map { |hook|
6
+ hook.evaluate(self)
7
+ }, "\n")
8
+ elsif block_given?
9
+ yield
10
+ end
7
11
  end
8
12
 
9
13
  def hook?(name)
10
- Trestle.config.hooks[name.to_s].any?
14
+ Trestle.config.hooks.key?(name.to_s) && hooks(name).any?
15
+ end
16
+
17
+ protected
18
+ def hooks(name)
19
+ Trestle.config.hooks[name.to_s].select { |h| h.visible?(self) }
11
20
  end
12
21
  end
13
22
  end
@@ -0,0 +1,14 @@
1
+ module Trestle
2
+ module I18nHelper
3
+ def i18n_fallbacks(locale=I18n.locale)
4
+ if I18n.respond_to?(:fallbacks)
5
+ I18n.fallbacks[locale]
6
+ elsif locale.to_s.include?("-")
7
+ fallback = locale.to_s.split("-").first
8
+ [locale, fallback]
9
+ else
10
+ [locale]
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,24 @@
1
+ module Trestle
2
+ module PanelHelper
3
+ def panel(options={}, &block)
4
+ html_class = options.fetch(:class) { "panel-default" }
5
+
6
+ content_tag(:div, class: ["panel", html_class]) do
7
+ if options[:title]
8
+ concat content_tag(:div, options[:title], class: "panel-heading")
9
+ end
10
+
11
+ concat content_tag(:div, class: "panel-body", &block)
12
+
13
+ if options[:footer]
14
+ concat content_tag(:div, options[:footer], class: "panel-footer")
15
+ end
16
+ end
17
+ end
18
+
19
+ def well(options={}, &block)
20
+ html_class = ["well", options[:class]].compact
21
+ content_tag(:div, options.merge(class: html_class), &block)
22
+ end
23
+ end
24
+ end
@@ -5,9 +5,9 @@ module Trestle
5
5
  end
6
6
 
7
7
  def tab(name, options={})
8
- tabs[name] = Tab.new(name, options)
8
+ tabs[name] = tab = Tab.new(name, options)
9
9
 
10
- content_tag(:div, id: "tab-#{name}", class: ["tab-pane", ('active' if name == tabs.keys.first)], role: "tabpanel") do
10
+ content_tag(:div, id: tab.id(("modal" if dialog_request?)), class: ["tab-pane", ('active' if name == tabs.keys.first)], role: "tabpanel") do
11
11
  if block_given?
12
12
  yield
13
13
  elsif options[:partial]
@@ -1,7 +1,46 @@
1
1
  module Trestle
2
2
  module TableHelper
3
- def table(collection, options={}, &block)
4
- table = Table::Builder.build(options, &block)
3
+ # Renders an existing named table or builds and renders a custom table if a block is provided.
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.
14
+ #
15
+ # Examples
16
+ #
17
+ # <%= table collection: -> { Account.all }, admin: :accounts do %>
18
+ # <% column(:name) %>
19
+ # <% column(:balance) { |account| account.balance.format } %>
20
+ # <% column(:created_at, align: :center)
21
+ # <% end %>
22
+ #
23
+ # <%= table :accounts %>
24
+ #
25
+ # Returns the HTML representation of the table as a HTML-safe String.
26
+ def table(name=nil, options={}, &block)
27
+ 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)
35
+ elsif name.is_a?(Trestle::Table)
36
+ table = name
37
+ else
38
+ table = admin.tables.fetch(name) { raise ArgumentError, "Unable to find table named #{name.inspect}" }
39
+ end
40
+
41
+ collection ||= options[:collection] || table.options[:collection]
42
+ collection = collection.call if collection.respond_to?(:call)
43
+
5
44
  render "trestle/table/table", table: table, collection: collection
6
45
  end
7
46
  end
@@ -1,17 +1,59 @@
1
1
  module Trestle
2
2
  module TimestampHelper
3
- def timestamp(time)
4
- time_tag(time, class: "timestamp") do
3
+ # Renders a Time object as a formatted timestamp (using a <time> tag)
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).
11
+ #
12
+ # Examples
13
+ #
14
+ # <%= timestamp(article.created_at) %>
15
+ #
16
+ # <%= timestamp(Time.current, class: "timestamp-inline", precision: :seconds) %>
17
+ #
18
+ # Returns the HTML representation of the given Time.
19
+ def timestamp(time, options={})
20
+ return unless time
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 }
26
+
27
+ time_tag(time, class: classes) do
5
28
  safe_join([
6
- l(time, format: :trestle_date, default: proc { |date| "#{date.day.ordinalize} %b %Y" }),
7
- content_tag(:small, l(time, format: :trestle_time_with_seconds, default: "%l:%M:%S %p"))
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"))
8
31
  ], "\n")
9
32
  end
10
33
  end
11
34
 
12
- def datestamp(date)
13
- time_tag(date, class: "datestamp") do
14
- l(date, format: :trestle_calendar, default: "%-m/%-d/%Y")
35
+ # Renders a Date object as formatted datestamp (using a <time> tag)
36
+ #
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).
41
+ #
42
+ # Examples
43
+ #
44
+ # <%= datestamp(Date.current) %>
45
+ #
46
+ # <%= datestamp(article.created_at, format: :trestle_date, class: "custom-datestamp") %>
47
+ #
48
+ # Returns the HTML representation of the given Date.
49
+ def datestamp(date, options={})
50
+ return unless date
51
+
52
+ classes = ["datestamp", options[:class]].compact
53
+ format = options.fetch(:format) { :trestle_calendar}
54
+
55
+ time_tag(date, class: classes) do
56
+ l(date, format: format, default: "%-m/%-d/%Y")
15
57
  end
16
58
  end
17
59
  end
@@ -0,0 +1,34 @@
1
+ module Trestle
2
+ module ToolbarsHelper
3
+ def toolbar(name, &block)
4
+ toolbar = (toolbars[name.to_s] ||= Toolbar.new)
5
+ toolbar.prepend(&block) if block_given?
6
+ toolbar
7
+ end
8
+
9
+ def toolbars
10
+ @_toolbars ||= {}
11
+ end
12
+
13
+ def render_toolbar(toolbar, *args)
14
+ result = toolbar.groups(self, *args).map do |items|
15
+ if items.many?
16
+ content_tag(:div, class: "btn-group", role: "group") do
17
+ safe_join(items, "\n")
18
+ end
19
+ else
20
+ items.first
21
+ end
22
+ end
23
+
24
+ safe_join(result, "\n")
25
+ end
26
+
27
+ def deprecated_toolbar(name)
28
+ if content_for?(:"#{name}_toolbar")
29
+ ActiveSupport::Deprecation.warn("Using content_for(:#{name}_toolbar) is deprecated. Please use toolbar(:#{name}) instead.")
30
+ content_for(:"#{name}_toolbar")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,25 +1,89 @@
1
1
  module Trestle
2
2
  module UrlHelper
3
- def admin_link_to(content, instance=nil, options={}, &block)
3
+ DIALOG_ACTIONS = [:new, :show, :edit]
4
+
5
+ def admin_link_to(content, instance_or_url=nil, options={}, &block)
6
+ # Block given - ignore content parameter and capture content from block
4
7
  if block_given?
5
- instance, options = content, instance || {}
8
+ instance_or_url, options = content, instance_or_url || {}
6
9
  content = capture(&block)
7
10
  end
8
11
 
9
- if admin = (options.key?(:admin) ? Trestle.lookup(options.delete(:admin)) : admin_for(instance))
10
- link_to(content, admin_url_for(instance, admin: admin), options)
12
+ if instance_or_url.is_a?(String)
13
+ # Treat string URL as regular link
14
+ link_to(content, instance_or_url, options)
11
15
  else
12
- content
16
+ # Normalize options if instance is not provided
17
+ if instance_or_url.is_a?(Hash)
18
+ instance, options = nil, instance_or_url
19
+ else
20
+ instance = instance_or_url
21
+ end
22
+
23
+ # Determine admin
24
+ if options.key?(:admin)
25
+ admin = Trestle.lookup(options.delete(:admin))
26
+ elsif instance
27
+ admin = admin_for(instance)
28
+ end
29
+
30
+ admin ||= self.admin if respond_to?(:admin)
31
+
32
+ if admin
33
+ # Ensure admin has controller context
34
+ admin = admin.new(self) if admin.is_a?(Class)
35
+
36
+ # Generate path
37
+ action = options.delete(:action) || :show
38
+ params = options.delete(:params) || {}
39
+
40
+ if admin.respond_to?(:instance_path) && instance
41
+ path = admin.instance_path(instance, params.reverse_merge(action: action))
42
+ else
43
+ params[:id] ||= admin.to_param(instance) if instance
44
+ path = admin.path(action, params)
45
+ end
46
+
47
+ # Determine link data options
48
+ if DIALOG_ACTIONS.include?(action) && admin.form.dialog?
49
+ options[:data] ||= {}
50
+ options[:data][:behavior] ||= "dialog"
51
+ end
52
+
53
+ link_to(content, path, options)
54
+ else
55
+ raise ActionController::UrlGenerationError, "An admin could not be inferred. Please specify an admin using the :admin option."
56
+ end
13
57
  end
14
58
  end
15
59
 
16
60
  def admin_url_for(instance, options={})
17
- admin = options.key?(:admin) ? Trestle.lookup(options[:admin]) : admin_for(instance)
18
- admin.path(options[:action] || :show, id: admin.to_param(instance)) if admin
61
+ admin = Trestle.lookup(options.delete(:admin)) if options.key?(:admin)
62
+ admin ||= admin_for(instance)
63
+ return unless admin
64
+
65
+ # Ensure admin has controller context
66
+ admin = admin.new(self) if admin.is_a?(Class)
67
+
68
+ if admin.respond_to?(:instance_path)
69
+ admin.instance_path(instance, options)
70
+ else
71
+ admin.path(options[:action] || :show, id: admin.to_param(instance))
72
+ end
19
73
  end
20
74
 
21
75
  def admin_for(instance)
22
- Trestle.admins[instance.class.name.underscore.pluralize]
76
+ klass = instance.class
77
+
78
+ while klass
79
+ admin = Trestle.admins[klass.name.underscore.pluralize]
80
+ return admin if admin
81
+
82
+ klass = klass.superclass
83
+ end
84
+
85
+ # No admin found
86
+ nil
23
87
  end
24
88
  end
25
89
  end
@@ -26,9 +26,7 @@
26
26
  Trestle.i18n['<%= key %>'] = "<%= escape_javascript(t(key, default: t(key, locale: :en))) %>";
27
27
  <% end %>
28
28
 
29
- if (flatpickr.l10ns['<%= I18n.locale %>']) {
30
- flatpickr.localize(flatpickr.l10ns['<%= I18n.locale %>']);
31
- }
29
+ Trestle.localize(<%= i18n_fallbacks.map { |l| "'#{l}'" }.join(", ").html_safe %>);
32
30
  </script>
33
31
 
34
32
  <%= hook :javascripts %>
@@ -38,14 +36,14 @@
38
36
  <%= hook :head %>
39
37
  </head>
40
38
 
41
- <body>
39
+ <body<%=raw " class=\"sidebar-#{cookies["trestle:sidebar"]}\"" if cookies["trestle:sidebar"] %>>
42
40
  <div class="app-wrapper">
43
41
  <%= render "trestle/shared/sidebar" %>
44
42
 
45
43
  <div class="app-container">
46
44
  <%= render "trestle/shared/header" %>
47
45
 
48
- <main class="app-main">
46
+ <main class="app-main" data-context="<%= request.fullpath %>">
49
47
  <%= yield %>
50
48
  </main>
51
49
 
@@ -0,0 +1,34 @@
1
+ <div class="modal-header">
2
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
3
+ <h4 class="modal-title"><%= content_for(:title) %></h4>
4
+ </div>
5
+
6
+ <div class="modal-flash">
7
+ <%= render "trestle/flash/flash" %>
8
+ </div>
9
+
10
+ <div class="modal-tabs">
11
+ <%= render "tabs" %>
12
+ </div>
13
+
14
+ <div class="modal-body">
15
+ <%= yield %>
16
+
17
+ <% if content_for?(:sidebar) %>
18
+ <div id="tab-sidebar" class="tab-pane" role="tabpanel">
19
+ <%= content_for(:sidebar) %>
20
+ </div>
21
+ <% end %>
22
+ </div>
23
+
24
+ <div class="modal-footer">
25
+ <div class="btn-toolbar secondary-toolbar">
26
+ <%= render_toolbar(toolbar(:secondary)) %>
27
+ <%= deprecated_toolbar(:secondary) %>
28
+ </div>
29
+
30
+ <div class="btn-toolbar primary-toolbar">
31
+ <%= render_toolbar(toolbar(:primary)) %>
32
+ <%= deprecated_toolbar(:primary) %>
33
+ </div>
34
+ </div>
@@ -1,27 +1,29 @@
1
1
  <header class="content-header">
2
- <% if content_for?(:primary_toolbar) %>
2
+ <div class="content-header-title">
3
+ <h1>
4
+ <%= content_for(:title) %>
5
+ </h1>
6
+
7
+ <% unless local_assigns.fetch(:hide_breadcrumbs, false) %>
8
+ <ol class="breadcrumb">
9
+ <% breadcrumbs.each do |breadcrumb| %>
10
+ <li class="breadcrumb-item<% if breadcrumb == breadcrumbs.last %> active<% end %>">
11
+ <%= link_to breadcrumb.label, breadcrumb.path %>
12
+ </li>
13
+ <% end %>
14
+ </ol>
15
+ <% end %>
16
+ </div>
17
+
18
+ <div class="content-header-toolbars">
3
19
  <div class="btn-toolbar primary-toolbar">
4
- <%= content_for(:primary_toolbar) %>
20
+ <%= render_toolbar(toolbar(:primary)) %>
21
+ <%= deprecated_toolbar(:primary) %>
5
22
  </div>
6
- <% end %>
7
23
 
8
- <% if content_for?(:secondary_toolbar) %>
9
24
  <div class="btn-toolbar secondary-toolbar">
10
- <%= content_for(:secondary_toolbar) %>
25
+ <%= render_toolbar(toolbar(:secondary)) %>
26
+ <%= deprecated_toolbar(:secondary) %>
11
27
  </div>
12
- <% end %>
13
-
14
- <h1>
15
- <%= content_for(:title) %>
16
- </h1>
17
-
18
- <% unless local_assigns.fetch(:hide_breadcrumbs, false) %>
19
- <ol class="breadcrumb">
20
- <% breadcrumbs.each do |breadcrumb| %>
21
- <li class="breadcrumb-item<% if breadcrumb == breadcrumbs.last %> active<% end %>">
22
- <%= link_to breadcrumb.label, breadcrumb.path %>
23
- </li>
24
- <% end %>
25
- </ol>
26
- <% end %>
28
+ </div>
27
29
  </header>
@@ -1,7 +1,7 @@
1
1
  <%= render "header", hide_breadcrumbs: local_assigns.fetch(:hide_breadcrumbs, false) if local_assigns.fetch(:header, true) %>
2
2
 
3
3
  <div class="main-content-area">
4
- <%= render "flash" %>
4
+ <%= render "trestle/flash/flash" %>
5
5
  <%= render "utilities" %>
6
6
  <%= render "tabs" %>
7
7
 
@@ -1,8 +1,14 @@
1
- <% if tabs.size > 1 -%>
1
+ <% if tabs.size > 1 || render_sidebar_as_tab? -%>
2
2
  <ul class="nav nav-tabs">
3
3
  <% tabs.each do |name, tab| %>
4
4
  <li<% if name == tabs.keys.first %> class="active"<% end %>>
5
- <%= link_to tab.label, "#tab-#{name}", role: "tab", data: { toggle: "tab" } %>
5
+ <%= link_to tab.label, "##{tab.id(("modal" if dialog_request?))}", role: "tab", data: { toggle: "tab" } %>
6
+ </li>
7
+ <% end %>
8
+
9
+ <% if render_sidebar_as_tab? %>
10
+ <li class="pull-right">
11
+ <%= link_to icon("fa fa-list-alt"), "#tab-sidebar", role: "tab", data: { toggle: "tab" } %>
6
12
  </li>
7
13
  <% end %>
8
14
  </ul>
@@ -4,7 +4,12 @@
4
4
  <%= icon %>
5
5
 
6
6
  <div class="alert-content">
7
- <h3><%= title %></h3>
8
- <p><%= message %></p>
7
+ <% if alert[:title] %>
8
+ <h3><%= alert[:title] %></h3>
9
+ <% end %>
10
+
11
+ <p><%= alert[:message] %></p>
12
+
13
+ <%= yield if block_given? %>
9
14
  </div>
10
15
  </div>
@@ -0,0 +1,8 @@
1
+ <%= link_to "Debug errors", "#debug-errors", class: "toggle-debug-errors small", data: { toggle: "collapse" } %>
2
+ <div id="debug-errors" class="debug-errors collapse">
3
+ <ul>
4
+ <% instance.errors.each do |key, message| %>
5
+ <li class="small"><tt><%= key %>:</tt> <%= message %></li>
6
+ <% end %>
7
+ </ul>
8
+ </div>
@@ -0,0 +1,7 @@
1
+ <% if flash[:message] -%>
2
+ <%= render "trestle/flash/alert", html_class: "alert-success", icon: icon("alert-icon ion-ios-checkmark-outline"), alert: normalize_flash_alert(flash[:message]) %>
3
+ <% elsif flash[:error] -%>
4
+ <%= render layout: "trestle/flash/alert", locals: { html_class: "alert-danger", icon: icon("alert-icon ion-ios-close-outline"), alert: normalize_flash_alert(flash[:error]) } do %>
5
+ <%= render "trestle/flash/debug" if debug_form_errors? %>
6
+ <% end %>
7
+ <% end -%>