1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
require "pro_tacts/admin/phlex"
module ProTacts
module Admin
# The trunk every form card shares: the record's caption row and
# card (admin.css), the form as the card's body, and the action
# bar under both.
#
# The form is the caller's block rather than the component's,
# because its rhythm is the caller's question: an editor's rows
# are a field-stack on the record's grid, and an upload's lone
# field is a Gloss settings field with no grid to align to.
# Everything around the form is the same for every caller, which
# is why it lives here.
#
# `back` names the caption row's link, `[href, label]`, the label
# a string or whatever `render` takes (GroupsEdit's is a
# GroupLabel); a card with neither back nor `nav` — the walk's
# editor, whose navigation is the list beside it — renders no
# caption row at all. `nav`, when something is passed, is the
# row's one action at its right edge. The footer's Cancel goes
# where the back link goes — the editors' own rule, and absent
# with it — and the submit reaches the form by id rather than
# sitting in it, so the action bar is the card's and not the
# form's.
class RecordCard < Phlex::HTML
# @rbs @back: ([String, (String | Phlex::HTML | Proc)])?
# @rbs @nav: (Proc | Phlex::HTML)?
# @rbs @heading: String?
# @rbs @aside: Phlex::HTML?
# @rbs @sidebar: Phlex::HTML?
# @rbs @submit: String?
# @rbs @form: String?
#: (?back: ([String, (String | Phlex::HTML | Proc)])?, ?submit: String?, ?form: String?, ?nav: (Proc | Phlex::HTML)?, ?heading: String?, ?aside: Phlex::HTML?, ?sidebar: Phlex::HTML?) -> void
def initialize(back: nil, submit: nil, form: nil, nav: nil, heading: nil, aside: nil, sidebar: nil)
@back = back
@nav = nav
@heading = heading
@aside = aside
@sidebar = sidebar
@submit = submit
@form = form
end
def view_template(&)
sidebar = @sidebar
return record(&) if sidebar.nil?
walk(sidebar, &)
end
private
# The walk's list of contacts, and the record beside it when
# there is one — the sidebar grid (admin.css). Skipped rather
# than rendered around one column for the reason #paired is:
# an ordinary edit has no walk to stand in.
#: (Phlex::HTML sidebar) { () -> void } -> void
def walk(sidebar, &)
div(class: "walk") do
render sidebar
record(&)
end
end
# The caption-to-card block the details page uses (.record in
# admin.css): the back link is this card's caption row, and the
# row's one action sits at its right edge — the same place, and
# the same shape, as the details page's edit link. Each mode's
# row names the other thing you can do to the record from it.
def record(&)
div(class: "record") do
href, label = @back
if href || @nav
div(class: "record-nav") do
if href
a(href:, class: "type-label") do
plain "‹ "
render label
end
end
render @nav if @nav
end
end
paired(&)
end
end
# The editor's card, and what stands beside it when something
# does — the two-column grid the dashboard uses, for its reason
# (admin.css). With nothing beside it the wrapper is skipped
# rather than rendered around one card: a grid of one is a
# different element for no difference on screen.
#: () { () -> void } -> void
def paired(&)
aside = @aside
return card(&) if aside.nil?
div(class: "paired") do
render aside
card(&)
end
end
def card(&)
div(class: "card") do
div(class: "card-body") do
h1(class: "type-h2") { @heading } if @heading
yield
end
# A dialog footer's shape (admin.css), absent outright when
# there is nothing to submit and nowhere to cancel to — a
# card that only reads (ImportSaved). Cancel is navigation: a
# link in Gloss's `.btn` contract, which is what an anchor
# that acts like a button opts into.
if @submit || @back
footer do
a(href: @back&.fetch(0), class: "btn") { "Cancel" } if @back
button(type: "submit", form: @form, data: {variant: "primary"}) { @submit } if @submit
end
end
end
end
end
end
end