card-mod-layout 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []