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.
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.6.0
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/flows.rb
115
- - lib/bh/helpers/heads.rb
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
@@ -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
- }
@@ -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
@@ -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