card-mod-layout 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/card/layout.rb +93 -0
- data/lib/card/layout/card_layout.rb +42 -0
- data/lib/card/layout/code_layout.rb +9 -0
- data/lib/card/layout/proc_layout.rb +9 -0
- data/lib/card/layout/unknown_layout.rb +20 -0
- data/set/abstract/account_dropdown.rb +25 -0
- data/set/abstract/pointer/html_views.rb +5 -0
- data/set/all/alert.rb +23 -0
- data/set/all/layouts.rb +34 -0
- data/set/all/modal.rb +119 -0
- data/set/all/modal/modal_dialog.haml +12 -0
- data/set/all/navbar_links.rb +68 -0
- data/set/all/overlay.rb +104 -0
- data/set/all/overlay/overlay_header.haml +0 -0
- data/set/all/process_layout.rb +77 -0
- data/set/all/tabs.rb +51 -0
- data/set/right/enabled_roles.rb +61 -0
- data/set/right/enabled_roles/role_checkbox.haml +4 -0
- data/set/right/head.rb +17 -0
- data/set/self/account_links.rb +99 -0
- data/set/self/alerts.rb +5 -0
- data/set/self/dropdown_divider.rb +9 -0
- data/set/self/foot.rb +6 -0
- data/set/self/head.rb +26 -0
- data/set/self/layout.rb +3 -0
- data/set/self/navbox.rb +33 -0
- data/set/self/sidebar.rb +5 -0
- data/set/type/html.rb +14 -0
- data/set/type/layout_type.rb +16 -0
- metadata +117 -0
checksums.yaml
ADDED
@@ -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
|
data/lib/card/layout.rb
ADDED
@@ -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,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
|
data/set/all/alert.rb
ADDED
@@ -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, "×", "aria-hidden" => true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/set/all/layouts.rb
ADDED
@@ -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
|
data/set/all/modal.rb
ADDED
@@ -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
|
data/set/all/overlay.rb
ADDED
@@ -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
|
data/set/all/tabs.rb
ADDED
@@ -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
|
data/set/right/head.rb
ADDED
@@ -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
|
data/set/self/alerts.rb
ADDED
data/set/self/foot.rb
ADDED
data/set/self/head.rb
ADDED
@@ -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
|
data/set/self/layout.rb
ADDED
data/set/self/navbox.rb
ADDED
@@ -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
|
data/set/self/sidebar.rb
ADDED
data/set/type/html.rb
ADDED
@@ -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: []
|