card-mod-layout 0.11.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 88a1f8fda8a7de79162a23970453a1d65fa854e5756bf2440097b6f8e095105f
4
+ data.tar.gz: 74e33218406078b293233db999cecf6c05e0c978a22cf160b21073cb152e684d
5
+ SHA512:
6
+ metadata.gz: 0cfe4f2e146fb804b486a603eac29d7bf19443e16642e08c1b94d0d9531e7527e97159cfbd6a82f7e8ecb568d3e479d6fdedb93ef6f609b1ae4e3c1aca57009a
7
+ data.tar.gz: 98df05daff1504932be99d4e439d862c6f1fa7d87e3104968202edf39203bf846c443b47443ff19c1efce17897eb7653660729e4b336eeeacc5f684ab0445d23
@@ -0,0 +1,93 @@
1
+ class Card
2
+ class Layout
3
+ class << self
4
+ def render layout, format
5
+ layout_class(layout).new(layout, format).render
6
+ end
7
+
8
+ def layout_class layout
9
+ if layout.respond_to? :call
10
+ Card::Layout::ProcLayout
11
+ elsif card_layout? layout
12
+ Card::Layout::CardLayout
13
+ elsif code_layout? layout
14
+ Card::Layout::CodeLayout
15
+ else
16
+ Card::Layout::UnknownLayout
17
+ end
18
+ end
19
+
20
+ def card_layout? name
21
+ Card.fetch_type_id(name).in? [Card::LayoutTypeID, Card::HtmlID, Card::BasicID]
22
+ rescue ArgumentError, Card::Error::CodenameNotFound => _e
23
+ false
24
+ end
25
+
26
+ def code_layout? name
27
+ built_in_layouts_hash.key? name.to_sym
28
+ end
29
+
30
+ def register_layout new_layout
31
+ key = layout_key new_layout
32
+ return if layouts[key]
33
+
34
+ layouts[key] = block_given? ? yield : {}
35
+ end
36
+
37
+ def deregister_layout layout_name
38
+ layouts.delete layout_key(layout_name)
39
+ end
40
+
41
+ def layout_key name
42
+ return name if name.is_a? Symbol
43
+ name.to_name.key.to_sym
44
+ end
45
+
46
+ def register_built_in_layout new_layout, opts
47
+ register_layout(new_layout) { opts.present? ? opts : nil }
48
+ built_in_layouts_hash[new_layout] = true
49
+ end
50
+
51
+ def built_in_layouts_hash
52
+ @built_in_layouts ||= {}
53
+ end
54
+
55
+ def built_in_layouts
56
+ built_in_layouts_hash.keys
57
+ end
58
+
59
+ def layouts
60
+ @layouts ||= {}
61
+ end
62
+
63
+ def clear_cache
64
+ @built_in_layouts = @layouts = nil
65
+ end
66
+
67
+ def main_nest_opts layout_name, format
68
+ key = layout_key layout_name
69
+ opts = layouts[key] || register_layout_with_nest(layout_name, format)
70
+ opts.clone
71
+ end
72
+
73
+ def register_layout_with_nest layout_name, format
74
+ register_layout(layout_name) do
75
+ layout_class(layout_name).new(layout_name, format).fetch_main_nest_opts
76
+ end
77
+ end
78
+ end
79
+
80
+ def initialize layout, format
81
+ @layout = layout
82
+ @format = format
83
+ end
84
+
85
+ def fetch_main_nest_opts
86
+ {}
87
+ end
88
+
89
+ def main_nest_opts
90
+ self.class.main_nest_opts @layout, @format
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,42 @@
1
+ class Card
2
+ class Layout
3
+ # Layout based on a card's content
4
+ class CardLayout < Layout
5
+ def layout_card
6
+ @layout_card ||= Card.quick_fetch @layout
7
+ end
8
+
9
+ def render
10
+ @format.process_content layout_card.content, chunk_list: :references
11
+ end
12
+
13
+ def fetch_main_nest_opts
14
+ find_main_nest_chunk&.options ||
15
+ raise(Card::Error, "no main nest found in layout \"#{@layout}\"")
16
+ end
17
+
18
+ MAIN_NESTING_LIMIT = 5
19
+
20
+ def find_main_nest_chunk card=layout_card, depth=0
21
+ content = Card::Content.new(card.content, @format, chunk_list: :nest_only)
22
+ return unless content.each_chunk.count.positive?
23
+
24
+ main_chunk(content) || go_deeper(content, depth)
25
+ end
26
+
27
+ def go_deeper content, depth
28
+ return if depth > MAIN_NESTING_LIMIT
29
+
30
+ content.each_chunk do |chunk|
31
+ main_chunk = find_main_nest_chunk chunk.referee_card, depth + 1
32
+ return main_chunk if main_chunk
33
+ end
34
+ nil
35
+ end
36
+
37
+ def main_chunk content
38
+ content.each_chunk.find(&:main?)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,9 @@
1
+ class Card
2
+ class Layout
3
+ class CodeLayout < Layout
4
+ def render
5
+ @format.send Card::Set::Format.layout_method_name(@layout)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class Card
2
+ class Layout
3
+ class ProcLayout < Layout
4
+ def render
5
+ @layout.call
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,20 @@
1
+ class Card
2
+ class Layout
3
+ class UnknownLayout < Layout
4
+ SCOPE = "mod.core.format.html_format".freeze
5
+
6
+ def render
7
+ @format.output [header, text]
8
+ end
9
+
10
+ def header
11
+ @format.content_tag(:h1, @format.tr(:unknown_layout, scope: SCOPE, name: @layout))
12
+ end
13
+
14
+ def text
15
+ @format.tr(:available_layouts, scope: SCOPE,
16
+ available_layouts: self.class.built_in_layouts)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ format :html do
2
+ def link_to_mycard
3
+ link_to_card Auth.current.name, nil,
4
+ id: "my-card-link", class: "nav-link #{classy('my-card')}"
5
+ end
6
+
7
+ def account_dropdown &render_role_item
8
+ split_button link_to_mycard, nil do
9
+ [
10
+ link_to_card([Auth.current, :account_settings], "Account"),
11
+ (["Roles", role_items(&render_role_item)] if special_roles?)
12
+ ]
13
+ end
14
+ end
15
+
16
+ def special_roles?
17
+ Auth.current_roles.size > 1
18
+ end
19
+
20
+ def role_items
21
+ Auth.current_roles.map do |role_name|
22
+ yield role_name
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ format :html do
2
+ view :nav_item do
3
+ nav_dropdown
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ format :html do
2
+ # alert_types: 'success', 'info', 'warning', 'danger'
3
+ def alert alert_type, dismissable=false, disappear=false, args={}
4
+ add_class args, alert_classes(alert_type, dismissable, disappear)
5
+ wrap_with :div, args.merge(role: "alert") do
6
+ [(alert_close_button if dismissable), output(yield)]
7
+ end
8
+ end
9
+
10
+ def alert_classes alert_type, dismissable, disappear
11
+ classes = ["alert", "alert-#{alert_type}"]
12
+ classes << "alert-dismissible " if dismissable
13
+ classes << "_disappear" if disappear
14
+ classy classes
15
+ end
16
+
17
+ def alert_close_button
18
+ wrap_with :button, type: "button", "data-dismiss": "alert",
19
+ class: "close", "aria-label": "Close" do
20
+ wrap_with :span, "&times;", "aria-hidden" => true
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ format :html do
2
+ attr_reader :interior
3
+
4
+ def layout_nest
5
+ wrap_main { interior }
6
+ end
7
+
8
+ layout :pre do # {{_main|raw}}
9
+ wrap_with :pre do
10
+ layout_nest
11
+ end
12
+ end
13
+
14
+ layout :simple do
15
+ layout_nest
16
+ end
17
+
18
+ layout :no_side do # {{_main|open}}
19
+ <<-HTML.strip_heredoc
20
+ <header>#{nest :header, view: :core}</header>
21
+ <article>#{layout_nest}</article>
22
+ <footer>{nest :footer, view: :core}</footer>
23
+ HTML
24
+ end
25
+
26
+ layout :default do
27
+ <<-HTML.strip_heredoc
28
+ <header>#{nest :header, view: :core}</header>
29
+ <article>#{layout_nest}</article>
30
+ <aside>#{nest :sidebar, view: :core}</aside>
31
+ <footer>{nest :footer, view: :core}</footer>
32
+ HTML
33
+ end
34
+ end
@@ -0,0 +1,119 @@
1
+ format :html do
2
+ MODAL_SIZE = { small: "sm", medium: nil, large: "lg", full: "full" }.freeze
3
+ MODAL_CLOSE_OPTS = { "data-dismiss": "modal",
4
+ "data-cy": "close-modal" }.freeze
5
+
6
+ wrapper :modal do |opts={}|
7
+ haml :modal_dialog, body: interior,
8
+ classes: modal_dialog_classes(opts),
9
+ title: normalize_modal_option(:title, opts),
10
+ menu: normalize_modal_option(:menu, opts),
11
+ footer: normalize_modal_option(:footer, opts)
12
+ end
13
+
14
+ def normalize_modal_option key, opts
15
+ val = opts[key]
16
+ return render("modal_#{key}") unless val
17
+ cast_model_option val
18
+ end
19
+
20
+ def cast_model_option val
21
+ case val
22
+ when Symbol
23
+ cast_model_option_symbol val
24
+ when Proc
25
+ val.call(self)
26
+ else
27
+ val
28
+ end
29
+ end
30
+
31
+ def cast_model_option_symbol val
32
+ respond_to?(val) ? send(val) : val
33
+ end
34
+
35
+ view :modal, wrap: :modal do
36
+ ""
37
+ end
38
+
39
+ def show_in_modal_link link_text, body
40
+ link_to_view :modal, link_text, "data-modal-body": body, "data-slotter-mode": "modal"
41
+ end
42
+
43
+ def modal_close_button link_text="Close", opts={}
44
+ classes = opts.delete(:class)
45
+ button_opts = opts.merge(MODAL_CLOSE_OPTS)
46
+ add_class button_opts, classes if classes
47
+ button_tag link_text, button_opts
48
+ end
49
+
50
+ def modal_submit_button opts={}
51
+ add_class opts, "submit-button _close-modal"
52
+ submit_button opts
53
+ end
54
+
55
+ view :modal_menu, unknown: true, wrap: :modal_menu do
56
+ [close_modal_window, pop_out_modal_window]
57
+ end
58
+
59
+ wrapper :modal_menu, :div, class: "modal-menu ml-auto"
60
+
61
+ view :modal_title, unknown: true do
62
+ ""
63
+ end
64
+
65
+ view :modal_footer, unknown: true do
66
+ button_tag "Close",
67
+ class: "btn-xs _close-modal float-right",
68
+ "data-dismiss" => "modal"
69
+ end
70
+
71
+ view :modal_link do
72
+ modal_link _render_title, size: voo.size
73
+ end
74
+
75
+ def modal_link text=nil, opts={}
76
+ opts = modal_link_opts(opts)
77
+ opts[:path][:layout] ||= :modal
78
+ link_to text, opts
79
+ end
80
+
81
+ def modal_link_opts opts
82
+ add_class opts, "slotter"
83
+ opts.reverse_merge! path: {},
84
+ "data-slotter-mode": "modal",
85
+ "data-modal-class": modal_dialog_classes(opts),
86
+ remote: true
87
+ opts
88
+ end
89
+
90
+ def modal_dialog_classes opts
91
+ classes = [classy("modal-dialog")]
92
+ return classes unless opts.present?
93
+
94
+ add_modal_size_class classes, opts[:size]
95
+ classes << "modal-dialog-centered" if opts[:vertically_centered]
96
+ classes.join " "
97
+ end
98
+
99
+ def add_modal_size_class classes, size
100
+ size = normalize_modal_size_class size
101
+ return if size == :medium || size.blank?
102
+
103
+ classes << "modal-#{MODAL_SIZE[size]}"
104
+ end
105
+
106
+ def normalize_modal_size_class size
107
+ size.in?(MODAL_SIZE.keys) ? size : cast_model_option(size)
108
+ end
109
+
110
+ def close_modal_window
111
+ link_to icon_tag(:close), path: "",
112
+ class: "_close-modal close",
113
+ "data-dismiss": "modal"
114
+ end
115
+
116
+ def pop_out_modal_window
117
+ link_to icon_tag(:new_window), path: {}, class: "pop-out-modal close"
118
+ end
119
+ end
@@ -0,0 +1,12 @@
1
+ #modal-container.modal.fade._modal-slot{role: "dialog", "data-backdrop": "static"}
2
+ -# .modal-dialog
3
+ %div{class: classes}
4
+ .modal-content
5
+ .modal-header.clearfix
6
+ = title
7
+ = menu
8
+ .modal-body
9
+ = body
10
+ - if footer.present?
11
+ .modal-footer
12
+ = footer
@@ -0,0 +1,68 @@
1
+ format :html do
2
+ view :navbar_links, perms: :none do
3
+ wrap_with :ul, class: "navbar-nav" do
4
+ navbar_items
5
+ end
6
+ end
7
+
8
+ # Iterates over all nests and links and renders them as bootstrap navbar items.
9
+ # Items that are pointer cards become dropdowns
10
+ def navbar_items view: :nav_item, link_class: "nav-link"
11
+ process_content nil, chunk_list: :references do |chunk|
12
+ case chunk
13
+ when Card::Content::Chunk::Link
14
+ navbar_link_chunk chunk, view, link_class
15
+ when Card::Content::Chunk::Nest
16
+ navbar_nest_chunk chunk, view
17
+ else
18
+ chunk.process_chunk
19
+ end
20
+ end
21
+ end
22
+
23
+ # overridden in Abstact::Pointer to render dropdown
24
+ view :nav_item do
25
+ wrap_with_nav_item link_view(class: "nav-link")
26
+ end
27
+
28
+ def wrap_with_nav_item content
29
+ wrap_with(:li, content, class: "nav-item")
30
+ end
31
+
32
+ view :nav_link_in_dropdown do
33
+ link_to_card card, render_title, class: "dropdown-item"
34
+ end
35
+
36
+ def nav_dropdown
37
+ wrap_with(:li, class: "nav-item dropdown") do
38
+ [
39
+ dropdown_toggle_link,
40
+ dropdown_menu
41
+ ]
42
+ end
43
+ end
44
+
45
+ def dropdown_toggle_link
46
+ link_to(render_title, href: "#", class: "nav-link dropdown-toggle",
47
+ "data-toggle": "dropdown")
48
+ end
49
+
50
+ def dropdown_menu
51
+ wrap_with :div, dropdown_menu_items, class: "dropdown-menu"
52
+ end
53
+
54
+ def dropdown_menu_items
55
+ navbar_items view: :nav_link_in_dropdown, link_class: "dropdown-item"
56
+ end
57
+
58
+ private
59
+
60
+ def navbar_link_chunk chunk, view, link_class
61
+ link = chunk.render_link view: view, explicit_link_opts: { class: link_class }
62
+ chunk.explicit_link? && view == :nav_item ? wrap_with_nav_item(link) : link
63
+ end
64
+
65
+ def navbar_nest_chunk chunk, view
66
+ content_nest chunk.options.merge view: view
67
+ end
68
+ end
@@ -0,0 +1,104 @@
1
+ format :html do
2
+ OVERLAY_CLOSE_OPTS = { class: "_close-overlay btn-sm",
3
+ "data-dismiss": "overlay",
4
+ type: "button" }.freeze
5
+
6
+ wrapper :overlay do |opts|
7
+ class_up "card-slot", "_overlay d0-card-overlay bg-body"
8
+ @content_body = true
9
+ voo.hide! :menu
10
+ overlay_frame true, overlay_header(opts[:title]), opts[:slot] do
11
+ interior
12
+ end
13
+ end
14
+
15
+ view :overlay_header, unknown: true do
16
+ overlay_header
17
+ end
18
+
19
+ view :overlay_title do
20
+ _render_title
21
+ end
22
+
23
+ view :overlay_menu do
24
+ wrap_with :div, class: "btn-group btn-group-sm align-self-start ml-auto" do
25
+ [render_overlay_help_link, slotify_overlay_link, close_overlay_link]
26
+ end
27
+ end
28
+
29
+ view :overlay_help_link, cache: :never, unknown: true do
30
+ opts = help_popover_opts
31
+ add_open_guide_opts opts
32
+ overlay_menu_link "question-circle", opts
33
+ end
34
+
35
+ def add_open_guide_opts opts
36
+ return unless card.guide_card
37
+
38
+ slot_selector = ".bridge-sidebar > ._overlay-container-placeholder > .card-slot"
39
+ opts.merge! remote: true,
40
+ href: path(mark: card, view: :overlay_guide),
41
+ "data-slot-selector": slot_selector,
42
+ "data-slotter-mode": "overlay"
43
+ add_class opts, "slotter"
44
+ end
45
+
46
+ def slotify_overlay_link
47
+ overlay_menu_link "external-link-square", card: card
48
+ end
49
+
50
+ def close_overlay_link
51
+ overlay_menu_link :close, path: "#", "data-dismiss": "overlay"
52
+ end
53
+
54
+ def overlay_close_button link_text="Close", opts={}
55
+ classes = opts.delete(:class)
56
+ button_opts = opts.merge(OVERLAY_CLOSE_OPTS)
57
+ add_class button_opts, classes if classes
58
+ button_tag link_text, button_opts
59
+ end
60
+
61
+ def overlay_delete_button
62
+ opts = { no_success: true }.merge OVERLAY_CLOSE_OPTS
63
+ delete_button opts
64
+ end
65
+
66
+ def overlay_save_and_close_button
67
+ submit_button text: "Save and Close", class: "_close-on-success",
68
+ "data-cy": "submit-overlay"
69
+ end
70
+
71
+ def overlay_menu_link icon, args={}
72
+ add_class args, "border-light text-dark p-1 ml-1"
73
+ button_link fa_icon(icon, class: "fa-lg"), args.merge(btn_type: "outline-secondary")
74
+ end
75
+
76
+ def overlay_header title=nil
77
+ title ||= _render_overlay_title
78
+ class_up "d0-card-header", "bg-body"
79
+ class_up "d0-card-header-title", "d-flex"
80
+ header_wrap [title, _render_overlay_menu]
81
+ end
82
+
83
+ def overlay_frame slot=true, header=render_overlay_header, slot_opts=nil
84
+ slot_opts ||= {}
85
+ overlay_framer slot, header, slot_opts do
86
+ wrap_body { yield }
87
+ end
88
+ end
89
+
90
+ def haml_overlay_frame slot=true, header=render_overlay_header
91
+ overlay_framer slot, header, {} do
92
+ haml_wrap_body { yield }
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def overlay_framer slot, header, slot_opts
99
+ class_up "card-slot", "_overlay"
100
+ with_frame slot, header, slot_opts do
101
+ yield
102
+ end
103
+ end
104
+ end
File without changes
@@ -0,0 +1,77 @@
1
+ format :html do
2
+ # TODO: use CodeFile cards for these
3
+ # builtin layouts allow for rescue / testing
4
+ # HTML_LAYOUTS = Mod::Loader.load_layouts(:html).merge "none" => "{{_main}}"
5
+ # HAML_LAYOUTS = Mod::Loader.load_layouts(:haml)
6
+
7
+ def show_with_page_layout view, args
8
+ main!
9
+ args = main_render_args view, args
10
+ if explicit_modal_wrapper?(view) && page_layout.to_sym != :modal
11
+ render_outside_of_layout view, args
12
+ else
13
+ render_with_layout view, page_layout, args
14
+ end
15
+ end
16
+
17
+ def main_render_args view, args
18
+ args[:view] = view if view
19
+ args[:main] = true
20
+ args[:main_view] = true
21
+ args
22
+ end
23
+
24
+ def page_layout
25
+ params[:layout] || layout_name_from_rule || :default
26
+ end
27
+
28
+ def render_with_layout view, layout, args={}
29
+ view_opts = Layout.main_nest_opts(layout, self)
30
+ view ||= view_opts.delete(:view) || default_nest_view
31
+ view_opts[:home_view] = view
32
+ view_opts[:layout] = layout
33
+ render! view, view_opts.reverse_merge(args)
34
+ end
35
+
36
+ def render_outside_of_layout view, args
37
+ body = render_with_layout(nil, page_layout, {})
38
+ modal = render!(view, args)
39
+ if body.include?("</body>")
40
+ # a bit hacky
41
+ # the problem is that the body tag has to be in the layout
42
+ # so that you can add layout css classes like <body class="right-sidebar">
43
+ body.sub!("</body>", "#{modal}</body>")
44
+ else
45
+ body += modal
46
+ end
47
+ body
48
+ end
49
+
50
+ def show_layout?
51
+ !Env.ajax? || params[:layout]
52
+ end
53
+
54
+ def explicit_modal_wrapper? view
55
+ return unless view_setting(:wrap, view)
56
+
57
+ wrapper_names(view_setting(:wrap, view)).any? { |n| n == :modal || n == :bridge }
58
+ end
59
+
60
+ def wrapper_names wrappers
61
+ case wrappers
62
+ when Hash then wrappers.keys
63
+ when Array then wrapper_names_from_array wrappers
64
+ else [wrappers]
65
+ end
66
+ end
67
+
68
+ def layout_name_from_rule
69
+ card.rule_card(:layout)&.try :item_name
70
+ end
71
+
72
+ private
73
+
74
+ def wrapper_names_from_array wrapper_array
75
+ wrapper_array.map { |w| w.is_a?(Array) ? w.first : w }
76
+ end
77
+ end
@@ -0,0 +1,51 @@
1
+ format :html do
2
+ view :tabs do
3
+ construct_tabs "tabs"
4
+ end
5
+
6
+ def construct_tabs tab_type
7
+ tabs = { active: {}, paths: {} }
8
+ voo.items[:view] ||= :content
9
+ card.each_item_name_with_options(_render_raw) do |name, options|
10
+ construct_tab tabs, name, options
11
+ end
12
+ tabs tabs[:paths], tabs[:active][:name], tab_type: tab_type, load: :lazy do
13
+ tabs[:active][:content]
14
+ end
15
+ end
16
+
17
+ def construct_tab tabs, name, explicit_options
18
+ tab_options = item_view_options explicit_options
19
+ tabs[:paths][name] = {
20
+ title: nest(name, view: :title, title: tab_options[:title]),
21
+ path: nest_path(name, tab_options).html_safe
22
+ }
23
+ return unless tabs[:active].empty?
24
+ tabs[:active] = { name: name, content: nest(name, tab_options) }
25
+ end
26
+
27
+ # def tab_title title, name
28
+ # return name unless title
29
+ # name.to_name.title title, @context_names
30
+ # end
31
+
32
+ view :pills do
33
+ construct_tabs "pills"
34
+ end
35
+
36
+ view :tabs_static do
37
+ construct_static_tabs "tabs"
38
+ end
39
+
40
+ view :pills_static do
41
+ construct_static_tabs "pills"
42
+ end
43
+
44
+ def construct_static_tabs tab_type
45
+ tabs = {}
46
+ card.item_cards.each do |item|
47
+ tabs[item.name] = nest item, item_view_options(args)
48
+ end
49
+ tabs tabs, nil, tab_type: tab_type
50
+ end
51
+ end
@@ -0,0 +1,61 @@
1
+ include_set Abstract::AccountDropdown
2
+
3
+ def ok_to_read
4
+ true
5
+ end
6
+
7
+ def ok_to_update
8
+ left_id == Auth.current_id
9
+ end
10
+
11
+ def ok_to_create
12
+ left_id == Auth.current_id
13
+ end
14
+
15
+ def ensure_roles
16
+ self.content = Auth.current_roles.to_pointer_content if content.blank?
17
+ end
18
+
19
+ event :validate_role_enabling, :validate, on: :save do
20
+ illegal_roles = item_names - Auth.current_roles
21
+ return if illegal_roles.empty?
22
+
23
+ errors.add :content, "illegal roles: #{illegal_roles.to_sentence}" # LOCALIZE
24
+ end
25
+
26
+ event :clear_roles_cache, :prepare_to_store, before: :store_in_session do
27
+ clear_roles
28
+ Auth.update_always_cache Auth.as_id, nil
29
+ end
30
+
31
+ format :html do
32
+ # permission change compared to super
33
+ view :edit_inline, perms: :none, unknown: true, cache: :never, wrap: :slot do
34
+ super()
35
+ end
36
+
37
+ def input_type
38
+ :checkbox
39
+ end
40
+
41
+ def edit_success
42
+ { reload: true }
43
+ end
44
+
45
+ def hidden_form_tags _action, opts
46
+ "#{super} #{hidden_tags card: { type_id: SessionID }}"
47
+ end
48
+
49
+ def checkbox_input
50
+ card.ensure_roles
51
+ wrap_with :div, class: "pointer-checkbox-list" do
52
+ account_dropdown(&method(:role_item_checkbox))
53
+ end
54
+ end
55
+
56
+ def role_item_checkbox role_name
57
+ haml :role_checkbox, id: "pointer-checkbox-#{role_name.to_name.key}",
58
+ checked: card.item_names.include?(role_name),
59
+ option_name: role_name
60
+ end
61
+ end
@@ -0,0 +1,4 @@
1
+ .pointer-checkbox
2
+ = check_box_tag "pointer_checkbox-#{unique_id}", option_name, checked,
3
+ id: id, class: "pointer-checkbox-button _submit-on-change"
4
+ = link_to_card option_name
@@ -0,0 +1,17 @@
1
+ def ok_to_read
2
+ true
3
+ end
4
+
5
+ format :html do
6
+ view :head_content do
7
+ process_content render_raw
8
+ end
9
+
10
+ view :one_line_content do
11
+ raw_one_line_content
12
+ end
13
+
14
+ view :core do
15
+ process_content ::CodeRay.scan(render_raw, :html).div
16
+ end
17
+ end
@@ -0,0 +1,99 @@
1
+ include_set Abstract::AccountDropdown
2
+
3
+ def ok_to_read
4
+ true
5
+ end
6
+
7
+ format :html do
8
+ view :core, cache: :never do
9
+ status_class = Auth.signed_in? ? "logged-in" : "logged-out"
10
+ wrap_with :div, id: "logging", class: status_class do
11
+ navbar_items.join "\n"
12
+ end
13
+ end
14
+
15
+ def navbar_items
16
+ # removed invite for now
17
+ links =
18
+ %i[my_card sign_out sign_up sign_in].map do |link_view|
19
+ render(link_view)
20
+ end.compact
21
+
22
+ links.map do |link|
23
+ wrap_with_nav_item link
24
+ end
25
+ end
26
+
27
+ def self.link_options opts={}
28
+ options = { denial: :blank, cache: :never }.merge opts
29
+ options[:perms] = ->(r) { yield r } if block_given?
30
+ options.clone
31
+ end
32
+
33
+ view :sign_up, link_options(&:show_signup_link?) do
34
+ link_to_card :signup, account_link_text(:sign_up),
35
+ class: nav_link_class("signup-link"),
36
+ path: { action: :new, mark: :signup }
37
+ end
38
+
39
+ view(:sign_in, link_options { !Auth.signed_in? }) do
40
+ link_to_card :signin, account_link_text(:sign_in),
41
+ class: nav_link_class("signin-link")
42
+ end
43
+
44
+ view(:sign_out, link_options { Auth.signed_in? }) do
45
+ link_to_card :signin, account_link_text(:sign_out),
46
+ class: nav_link_class("signout-link"),
47
+ path: { action: :delete }
48
+ end
49
+
50
+ view :invite, link_options(&:show_invite_link?) do
51
+ link_to_card :signup, account_link_text(:invite),
52
+ class: nav_link_class("invite-link"),
53
+ path: { action: :new, mark: :signup }
54
+ end
55
+
56
+ view(:my_card, link_options { Auth.signed_in? }) do
57
+ can_disable_roles? ? interactive_roles_dropdown : simple_roles_dropdown
58
+ end
59
+
60
+ def interactive_roles_dropdown
61
+ nest(enabled_roles_card,
62
+ view: :edit_inline, hide: %i[edit_inline_buttons name_formgroup])
63
+ end
64
+
65
+ def simple_roles_dropdown
66
+ account_dropdown(&method(:link_to_card))
67
+ end
68
+
69
+ def enabled_roles_card
70
+ Auth.current.fetch :enabled_roles, new: { type_id: SessionID }
71
+ end
72
+
73
+ def role_list
74
+ Auth.current_roles.map(&method(:link_to_card))
75
+ end
76
+
77
+ def can_disable_roles?
78
+ Auth.current_roles.size > 1 &&
79
+ Card::Codename.exists?(:enabled_roles) # workaround for broken migrations
80
+ end
81
+
82
+ def account_link_text purpose
83
+ voo.title ||
84
+ I18n.t(purpose, scope: "mod.card-mod-account.set.self.account_links")
85
+ end
86
+
87
+ def nav_link_class type
88
+ "nav-link #{classy(type)}"
89
+ end
90
+
91
+ def show_signup_link?
92
+ !Auth.signed_in? && Card.new(type_id: SignupID).ok?(:create)
93
+ end
94
+
95
+ def show_invite_link?
96
+ Auth.signed_in? &&
97
+ Card.new(type_id: Card.default_accounted_type_id).ok?(:create)
98
+ end
99
+ end
@@ -0,0 +1,5 @@
1
+ def content
2
+ "<!-- *alerts is deprecated. please remove from layout -->"
3
+ end
4
+
5
+ # view :core, :raw
@@ -0,0 +1,9 @@
1
+ format :html do
2
+ view :nav_item do
3
+ wrap_with :div, "", class: "dropdown-divider"
4
+ end
5
+
6
+ view :nav_link_in_dropdown do
7
+ wrap_with :div, "", class: "dropdown-divider"
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+
2
+ def content
3
+ "<!-- *foot is deprecated. please remove from layout -->"
4
+ end
5
+
6
+ # view :core, :raw
@@ -0,0 +1,26 @@
1
+ setting_opts group: :webpage, position: 1, rule_type_editable: false,
2
+ short_help_text: "head tag content",
3
+ help_text: "head tag content"
4
+
5
+ format :html do
6
+ # when *head is rendered in the main body of a page, we escape the HTML
7
+ # otherwise (most typically in the head tag, of course), we render the
8
+ # HTML unescaped
9
+ view :core, cache: :never do
10
+ escape_in_main do
11
+ nest root.card, view: :head
12
+ # note that the head tag for each card is different
13
+ # (different title, different style rules, etc)
14
+ # so we don't cache the core of *head, but we _do_ cache some
15
+ # views within each head (see all/head.rb)
16
+ end
17
+ end
18
+
19
+ view :input do
20
+ "Content can't be edited."
21
+ end
22
+
23
+ def escape_in_main
24
+ main? ? (h yield) : yield
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ setting_opts group: :webpage, position: 3, rule_type_editable: false,
2
+ help_text: "HTML structure of card's page "\
3
+ "[[http://decko.org/layouts | more]]"
@@ -0,0 +1,33 @@
1
+ format :html do
2
+ view :navbox, cache: :never do
3
+ select_tag "query[keyword]", "", class: "_navbox navbox form-control w-100",
4
+ placeholder: navbar_placeholder
5
+ end
6
+
7
+ view :navbar do
8
+ # FIXME: not bootstrap class here.
9
+ class_up "navbox-form", "form-inline"
10
+ render_core
11
+ end
12
+
13
+ view :core do
14
+ form_tag path(mark: :search), method: "get", role: "search",
15
+ class: classy("navbox-form", "nodblclick") do
16
+ wrap_with :div, class: "form-group w-100" do
17
+ render_navbox
18
+ end
19
+ end
20
+ end
21
+
22
+ # def initial_options
23
+ # return "" unless (keyword = params.dig :query, :keyword)
24
+ # options_for_select [keyword]
25
+ # end
26
+
27
+ # TODO: the more natural placeholder would be the content of the navbox card, no?
28
+ # Also, the forced division of "raw" and "core" should probably be replaced
29
+ # with a single haml template (for core view)
30
+ def navbar_placeholder
31
+ @placeholder ||= Card[:navbox, "*placeholder"]&.content || "Search"
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ def raw_help_text
2
+ <<-TEXT
3
+ Sidebar content of [[Layouts]] with sidebar. [[http://decko.org/sidebar|more]]
4
+ TEXT
5
+ end
@@ -0,0 +1,14 @@
1
+ format :html do
2
+ # deprecated; here to support old "*main menu" html cards in existing decks
3
+ view :navbar_links, perms: :none do
4
+ wrap_with :ul, class: "navbar-nav" do
5
+ item_links.map do |link|
6
+ wrap_with(:li, class: "nav-item") { link }
7
+ end.join "\n"
8
+ end
9
+ end
10
+
11
+ def item_links _args={}
12
+ raw(render_core).split(/[,\n]/)
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ include_set Type::Html
4
+
5
+ event :update_layout_registry, :finalize, on: :update do
6
+ Card::Layout.deregister_layout name
7
+ Card::Layout.register_layout_with_nest name, format
8
+ end
9
+
10
+ format :html do
11
+ view :core do
12
+ with_nest_mode :template do
13
+ process_content ::CodeRay.scan(_render_raw, :html).div
14
+ end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: card-mod-layout
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.11.0
5
+ platform: ruby
6
+ authors:
7
+ - Ethan McCutchen
8
+ - Philipp Kühl
9
+ - Gerry Gleason
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2020-12-24 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: card
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - '='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.101.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - '='
27
+ - !ruby/object:Gem::Version
28
+ version: 1.101.0
29
+ - !ruby/object:Gem::Dependency
30
+ name: card-mod-account
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - '='
34
+ - !ruby/object:Gem::Version
35
+ version: 0.11.0
36
+ type: :runtime
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - '='
41
+ - !ruby/object:Gem::Version
42
+ version: 0.11.0
43
+ - !ruby/object:Gem::Dependency
44
+ name: card-mod-session
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - '='
48
+ - !ruby/object:Gem::Version
49
+ version: 0.11.0
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - '='
55
+ - !ruby/object:Gem::Version
56
+ version: 0.11.0
57
+ description: ''
58
+ email:
59
+ - info@decko.org
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - lib/card/layout.rb
65
+ - lib/card/layout/card_layout.rb
66
+ - lib/card/layout/code_layout.rb
67
+ - lib/card/layout/proc_layout.rb
68
+ - lib/card/layout/unknown_layout.rb
69
+ - set/abstract/account_dropdown.rb
70
+ - set/abstract/pointer/html_views.rb
71
+ - set/all/alert.rb
72
+ - set/all/layouts.rb
73
+ - set/all/modal.rb
74
+ - set/all/modal/modal_dialog.haml
75
+ - set/all/navbar_links.rb
76
+ - set/all/overlay.rb
77
+ - set/all/overlay/overlay_header.haml
78
+ - set/all/process_layout.rb
79
+ - set/all/tabs.rb
80
+ - set/right/enabled_roles.rb
81
+ - set/right/enabled_roles/role_checkbox.haml
82
+ - set/right/head.rb
83
+ - set/self/account_links.rb
84
+ - set/self/alerts.rb
85
+ - set/self/dropdown_divider.rb
86
+ - set/self/foot.rb
87
+ - set/self/head.rb
88
+ - set/self/layout.rb
89
+ - set/self/navbox.rb
90
+ - set/self/sidebar.rb
91
+ - set/type/html.rb
92
+ - set/type/layout_type.rb
93
+ homepage: http://decko.org
94
+ licenses:
95
+ - GPL-3.0
96
+ metadata:
97
+ card-mod: layout
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '2.5'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubygems_version: 3.0.3
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: decko layouts
117
+ test_files: []