cafe_car 0.1.0 → 0.1.2
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.
- checksums.yaml +4 -4
- data/README.md +682 -8
- data/Rakefile +21 -0
- data/app/assets/fonts/Lexend.css +7 -0
- data/app/assets/fonts/Lexend.ttf +0 -0
- data/app/assets/images/noise.svg +16 -0
- data/app/assets/stylesheets/actiontext.css +31 -0
- data/app/assets/stylesheets/application.css +1 -0
- data/app/assets/stylesheets/cafe_car/code/base16-dark.css +89 -0
- data/app/assets/stylesheets/cafe_car/code/base16-light.css +90 -0
- data/app/assets/stylesheets/cafe_car/pagination.css +5 -0
- data/app/assets/stylesheets/cafe_car/themes/cool.css +32 -0
- data/app/assets/stylesheets/cafe_car/themes/cool2.css +31 -0
- data/app/assets/stylesheets/cafe_car/themes/defaults.css +60 -0
- data/app/assets/stylesheets/cafe_car/themes/warm-dark.css +29 -0
- data/app/assets/stylesheets/cafe_car/themes/warm.css +24 -0
- data/app/assets/stylesheets/cafe_car/tooltips.css +20 -0
- data/app/assets/stylesheets/cafe_car/trix.css +56 -0
- data/app/assets/stylesheets/cafe_car/utility.css +63 -0
- data/app/assets/stylesheets/cafe_car.css +96 -0
- data/app/assets/stylesheets/iconoir.css +22 -0
- data/app/assets/stylesheets/ui/Alert.css +25 -0
- data/app/assets/stylesheets/ui/Article.css +11 -0
- data/app/assets/stylesheets/ui/Button.css +42 -0
- data/app/assets/stylesheets/ui/Card.css +74 -0
- data/app/assets/stylesheets/ui/Chat.css +33 -0
- data/app/assets/stylesheets/ui/Close.css +11 -0
- data/app/assets/stylesheets/ui/Code.css +4 -0
- data/app/assets/stylesheets/ui/Controls.css +16 -0
- data/app/assets/stylesheets/ui/Error.css +3 -0
- data/app/assets/stylesheets/ui/Example.css +45 -0
- data/app/assets/stylesheets/ui/Field.css +31 -0
- data/app/assets/stylesheets/ui/Grid.css +6 -0
- data/app/assets/stylesheets/ui/Group.css +16 -0
- data/app/assets/stylesheets/ui/Icon.css +27 -0
- data/app/assets/stylesheets/ui/Image.css +14 -0
- data/app/assets/stylesheets/ui/InfoCircle.css +11 -0
- data/app/assets/stylesheets/ui/Input.css +36 -0
- data/app/assets/stylesheets/ui/Layout.css +100 -0
- data/app/assets/stylesheets/ui/Menu.css +38 -0
- data/app/assets/stylesheets/ui/Modal.css +26 -0
- data/app/assets/stylesheets/ui/Navigation.css +37 -0
- data/app/assets/stylesheets/ui/Page.css +105 -0
- data/app/assets/stylesheets/ui/Row.css +9 -0
- data/app/assets/stylesheets/ui/Table.css +101 -0
- data/app/assets/stylesheets/ui/components.css +24 -0
- data/app/controllers/cafe_car/application_controller.rb +9 -0
- data/app/controllers/cafe_car/examples_controller.rb +22 -0
- data/app/controllers/cafe_car/sessions_controller.rb +30 -0
- data/app/controllers/concerns/cafe_car/authentication.rb +61 -0
- data/app/javascript/application.js +5 -0
- data/app/javascript/cafe_car.js +174 -0
- data/app/models/cafe_car/session.rb +18 -0
- data/app/policies/cafe_car/application_policy.rb +42 -0
- data/app/policies/cafe_car/session_policy.rb +19 -0
- data/app/presenters/cafe_car/action_text/rich_text_presenter.rb +7 -0
- data/app/presenters/cafe_car/active_record/base_presenter.rb +6 -0
- data/app/presenters/cafe_car/active_record/relation_presenter.rb +17 -0
- data/app/presenters/cafe_car/active_storage/attached/one_presenter.rb +9 -0
- data/app/presenters/cafe_car/active_storage/attachment_presenter.rb +18 -0
- data/app/presenters/cafe_car/basic_object_presenter.rb +5 -0
- data/app/presenters/cafe_car/code_presenter.rb +18 -0
- data/app/presenters/cafe_car/currency_presenter.rb +5 -0
- data/app/presenters/cafe_car/date_and_time/compatibility_presenter.rb +6 -0
- data/app/presenters/cafe_car/date_presenter.rb +5 -0
- data/app/presenters/cafe_car/date_time_presenter.rb +11 -0
- data/app/presenters/cafe_car/enumerable_presenter.rb +13 -0
- data/app/presenters/cafe_car/false_class_presenter.rb +5 -0
- data/app/presenters/cafe_car/hash_presenter.rb +6 -0
- data/app/presenters/cafe_car/nil_class_presenter.rb +13 -0
- data/app/presenters/cafe_car/presenter.rb +157 -0
- data/app/presenters/cafe_car/range_presenter.rb +16 -0
- data/app/presenters/cafe_car/record_presenter.rb +5 -0
- data/app/presenters/cafe_car/string_presenter.rb +20 -0
- data/app/presenters/cafe_car/symbol_presenter.rb +5 -0
- data/app/presenters/cafe_car/true_class_presenter.rb +5 -0
- data/app/ui/cafe_car/ui/button.rb +9 -0
- data/app/ui/cafe_car/ui/card.rb +18 -0
- data/app/ui/cafe_car/ui/field.rb +11 -0
- data/app/ui/cafe_car/ui/grid.rb +30 -0
- data/app/ui/cafe_car/ui/layout.rb +7 -0
- data/app/ui/cafe_car/ui/page.rb +14 -0
- data/app/views/application/_actions.html.haml +1 -0
- data/app/views/application/_alerts.html.haml +2 -0
- data/app/views/application/_body.html.haml +7 -0
- data/app/views/application/_controls.html.haml +12 -0
- data/app/views/application/_debug.html.haml +18 -0
- data/app/views/application/_empty.html.haml +1 -0
- data/app/views/application/_errors.html.haml +4 -0
- data/app/views/application/_field.html.haml +5 -0
- data/app/views/application/_fields.html.haml +1 -0
- data/app/views/application/_filters.html.haml +8 -0
- data/app/views/application/_form.html.haml +6 -0
- data/app/views/application/_grid.html.haml +3 -0
- data/app/views/application/_grid_item.html.haml +1 -0
- data/app/views/application/_head.html.haml +17 -0
- data/app/views/application/_index.html.haml +8 -0
- data/app/views/application/_index_actions.html.haml +7 -0
- data/app/views/application/_navigation.html.haml +9 -0
- data/app/views/application/_navigation_links.html.haml +5 -0
- data/app/views/application/_notes.html.haml +10 -0
- data/app/views/application/_popup.html.haml +7 -0
- data/app/views/application/_show.html.haml +9 -0
- data/app/views/application/_submit.html.haml +1 -0
- data/app/views/application/_table.html.haml +6 -0
- data/app/views/cafe_car/application/create.turbo_stream.haml +2 -0
- data/app/views/cafe_car/application/destroy.turbo_stream.haml +1 -0
- data/app/views/cafe_car/application/edit.html.haml +18 -0
- data/app/views/cafe_car/application/edit.turbo_stream.haml +7 -0
- data/app/views/cafe_car/application/index.html.haml +15 -0
- data/app/views/cafe_car/application/new.html.haml +8 -0
- data/app/views/cafe_car/application/new.turbo_stream.haml +8 -0
- data/app/views/cafe_car/application/show.html.haml +36 -0
- data/app/views/cafe_car/application/update.turbo_stream.haml +2 -0
- data/app/views/cafe_car/examples/_example.html.haml +12 -0
- data/app/views/cafe_car/examples/_index.html.haml +16 -0
- data/app/views/cafe_car/examples/_navigation_links.html.haml +2 -0
- data/app/views/cafe_car/examples/ui/_alert.html.haml +4 -0
- data/app/views/cafe_car/examples/ui/_button.html.haml +3 -0
- data/app/views/cafe_car/examples/ui/_card.html.haml +6 -0
- data/app/views/cafe_car/examples/ui/_chat.html.haml +3 -0
- data/app/views/cafe_car/examples/ui/_controls.html.haml +3 -0
- data/app/views/cafe_car/examples/ui/_error.html.haml +1 -0
- data/app/views/cafe_car/examples/ui/_field.html.haml +9 -0
- data/app/views/cafe_car/examples/ui/_grid.html.haml +11 -0
- data/app/views/cafe_car/examples/ui/_group.html.haml +21 -0
- data/app/views/cafe_car/examples/ui/_info_circle.html.haml +1 -0
- data/app/views/cafe_car/examples/ui/_menu.html.haml +5 -0
- data/app/views/cafe_car/examples/ui/_modal.html.haml +4 -0
- data/app/views/cafe_car/examples/ui/_navigation.html.haml +4 -0
- data/app/views/cafe_car/examples/ui/_page.html.haml +4 -0
- data/app/views/cafe_car/examples/ui/_table.html.haml +13 -0
- data/app/views/cafe_car/layouts/mailer.html.haml +8 -0
- data/app/views/cafe_car/layouts/mailer.text.erb +1 -0
- data/app/views/layouts/action_text/contents/_content.html.erb +3 -0
- data/app/views/layouts/application.html.haml +4 -0
- data/app/views/layouts/mailer.html.erb +13 -0
- data/app/views/layouts/mailer.text.erb +1 -0
- data/app/views/notes/_fields.html.haml +1 -0
- data/app/views/passwords_mailer/reset.html.haml +5 -0
- data/app/views/passwords_mailer/reset.text.erb +4 -0
- data/app/views/ui/_card.html.haml +8 -0
- data/app/views/ui/_field.html.haml +1 -0
- data/app/views/ui/_modal_close.html.haml +1 -0
- data/app/views/ui/_page.html.haml +7 -0
- data/config/brakeman.ignore +77 -0
- data/config/importmap.rb +12 -0
- data/config/locales/en.yml +63 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20251005220017_create_slugs.rb +13 -0
- data/lib/cafe_car/active_record.rb +21 -0
- data/lib/cafe_car/application_responder.rb +17 -0
- data/lib/cafe_car/attributes.rb +23 -0
- data/lib/cafe_car/auto_resolver.rb +49 -0
- data/lib/cafe_car/caching.rb +20 -0
- data/lib/cafe_car/component.rb +155 -0
- data/lib/cafe_car/context.rb +17 -0
- data/lib/cafe_car/controller/filtering.rb +30 -0
- data/lib/cafe_car/controller.rb +218 -0
- data/lib/cafe_car/core_ext/array.rb +24 -0
- data/lib/cafe_car/core_ext/hash.rb +15 -0
- data/lib/cafe_car/core_ext/module.rb +15 -0
- data/lib/cafe_car/core_ext.rb +5 -0
- data/lib/cafe_car/current.rb +9 -0
- data/lib/cafe_car/engine.rb +107 -0
- data/lib/cafe_car/field_builder.rb +44 -0
- data/lib/cafe_car/field_info.rb +144 -0
- data/lib/cafe_car/fields.rb +21 -0
- data/lib/cafe_car/filter/field_builder.rb +4 -0
- data/lib/cafe_car/filter/field_info.rb +22 -0
- data/lib/cafe_car/filter/form_builder.rb +21 -0
- data/lib/cafe_car/filter.rb +5 -0
- data/lib/cafe_car/filter_builder.rb +20 -0
- data/lib/cafe_car/form_builder.rb +105 -0
- data/lib/cafe_car/generators.rb +30 -0
- data/lib/cafe_car/helpers.rb +178 -0
- data/lib/cafe_car/href_builder.rb +97 -0
- data/lib/cafe_car/informable.rb +9 -0
- data/lib/cafe_car/input_builder.rb +25 -0
- data/lib/cafe_car/inputs/association_builder.rb +6 -0
- data/lib/cafe_car/inputs/base_input.rb +19 -0
- data/lib/cafe_car/inputs/belongs_to_builder.rb +6 -0
- data/lib/cafe_car/inputs/password_input.rb +7 -0
- data/lib/cafe_car/inputs/string_input.rb +7 -0
- data/lib/cafe_car/link_builder.rb +65 -0
- data/lib/cafe_car/model.rb +23 -0
- data/lib/cafe_car/model_info.rb +24 -0
- data/lib/cafe_car/name_patch.rb +17 -0
- data/lib/cafe_car/navigation.rb +76 -0
- data/lib/cafe_car/option_helpers.rb +53 -0
- data/lib/cafe_car/param_parser.rb +45 -0
- data/lib/cafe_car/pluralization.rb +15 -0
- data/lib/cafe_car/policy.rb +77 -0
- data/lib/cafe_car/proc_helpers.rb +13 -0
- data/lib/cafe_car/query_builder.rb +186 -0
- data/lib/cafe_car/queryable.rb +29 -0
- data/lib/cafe_car/resolver.rb +27 -0
- data/lib/cafe_car/routing.rb +17 -0
- data/lib/cafe_car/table/body_builder.rb +12 -0
- data/lib/cafe_car/table/builder.rb +52 -0
- data/lib/cafe_car/table/foot_builder.rb +14 -0
- data/lib/cafe_car/table/head_builder.rb +26 -0
- data/lib/cafe_car/table/label_builder.rb +48 -0
- data/lib/cafe_car/table/objects_builder.rb +8 -0
- data/lib/cafe_car/table/row_builder.rb +39 -0
- data/lib/cafe_car/table_builder.rb +13 -0
- data/lib/cafe_car/turbo_tag_builder.rb +7 -0
- data/lib/cafe_car/ui.rb +11 -0
- data/lib/cafe_car/version.rb +1 -1
- data/lib/cafe_car/visitors.rb +21 -0
- data/lib/cafe_car.rb +25 -168
- data/lib/generators/cafe_car/controller/USAGE +11 -0
- data/lib/generators/cafe_car/controller/controller_generator.rb +26 -0
- data/lib/generators/cafe_car/controller/templates/controller.rb.tt +5 -0
- data/lib/generators/cafe_car/install/USAGE +8 -0
- data/lib/generators/cafe_car/install/install_generator.rb +46 -0
- data/lib/generators/cafe_car/install/templates/application_policy.rb.tt +7 -0
- data/lib/generators/cafe_car/notes/USAGE +12 -0
- data/lib/generators/cafe_car/notes/notes_generator.rb +13 -0
- data/lib/generators/cafe_car/notes/templates/create_notes.rb.tt +12 -0
- data/lib/generators/cafe_car/notes/templates/notable.rb.tt +7 -0
- data/lib/generators/cafe_car/notes/templates/note.rb.tt +6 -0
- data/lib/generators/cafe_car/policy/USAGE +8 -0
- data/lib/generators/cafe_car/policy/policy_generator.rb +39 -0
- data/lib/generators/cafe_car/policy/templates/policy.rb.tt +20 -0
- data/lib/generators/cafe_car/resource/USAGE +13 -0
- data/lib/generators/cafe_car/resource/resource_generator.rb +32 -0
- data/lib/generators/cafe_car/sessions/USAGE +17 -0
- data/lib/generators/cafe_car/sessions/sessions_generator.rb +29 -0
- data/lib/generators/cafe_car/sessions/templates/create_sessions.rb.tt +12 -0
- data/lib/tasks/holdco_tasks.rake +532 -0
- data/lib/tasks/templates/tasks_header.md +37 -0
- metadata +444 -21
- data/app/views/cafe_car/application/_fields.html.erb +0 -7
- data/app/views/cafe_car/application/_filters.html.erb +0 -0
- data/app/views/cafe_car/application/_form.html.erb +0 -22
- data/lib/cafe_car/railtie.rb +0 -4
- /data/app/views/{cafe_car/application/_actions.html.erb → application/_aside.html.haml} +0 -0
- /data/app/views/{cafe_car/application/_aside.html.erb → application/_footer.html.haml} +0 -0
- /data/app/views/cafe_car/{application/_extra_fields.html.erb → examples/_index_actions.html.haml} +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
class CodePresenter < self[:Presenter]
|
|
3
|
+
def formatter = Rouge::Formatters::HTML.new
|
|
4
|
+
def source = object.to_s
|
|
5
|
+
def formatted = formatter.format(lexer.lex(source))
|
|
6
|
+
|
|
7
|
+
def options_lexer = options[:lang].try { Rouge::Lexer.find(_1) }
|
|
8
|
+
def guess_lexer = Rouge::Lexer.guess(source:)
|
|
9
|
+
|
|
10
|
+
def lexer
|
|
11
|
+
@lexer ||= options_lexer || guess_lexer
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_html
|
|
15
|
+
ui.Code(formatted.html_safe, class: [ "highlight", lexer.tag ])
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
class DateTimePresenter < Presenter
|
|
3
|
+
def distance = @template.time_ago_in_words(object)
|
|
4
|
+
def words = object.past? ? "#{distance} ago" : "in #{distance}"
|
|
5
|
+
def datetime = object.iso8601
|
|
6
|
+
def long = l(object, format: :long)
|
|
7
|
+
|
|
8
|
+
def string = words
|
|
9
|
+
def to_html = tag.time words, datetime:, "data-tip": long
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
class EnumerablePresenter < self[:Presenter]
|
|
3
|
+
def count = options[:count] || object.count
|
|
4
|
+
def with_count(list) = t(:list_html, list:, count:)
|
|
5
|
+
|
|
6
|
+
def to_html
|
|
7
|
+
object.map { present(_1) }
|
|
8
|
+
.then { options[:count] == object.count ? _1 : [ _1, "..." ] }
|
|
9
|
+
.then { safe_join(_1, ", ") }
|
|
10
|
+
.then { with_count _1 }
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
# Acts 1:3
|
|
3
|
+
class Presenter
|
|
4
|
+
include Caching
|
|
5
|
+
include OptionHelpers
|
|
6
|
+
|
|
7
|
+
delegate *%w[l t capture concat link link_to partial? get_partial href_for render safe_join raw tag ui], to: :@template
|
|
8
|
+
delegate :context, :context?, to: :@template
|
|
9
|
+
delegate :model_name, to: :model
|
|
10
|
+
|
|
11
|
+
attr_reader :object, :options, :block
|
|
12
|
+
class_attribute :show_defaults, default: Hash.new { _1[_2] = {} }
|
|
13
|
+
option :blank, presence: false
|
|
14
|
+
|
|
15
|
+
def self.inherited(subclass)
|
|
16
|
+
super
|
|
17
|
+
subclass.show_defaults = show_defaults.deep_dup
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.present(template, object, **options, &)
|
|
21
|
+
object = object.object if object.is_a?(Presenter)
|
|
22
|
+
find(options.fetch(:as) { object.class }).new(template, object, **options, &)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.find(klass)
|
|
26
|
+
candidates(klass).filter_map { CafeCar[_1] }.first or raise "Could not find presenter"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.candidates(klass)
|
|
30
|
+
names(klass).map { "#{_1}Presenter" }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.names(klass)
|
|
34
|
+
return [ klass.to_s.classify ] if klass.is_a?(Symbol)
|
|
35
|
+
klass.ancestors.lazy.map(&:name).compact
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.show(method, block = nil, **)
|
|
39
|
+
show_defaults[method].merge!({ block: }.compact, **)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(template, object, **options, &block)
|
|
43
|
+
@template = template
|
|
44
|
+
@object = object
|
|
45
|
+
@options = options
|
|
46
|
+
@block = options.delete(:block) { block }
|
|
47
|
+
@shown_attributes = {}
|
|
48
|
+
assign_options!
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def to_model = @object
|
|
52
|
+
def model = @object.is_a?(Class) ? @object : @object.class
|
|
53
|
+
def html_safe? = true
|
|
54
|
+
def to_s = to_html.to_s
|
|
55
|
+
def present(...) = @template.present(...)
|
|
56
|
+
def partial = @object.try(:to_partial_path)
|
|
57
|
+
def has_partial? = partial&.then { partial? _1 }
|
|
58
|
+
|
|
59
|
+
derive :policy, -> { @template.policy(@object) }
|
|
60
|
+
derive :captured, -> { block ? capture(object, &block) : object.to_s }
|
|
61
|
+
|
|
62
|
+
def to_html
|
|
63
|
+
return render(object:, partial:) if has_partial?
|
|
64
|
+
return blank if captured.blank? && blank
|
|
65
|
+
preview
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def title(*, **, &) = show(policy.title_attribute, *, blank: show(:id), **, &)
|
|
69
|
+
def logo(*, **, &) = show(policy.logo_attribute, *, **, &)
|
|
70
|
+
|
|
71
|
+
def timestamps(**, &)
|
|
72
|
+
attributes(*model.info.fields.timestamps.names, **, &)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def attributes(*methods, except: nil, **options, &block)
|
|
76
|
+
methods = policy.displayable_attributes if methods.empty?
|
|
77
|
+
methods = methods.flatten.compact
|
|
78
|
+
methods -= [ *except ]
|
|
79
|
+
capture do
|
|
80
|
+
methods.map do |method|
|
|
81
|
+
attribute(method, **options, &block)
|
|
82
|
+
end.each { concat(_1) }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def links = link(object)
|
|
87
|
+
|
|
88
|
+
# The object's canonical path, or nil when it isn't independently routable
|
|
89
|
+
# (e.g. a nested resource like a line item).
|
|
90
|
+
def href
|
|
91
|
+
href_for(object)
|
|
92
|
+
rescue NoMethodError, ActionController::UrlGenerationError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def preview(**, &)
|
|
97
|
+
ui.Row :space, href: do
|
|
98
|
+
concat logo(size: :icon)
|
|
99
|
+
concat title
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def attribute(method, **, &)
|
|
104
|
+
content = show(method, &)
|
|
105
|
+
return "" if content.blank?
|
|
106
|
+
|
|
107
|
+
ui.Field do |f|
|
|
108
|
+
concat f.Label(safe_join([ human(method), *info_circle(method) ], " "), tag: :strong)
|
|
109
|
+
concat f.Content(content)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def info(method) = model.info.field(method)
|
|
114
|
+
def human(method, **) = model.human_attribute_name(method, **)
|
|
115
|
+
|
|
116
|
+
def value(method)
|
|
117
|
+
model.inspection_filter.filter_param(method, object.try(method))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def show(method, **options, &)
|
|
121
|
+
return if method.nil?
|
|
122
|
+
@shown_attributes[method] = true
|
|
123
|
+
present(value(method), **show_defaults[method], **options, &)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def remaining_attributes(count = nil, **options, &block)
|
|
127
|
+
attrs = policy.displayable_attributes - @shown_attributes.keys
|
|
128
|
+
attrs = attrs.take(count) if count
|
|
129
|
+
attributes(*attrs, **options, &block)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def associations(*names, **options, &block)
|
|
133
|
+
names = policy.displayable_associations if names.blank?
|
|
134
|
+
attributes(*names, **options, &block)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def info_circle(method, *args, **opts, &block)
|
|
138
|
+
tip = info(method).hint
|
|
139
|
+
return unless tip
|
|
140
|
+
ui.InfoCircle(*args, tip:, **opts, &block)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def controls(**options, &block)
|
|
144
|
+
render("controls", object:, options:, &block)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def i18n_vars(names) = names.merge(*names.map { { _1.to_s.downcase.to_sym => _2.downcase } })
|
|
148
|
+
|
|
149
|
+
def i18n(action, scope: nil, **)
|
|
150
|
+
vars = i18n_vars Action: t(action, default: action.to_s.humanize),
|
|
151
|
+
Model: model_name.human,
|
|
152
|
+
Models: model_name.human(count: 2),
|
|
153
|
+
Object: link(@object).to_s.html_safe
|
|
154
|
+
@template.translate(action, scope:, default: :default, deep_interpolation: true, **vars, **)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
class RangePresenter < CafeCar[:Presenter]
|
|
3
|
+
def to_html
|
|
4
|
+
min, max = object.begin, object.end
|
|
5
|
+
if min.blank? && max.blank?
|
|
6
|
+
""
|
|
7
|
+
elsif max.blank? || max == Float::INFINITY
|
|
8
|
+
"#{min}+"
|
|
9
|
+
elsif min == max
|
|
10
|
+
min
|
|
11
|
+
else
|
|
12
|
+
"#{min} – #{max}"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
class StringPresenter < self[:Presenter]
|
|
3
|
+
def title = to_s
|
|
4
|
+
|
|
5
|
+
def to_s
|
|
6
|
+
length = @options[:truncate]
|
|
7
|
+
txt = object
|
|
8
|
+
txt = @template.truncate(txt, length:) if length
|
|
9
|
+
|
|
10
|
+
case object
|
|
11
|
+
when %r{^https?://.+\.(png|jpe?g|svg)$}
|
|
12
|
+
@template.image_tag object, style: "width: 1em"
|
|
13
|
+
when %r{^https?://}
|
|
14
|
+
link_to(txt, object, target: "_blank", rel: "noopener")
|
|
15
|
+
else
|
|
16
|
+
txt
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
module UI
|
|
3
|
+
component :Card do
|
|
4
|
+
flag :slim
|
|
5
|
+
option :title
|
|
6
|
+
option :subtitle
|
|
7
|
+
option :image
|
|
8
|
+
option :actions
|
|
9
|
+
option :tabs
|
|
10
|
+
|
|
11
|
+
component :Head, :Aside, :Body, :Foot
|
|
12
|
+
component :Title, tag: :h2
|
|
13
|
+
component :Subtitle, tag: :h3
|
|
14
|
+
component :Image, tag: :img
|
|
15
|
+
component :Actions
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module CafeCar
|
|
2
|
+
module UI
|
|
3
|
+
component :Grid do
|
|
4
|
+
option :columns
|
|
5
|
+
option :template
|
|
6
|
+
option :style
|
|
7
|
+
|
|
8
|
+
def attributes
|
|
9
|
+
super.merge(style:)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def style
|
|
13
|
+
style = [ *@style ]
|
|
14
|
+
style << "grid-template: #{template}" if template?
|
|
15
|
+
style << "grid-template-columns: #{columns}" if columns?
|
|
16
|
+
style.compact_blank.join("; ").presence
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def columns
|
|
20
|
+
case @columns
|
|
21
|
+
in Numeric
|
|
22
|
+
"repeat(#{@columns}, 1fr)"
|
|
23
|
+
in [ a, b ]
|
|
24
|
+
"repeat(auto-fill, minmax(#{a}, #{b}))"
|
|
25
|
+
else @columns
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= yield :actions
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
- index = options.delete(:index) { false }
|
|
2
|
+
- show = options.delete(:show) { false }
|
|
3
|
+
- edit = options.delete(:edit) { true }
|
|
4
|
+
- destroy = options.delete(:destroy) { true }
|
|
5
|
+
- links = link(object)
|
|
6
|
+
|
|
7
|
+
-# TODO: Handle resources with missing routes
|
|
8
|
+
= Controls tag: :span, **options do |c|
|
|
9
|
+
= c.Link links.index if index
|
|
10
|
+
= c.Link links.show if show
|
|
11
|
+
= c.Link links.edit if edit
|
|
12
|
+
= c.Link links.destroy if destroy
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
- console if Rails.env.development?
|
|
2
|
+
|
|
3
|
+
= Card title: "Debug" do |card|
|
|
4
|
+
= card.Section do
|
|
5
|
+
= Field label: "Params" do
|
|
6
|
+
= p parsed_params
|
|
7
|
+
|
|
8
|
+
= Field label: "Query" do
|
|
9
|
+
= p scope.to_sql, as: :code, lang: :sql
|
|
10
|
+
|
|
11
|
+
- if session = current_session
|
|
12
|
+
= Field label: "Session" do
|
|
13
|
+
= p session.as_json
|
|
14
|
+
|
|
15
|
+
= Field label: "Cookies" do
|
|
16
|
+
= p cookies.signed.as_json.to_s, as: :code
|
|
17
|
+
|
|
18
|
+
= yield :debug
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
No #{model_name.human(count: 0).downcase} yet.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= f.remaining_fields
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= Card title: object.title, image: object.logo(href: object), actions: object.controls
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
%title= yield :title
|
|
2
|
+
|
|
3
|
+
%meta{name: "viewport", content: "width=device-width,initial-scale=1"}
|
|
4
|
+
%meta{name: "apple-mobile-web-app-capable", content:"yes"}
|
|
5
|
+
%meta{name: "mobile-web-app-capable", content:"yes"}
|
|
6
|
+
%meta{name: "turbo-prefetch", content: "true"}
|
|
7
|
+
%meta{name: "turbo-refresh-method", content: "morph"}
|
|
8
|
+
%meta{name: "turbo-refresh-scroll", content: "preserve"}
|
|
9
|
+
%meta{name: "view-transition", content: "same-origin"}
|
|
10
|
+
%meta{name: "turbo-cache-control", content: "no-preview"}
|
|
11
|
+
= csrf_meta_tags
|
|
12
|
+
= csp_meta_tag
|
|
13
|
+
|
|
14
|
+
= yield :head
|
|
15
|
+
|
|
16
|
+
= stylesheet_link_tag "application", "data-turbo-track": "dynamic"
|
|
17
|
+
= javascript_importmap_tags
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
= Group do
|
|
2
|
+
= Button href: view_url(:grid), tip: "Grid View" do
|
|
3
|
+
= icon(:view_grid)
|
|
4
|
+
= Button href: view_url(:table), tip: "Table View" do
|
|
5
|
+
= icon(:table_rows)
|
|
6
|
+
|
|
7
|
+
= link(model.build).new(class: ui.Button(:primary).class_name, params: dot_params) { icon(:plus) + _1}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
= Navigation tag: :nav do |nav|
|
|
2
|
+
= render "navigation_links", nav:
|
|
3
|
+
|
|
4
|
+
- if try(:authenticated?)
|
|
5
|
+
= nav.Link(:bottom, href: try(:session_path)) { p(current_session) }
|
|
6
|
+
- elsif href = try(:new_session_path)
|
|
7
|
+
= nav.Link(:bottom, href:) do
|
|
8
|
+
= Icon t(:new_session, scope: "navigation.icon").to_sym
|
|
9
|
+
Sign In
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
- return unless object.respond_to? :notes
|
|
2
|
+
- href_for(:notes) rescue return
|
|
3
|
+
|
|
4
|
+
- ui(object.notes).each do |note|
|
|
5
|
+
= card.Section :block do
|
|
6
|
+
%small.shy.right= note.controls
|
|
7
|
+
= note.show :body
|
|
8
|
+
— #{note.show :author}
|
|
9
|
+
|
|
10
|
+
= render "form", object: object.notes.new, card:
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
= turbo_stream.append_all("body") do
|
|
2
|
+
= Modal :fixed, id: dom_id(object, :modal), class: "popup", data: {turbo_permanent: true} do |m|
|
|
3
|
+
= yield
|
|
4
|
+
|
|
5
|
+
-# = turbo_stream.after_all ui.Page.Body.selector do
|
|
6
|
+
-# = ui.Page.Aside class: "popup", id: dom_id(object, :popup), data: {turbo_permanent: true} do
|
|
7
|
+
-# = yield
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= f.submit data: { turbo_submits_with: "Saving..." }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= turbo_stream.refresh request_id: nil
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
- title = breadcrumbs(link(object).index, link(object), title("Edit #{model_name.human}"))
|
|
2
|
+
|
|
3
|
+
= Page :slim, title: do |page|
|
|
4
|
+
= page.Body do
|
|
5
|
+
= render "alerts"
|
|
6
|
+
= ui.Card do |card|
|
|
7
|
+
= render 'form', card:
|
|
8
|
+
= render "debug" if debug?
|
|
9
|
+
|
|
10
|
+
= page.Aside do
|
|
11
|
+
= Card :sticky do |card|
|
|
12
|
+
= card.Section ui(object).controls(edit: false)
|
|
13
|
+
- if object.persisted?
|
|
14
|
+
= card.Section do
|
|
15
|
+
= ui(object).attributes :updated_at, :created_at, try: true
|
|
16
|
+
|
|
17
|
+
= Card title: "Actions" do
|
|
18
|
+
= render('actions')
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
- title = title model_name.human(count: :all)
|
|
2
|
+
- actions = render "index_actions"
|
|
3
|
+
- body = render("index")
|
|
4
|
+
|
|
5
|
+
= Page title:, actions: do |page|
|
|
6
|
+
-# = page.Aside do
|
|
7
|
+
-# = render "filters"
|
|
8
|
+
|
|
9
|
+
= page.Body do
|
|
10
|
+
= render "alerts"
|
|
11
|
+
= body
|
|
12
|
+
= render "debug" if debug?
|
|
13
|
+
|
|
14
|
+
= page.Foot do
|
|
15
|
+
= yield :foot
|