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.
- 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: []
|