@markout-lang/bootstrap-kit 0.2.0 → 0.2.2
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.
- package/README.md +20 -48
- package/package.json +1 -1
- package/parts/input.htm +17 -0
- package/parts/navbar.htm +12 -1
package/README.md
CHANGED
|
@@ -33,54 +33,10 @@ Bootstrap out:
|
|
|
33
33
|
<:import src="/npm/@markout-lang/bootstrap-kit/parts/card.htm" />
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
That serves [the site](../../sites/site/), which is where the pages built on
|
|
43
|
-
this kit live: `/demos/kitchen-sink.html`, every component one after another;
|
|
44
|
-
and `/demos/orbit.html`, an operations dashboard built out of them, which is
|
|
45
|
-
what they look like wired to one page's data.
|
|
46
|
-
|
|
47
|
-
The site is a plain Express app rather than markout's `Server`, because Orbit
|
|
48
|
-
is a whole application: it has an API of its own, served from a fake
|
|
49
|
-
in-memory database (`orbit-db.ts`), and markout is the middleware that
|
|
50
|
-
renders its pages. Orbit reads that API with `std-data` from the std kit,
|
|
51
|
-
which fetches while the page renders — so the served console is complete and
|
|
52
|
-
the browser asks for nothing.
|
|
53
|
-
|
|
54
|
-
Orbit is four files, which is the shape an application takes:
|
|
55
|
-
`demos/orbit.html` for its state, layout and logic; `demos/orbit/components.htm`
|
|
56
|
-
for the tags it defines on top of the kit's; `demos/orbit/sources.htm` for
|
|
57
|
-
where its data comes from; and `server.ts` for its API. The first is imported,
|
|
58
|
-
the second included — a definition wants to arrive once per page however often
|
|
59
|
-
it is named, and an instance wants to be spliced exactly where it is written,
|
|
60
|
-
because that is where its name resolves.
|
|
61
|
-
|
|
62
|
-
`orbit/sources.htm` also shows the token pattern working for an application's
|
|
63
|
-
own fragment: its root carries `:apiBase="/api"`, which lands on whatever
|
|
64
|
-
contains the `<:include>` unless that element declares it, so a page points
|
|
65
|
-
Orbit at another host without the fragment changing. That is the same
|
|
66
|
-
mechanism as `bsCssUrl` below — not something kits get and applications
|
|
67
|
-
don't.
|
|
68
|
-
|
|
69
|
-
## Tests
|
|
70
|
-
|
|
71
|
-
`packages/cli/test/kits/bootstrap-kit.test.ts`, in two tiers:
|
|
72
|
-
|
|
73
|
-
- **compiled**, which is most of it: every part compiles on its own, the
|
|
74
|
-
showcase compiles and server-renders with nothing reported, and the id
|
|
75
|
-
wiring is checked mechanically — every `aria-controls`, `aria-labelledby`,
|
|
76
|
-
`for`, `data-bs-target` and `data-bs-parent` has to name an element that
|
|
77
|
-
exists. That last one is the kit's whole reason for existing, so it is
|
|
78
|
-
worth a test that can't be argued with.
|
|
79
|
-
- **live**, in Playwright: the value-driven components actually driven, and
|
|
80
|
-
a stubbed Bootstrap asserting the plugin calls. Skipped when no browser is
|
|
81
|
-
installed (`npx playwright install chromium`).
|
|
82
|
-
|
|
83
|
-
Nothing reaches the network: the tests set the URL tokens to local files.
|
|
36
|
+
Every component below is shown one after another in the [kitchen
|
|
37
|
+
sink](https://markout.dev/demos/kitchen-sink), and
|
|
38
|
+
[Orbit](https://markout.dev/demos/orbit) is an operations dashboard built out
|
|
39
|
+
of them — what they look like wired to one page's data.
|
|
84
40
|
|
|
85
41
|
## What is and isn't a component
|
|
86
42
|
|
|
@@ -152,6 +108,19 @@ That holds however deeply the component sits: a `bs-toast :aka="saved"`
|
|
|
152
108
|
written inside a `bs-toast-container` is still named where you wrote it, so
|
|
153
109
|
`saved.open = true` reaches it from anywhere on the page.
|
|
154
110
|
|
|
111
|
+
`bs-input` and `bs-textarea` answer `valid` the same way -- is there a value,
|
|
112
|
+
and does it pass `check`? -- which is what a submit button needs:
|
|
113
|
+
|
|
114
|
+
```html
|
|
115
|
+
<bs-input :aka="email" :label="Email" :check=${v => v.includes('@')} />
|
|
116
|
+
<bs-button :disabled=${!email.valid}>Send</bs-button>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
It is not the negation of the error state. An empty field is not marked
|
|
120
|
+
wrong, which is what keeps a form from opening in red, and it is not
|
|
121
|
+
something to submit either -- so it shows nothing and answers `false`.
|
|
122
|
+
`required` is what makes empty an error as well as a gap.
|
|
123
|
+
|
|
155
124
|
**Optional regions are `:if`.** A region that exists only when a parameter
|
|
156
125
|
was given is written `:if=${header}`, and nothing inside it evaluates while
|
|
157
126
|
the condition is false — which is what makes `${user.name}` safe to write in
|
|
@@ -274,6 +243,9 @@ commented.
|
|
|
274
243
|
| `bs-range` | `label` `name` `min` `max` `step` `value` `disabled` `showValue` |
|
|
275
244
|
| `bs-input-group` | `prefix` `suffix` `size` |
|
|
276
245
|
|
|
246
|
+
`bs-input` and `bs-textarea` also answer `valid`, which is read rather than
|
|
247
|
+
passed -- see *Values are read and written* above.
|
|
248
|
+
|
|
277
249
|
### Components
|
|
278
250
|
|
|
279
251
|
| Tag | Parameters |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markout-lang/bootstrap-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Bootstrap 5.3 as Markout components: one part per component, with the id wiring, the ARIA attributes and the repetition written once",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
package/parts/input.htm
CHANGED
|
@@ -19,6 +19,17 @@
|
|
|
19
19
|
invalid once it is non-empty and fails `:check`. Empty is neutral, so a
|
|
20
20
|
form doesn't open covered in errors. `:required` extends that to empty.
|
|
21
21
|
|
|
22
|
+
`:valid` is the other side of that, and what a form's own controls read —
|
|
23
|
+
|
|
24
|
+
<bs-input :aka="email" :label="Email" :check=${v => v.includes('@')} />
|
|
25
|
+
<bs-button :disabled=${!email.valid}>Send</bs-button>
|
|
26
|
+
|
|
27
|
+
It is deliberately NOT the negation of the error state, which is the one
|
|
28
|
+
thing about it worth knowing. An untouched field is not wrong — that is
|
|
29
|
+
what keeps the form out of red — and it is also not something to submit.
|
|
30
|
+
So empty answers `false` here while showing nothing, and `:required` is
|
|
31
|
+
what makes empty an error as well as a gap.
|
|
32
|
+
|
|
22
33
|
Floating labels are the same control with the label after the input
|
|
23
34
|
instead of before it — Bootstrap's CSS keys off that order — so the two
|
|
24
35
|
layouts are two regions rather than one with a class toggled on it.
|
|
@@ -49,6 +60,9 @@
|
|
|
49
60
|
:_invalid=${_empty ? required : !check(value)}
|
|
50
61
|
:_class=${['form-control', size ? `form-control-${size}` : '']
|
|
51
62
|
.filter(s => s).join(' ')}
|
|
63
|
+
|
|
64
|
+
// read from outside: is there a value, and does it pass?
|
|
65
|
+
:valid=${!_empty && check(value)}
|
|
52
66
|
>
|
|
53
67
|
<!--- plain layout: label, control, feedback -->
|
|
54
68
|
<div :if=${!floating}>
|
|
@@ -123,6 +137,9 @@
|
|
|
123
137
|
:_helpId=${`bs-textarea-help-${$id}`}
|
|
124
138
|
:_empty=${value === '' || value == null}
|
|
125
139
|
:_invalid=${_empty ? required : !check(value)}
|
|
140
|
+
|
|
141
|
+
// read from outside, as bs-input's is
|
|
142
|
+
:valid=${!_empty && check(value)}
|
|
126
143
|
>
|
|
127
144
|
<label class="form-label"
|
|
128
145
|
for=${_id}
|
package/parts/navbar.htm
CHANGED
|
@@ -40,6 +40,17 @@
|
|
|
40
40
|
extra,
|
|
41
41
|
].filter(s => s).join(' ')}
|
|
42
42
|
|
|
43
|
+
// The gap under the links is only wanted while they are stacked, which
|
|
44
|
+
// is up to `expand` and not to any one breakpoint: left at `mb-lg-0` it
|
|
45
|
+
// would survive into a bar that has already gone horizontal and push the
|
|
46
|
+
// links up off the brand's line. A navbar that never collapses has no
|
|
47
|
+
// stacked state to space out, hence the plain `mb-0`.
|
|
48
|
+
:_navClass=${[
|
|
49
|
+
'navbar-nav',
|
|
50
|
+
'me-auto',
|
|
51
|
+
expand ? `mb-2 mb-${expand}-0` : 'mb-0',
|
|
52
|
+
].join(' ')}
|
|
53
|
+
|
|
43
54
|
class=${_class}
|
|
44
55
|
:class-sticky-top=${sticky}
|
|
45
56
|
:class-fixed-top=${fixed}
|
|
@@ -59,7 +70,7 @@
|
|
|
59
70
|
</button>
|
|
60
71
|
|
|
61
72
|
<div class="collapse navbar-collapse" id=${_id}>
|
|
62
|
-
<ul class
|
|
73
|
+
<ul class=${_navClass}>
|
|
63
74
|
<li class="nav-item" :for-each=${items} :for-as="item">
|
|
64
75
|
<a class="nav-link"
|
|
65
76
|
:class-active=${item.active}
|