bh 6.6.0 → 6.7.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 +4 -4
- data/CHANGELOG.md +68 -0
- data/README.md +10 -9
- data/app/stylesheets/bh/base.css +3 -0
- data/app/stylesheets/bh.css +0 -1
- data/config/locales/bh.en.yml +1 -0
- data/lib/bh/form_builder/adornments.rb +47 -0
- data/lib/bh/form_builder/buttons.rb +40 -0
- data/lib/bh/form_builder/controls.rb +16 -18
- data/lib/bh/form_builder.rb +21 -36
- data/lib/bh/helpers/confirmations.rb +20 -0
- data/lib/bh/helpers/{notices.rb → toasts.rb} +11 -4
- data/lib/bh/helpers/tooltips.rb +15 -0
- data/lib/bh/helpers.rb +4 -4
- data/lib/bh/version.rb +1 -1
- data/public/bh/css/bh.css +1 -1
- metadata +6 -5
- data/app/stylesheets/bh/flow.css +0 -94
- data/lib/bh/helpers/flows.rb +0 -49
- data/lib/bh/helpers/heads.rb +0 -27
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: bh
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 6.
|
|
4
|
+
version: 6.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- claudiob
|
|
@@ -82,7 +82,6 @@ files:
|
|
|
82
82
|
- app/stylesheets/bh.css
|
|
83
83
|
- app/stylesheets/bh/base.css
|
|
84
84
|
- app/stylesheets/bh/chat.css
|
|
85
|
-
- app/stylesheets/bh/flow.css
|
|
86
85
|
- app/stylesheets/bh/map.css
|
|
87
86
|
- app/stylesheets/bh/pin.css
|
|
88
87
|
- app/stylesheets/bh/search.css
|
|
@@ -103,6 +102,8 @@ files:
|
|
|
103
102
|
- lib/bh.rb
|
|
104
103
|
- lib/bh/engine.rb
|
|
105
104
|
- lib/bh/form_builder.rb
|
|
105
|
+
- lib/bh/form_builder/adornments.rb
|
|
106
|
+
- lib/bh/form_builder/buttons.rb
|
|
106
107
|
- lib/bh/form_builder/comboboxes.rb
|
|
107
108
|
- lib/bh/form_builder/controls.rb
|
|
108
109
|
- lib/bh/form_builder/pins.rb
|
|
@@ -110,10 +111,10 @@ files:
|
|
|
110
111
|
- lib/bh/helpers/chats.rb
|
|
111
112
|
- lib/bh/helpers/chats/asides.rb
|
|
112
113
|
- lib/bh/helpers/chats/asks.rb
|
|
114
|
+
- lib/bh/helpers/confirmations.rb
|
|
113
115
|
- lib/bh/helpers/dialogs.rb
|
|
114
|
-
- lib/bh/helpers/
|
|
115
|
-
- lib/bh/helpers/
|
|
116
|
-
- lib/bh/helpers/notices.rb
|
|
116
|
+
- lib/bh/helpers/toasts.rb
|
|
117
|
+
- lib/bh/helpers/tooltips.rb
|
|
117
118
|
- lib/bh/message.rb
|
|
118
119
|
- lib/bh/version.rb
|
|
119
120
|
- package.json
|
data/app/stylesheets/bh/flow.css
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/* The page `flow` draws: a column taking the rest of the window so the foot rests at the
|
|
2
|
-
bottom of a tall screen and scrolls into view on a short one, everything centered. */
|
|
3
|
-
.flow { flex: 1; display: flex; flex-direction: column; text-align: center }
|
|
4
|
-
.flow > header { display: flex; justify-content: center; margin-bottom: 2rem }
|
|
5
|
-
.flow > h1 { margin: 0 0 0.75rem; font-size: clamp(1.5rem, 4vw, 2rem); font-weight: 700; letter-spacing: -0.02em }
|
|
6
|
-
.flow > p { margin: 0 0 1.5rem }
|
|
7
|
-
/* `display: contents`, so a `button_to` form stops being a box and its button becomes a flex
|
|
8
|
-
item, which is what lets the middot space evenly between two words. A button in the foot is
|
|
9
|
-
a word among words: the foot's size, no padding and no height of its own. */
|
|
10
|
-
.flow > footer { flex: none; margin-top: auto; padding-top: 2.5rem; display: flex; align-items: baseline; justify-content: center; gap: 0.5rem; font-size: 0.875rem }
|
|
11
|
-
.flow > footer form { display: contents }
|
|
12
|
-
.flow > footer .btn { font-size: inherit; padding: 0; min-height: 0 }
|
|
13
|
-
@media (max-width: 575px) { .flow > header { margin-bottom: 1.75rem } }
|
|
14
|
-
|
|
15
|
-
/* The one element on the page allowed a fill, a border and a shadow at once, because it holds
|
|
16
|
-
the only thing the page is for. As wide as the page said, `--flow-width`, and nine tenths of
|
|
17
|
-
the page color rather than `opacity`, which would fade the border and the words with it. */
|
|
18
|
-
.flow-card {
|
|
19
|
-
width: 100%;
|
|
20
|
-
max-width: var(--flow-width, 75rem);
|
|
21
|
-
background: color-mix(in srgb, var(--bs-bg-body) 90%, transparent);
|
|
22
|
-
border: 1px solid var(--bs-primary-border);
|
|
23
|
-
border-radius: 0.375rem;
|
|
24
|
-
box-shadow: -0.5rem 1rem 2.25rem -0.75rem rgb(6 22 40 / 40%);
|
|
25
|
-
}
|
|
26
|
-
/* Centered on the controls rather than the form, since a field does not inherit it and a
|
|
27
|
-
form helper may hard-code `text-start`. */
|
|
28
|
-
/* The card is read down the middle, and so is what a form says about itself: the legend over a
|
|
29
|
-
group, a submit, a single box a page is nothing but. A row is the exception -- its words and
|
|
30
|
-
the box under them are read from where they start, since a label centered over the box it
|
|
31
|
-
names leaves the eye hunting for where each one begins. */
|
|
32
|
-
.flow-card .form-label, .flow-card .form-control, .flow-card .invalid-feedback { text-align: center }
|
|
33
|
-
.flow-card form:has(.flow-field ~ .flow-field, .form-check ~ .form-check) :is(.flow-field, .form-check),
|
|
34
|
-
.flow-card form:has(.flow-field ~ .flow-field, .form-check ~ .form-check) .flow-field .form-label,
|
|
35
|
-
.flow-card form:has(.flow-field ~ .flow-field, .form-check ~ .form-check) .flow-field .form-control { text-align: start }
|
|
36
|
-
|
|
37
|
-
/* A ticked box and the words beside it are a row of two, with room between them: Bootstrap
|
|
38
|
-
hangs the box in the padding by a negative margin, which puts it under centered words. */
|
|
39
|
-
.flow-card .form-check { display: flex; align-items: start; gap: 1rem; padding-left: 0 }
|
|
40
|
-
.flow-card .form-check .form-check-input { flex: none; margin: 0.25em 0 0 }
|
|
41
|
-
|
|
42
|
-
/* A group of controls carries its own spacing, and twice as much under it as over: the words
|
|
43
|
-
over a group belong to it, and even space either side would let them drift between two. */
|
|
44
|
-
.flow-fieldset { margin-block: 1rem 2rem; gap: 1rem }
|
|
45
|
-
|
|
46
|
-
/* The words and the control they name, as one cell, which is what lets a form be laid out in
|
|
47
|
-
columns however much markup a control draws for itself. Named for the flow rather than the
|
|
48
|
-
form: Bootstrap has a `form-field` of its own, whose rules sized our words to themselves and
|
|
49
|
-
held them at the left of a row they were meant to be centered in. */
|
|
50
|
-
.flow-card .flow-field { display: grid; align-content: start }
|
|
51
|
-
|
|
52
|
-
/* A card wide enough for two columns takes them, and everything else in the fieldset -- the
|
|
53
|
-
legend, a submit -- keeps the whole row. The card is what is measured rather than the
|
|
54
|
-
window, so the narrow card of a sign-in page stays one column on the widest screen. */
|
|
55
|
-
.flow-card { container-type: inline-size }
|
|
56
|
-
@container (min-width: 40rem) {
|
|
57
|
-
.flow-card fieldset.d-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 1rem }
|
|
58
|
-
.flow-card fieldset.d-grid > :not(.flow-field) { grid-column: 1 / -1 }
|
|
59
|
-
}
|
|
60
|
-
/* A rule between two ways of doing the same thing, with room either side of it: a card is
|
|
61
|
-
short enough that a thin line needs the space to read as a divide rather than as a scratch. */
|
|
62
|
-
.flow-card hr { margin-block: 2rem }
|
|
63
|
-
|
|
64
|
-
/* The same edge the card wears, rather than Bootstrap's fainter default. */
|
|
65
|
-
.flow-card .dialog { --bs-dialog-border-color: var(--bs-primary-border) }
|
|
66
|
-
|
|
67
|
-
/* A card holding something longer than the page takes the rest of the window and scrolls that
|
|
68
|
-
part inside itself, so the line at its head, whatever sits at its foot and the way out under
|
|
69
|
-
the card stay in sight however long it gets. A thread is one such thing and says so by its
|
|
70
|
-
own markup; anything else -- a contract, a table -- says `flow-scroll` on the part that
|
|
71
|
-
scrolls. `flex: none` before the height: the column is a flex item of `main`, and `flex: 1`
|
|
72
|
-
would size it to its content and let the page grow and scroll instead. `min-height: 0` down
|
|
73
|
-
the chain, since a flex item will not shrink below its content without it. The 2rem is the
|
|
74
|
-
layout's `py-4`, the gutter above and below `main`. A card given a height cannot always
|
|
75
|
-
honour it -- a short window leaves less room than what does not scroll needs -- so it
|
|
76
|
-
scrolls whole rather than letting what it holds out past its own edges. */
|
|
77
|
-
.flow:has(.chat-thread, .flow-scroll, .flow-middle) { flex: none; height: calc(100svh - 2rem) }
|
|
78
|
-
.flow-card:has(.chat-thread, .flow-scroll, .flow-middle) { display: flex; flex-direction: column; flex: 1; min-height: 15rem; overflow: auto }
|
|
79
|
-
.flow-card .chat-thread,
|
|
80
|
-
.flow-card > :has(.flow-scroll) { display: flex; flex-direction: column; flex: 1; min-height: 0 }
|
|
81
|
-
.flow-card .chat, .flow-card .flow-scroll { flex: 1; min-height: 0; overflow-y: auto }
|
|
82
|
-
.flow-card .chat-ask { text-align: center }
|
|
83
|
-
/* A bubble reads from its start, though: a flow centers a card's words, and an answer long
|
|
84
|
-
enough to wrap is a paragraph, which is read down an edge rather than about a middle. */
|
|
85
|
-
.flow-card .chat-bubble { text-align: start }
|
|
86
|
-
|
|
87
|
-
/* A card standing as tall as those but with little in it: what it holds sits in the middle of
|
|
88
|
-
it, down as well as across, rather than at the top of a sheet of white. */
|
|
89
|
-
.flow-card .flow-middle { flex: 1; display: flex; flex-direction: column; justify-content: center }
|
|
90
|
-
/* On a phone the card is the page: it breaks out of the gutter with `margin-inline` rather
|
|
91
|
-
than a width, so it stays in the flow, and loses the side border and the corners with it. */
|
|
92
|
-
@media (width < 768px) {
|
|
93
|
-
.flow-card:has(.chat-thread) { width: auto; margin-inline: calc(50% - 50vw); padding-inline: calc(50vw - 50%); border-inline: 0; border-radius: 0 }
|
|
94
|
-
}
|
data/lib/bh/helpers/flows.rb
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
module Bh
|
|
2
|
-
module Helpers
|
|
3
|
-
# The page of a signup flow: a mark over whatever it stands on, a title, a line, one card,
|
|
4
|
-
# the way out.
|
|
5
|
-
module Flows
|
|
6
|
-
# How wide the card is unless a page says otherwise: as wide as a thread, a table or a
|
|
7
|
-
# contract wants. A page asking one question says a narrower one.
|
|
8
|
-
WIDTH = '75rem'
|
|
9
|
-
|
|
10
|
-
# Draws the page around the card's content: `header` over what the view said in
|
|
11
|
-
# `content_for` — its `:title`, its `:subtitle`, how wide the card is in `:width` — and
|
|
12
|
-
# the pieces of `footer` under the card joined by a middot. No header draws no header,
|
|
13
|
-
# no subtitle no line and an empty footer no foot: a page says only what it has. Every
|
|
14
|
-
# other option is an attribute of the column, which is where a host names the controller
|
|
15
|
-
# that paints what the page stands on.
|
|
16
|
-
def flow(header: nil, footer: [], **options, &)
|
|
17
|
-
parts = [
|
|
18
|
-
(tag.header header if header),
|
|
19
|
-
*bh_flow_words,
|
|
20
|
-
bh_flow_card(content_for(:width), &),
|
|
21
|
-
bh_flow_footer(footer),
|
|
22
|
-
]
|
|
23
|
-
classes = class_names 'flow', options[:class]
|
|
24
|
-
|
|
25
|
-
tag.div safe_join(parts.compact), class: classes, **options.except(:class)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def bh_flow_words
|
|
31
|
-
[tag.h1(content_for(:title)), (tag.p content_for :subtitle if content_for? :subtitle)]
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def bh_flow_card(width, &)
|
|
35
|
-
width = width.presence || WIDTH
|
|
36
|
-
style = "--flow-width: #{width}" unless width == WIDTH
|
|
37
|
-
|
|
38
|
-
tag.div capture(&), class: 'flow-card p-3 md:p-4 mx-auto', style:
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def bh_flow_footer(pieces)
|
|
42
|
-
pieces = Array(pieces).compact_blank
|
|
43
|
-
return if pieces.empty?
|
|
44
|
-
|
|
45
|
-
tag.footer safe_join(pieces, tag.span('·', class: 'fg-2', aria: { hidden: true }))
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
data/lib/bh/helpers/heads.rb
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module Bh
|
|
2
|
-
module Helpers
|
|
3
|
-
# What a page built on Bootstrap 6 puts in its `<head>`.
|
|
4
|
-
module Heads
|
|
5
|
-
# One line per tag, each at the depth a head's children sit at in a layout indented
|
|
6
|
-
# by two: `<html>`, then `<head>`, then these.
|
|
7
|
-
INDENT = "\n "
|
|
8
|
-
|
|
9
|
-
# The metas Bootstrap and Turbo read, and the stylesheet and script this gem serves.
|
|
10
|
-
# `color-scheme` is what lets `light dark` follow the system, so most pages need no
|
|
11
|
-
# theme attribute at all. A page refresh Turbo is sent morphs the page in place and
|
|
12
|
-
# keeps the scroll, so an answer landing in a thread leaves the reader where they were.
|
|
13
|
-
# The icon font is the host's to link: which glyphs a page draws is the host's business,
|
|
14
|
-
# and a gem reaching for somebody else's CDN on its own is not.
|
|
15
|
-
def bh_head_tags
|
|
16
|
-
safe_join [
|
|
17
|
-
tag.meta(name: 'viewport', content: 'width=device-width, initial-scale=1'),
|
|
18
|
-
tag.meta(name: 'color-scheme', content: 'light dark'),
|
|
19
|
-
tag.meta(name: 'turbo-refresh-method', content: 'morph'),
|
|
20
|
-
tag.meta(name: 'turbo-refresh-scroll', content: 'preserve'),
|
|
21
|
-
tag.link(rel: 'stylesheet', href: "#{Engine::PREFIX}css/bh.css"),
|
|
22
|
-
tag.script('', type: 'module', src: "#{Engine::PREFIX}js/bh.js"),
|
|
23
|
-
], INDENT
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|