@blackcube/aurelia-kit 1.0.0-rc1 → 1.0.0-rc10
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 +250 -16
- package/dist/index.es.js +1619 -590
- package/dist/index.es.js.map +1 -1
- package/dist/types/abstracts/components/abstract-dialog.d.ts +41 -0
- package/dist/types/abstracts/components/abstract-dialog.d.ts.map +1 -0
- package/dist/types/abstracts/components/abstract-dropdown.d.ts +117 -0
- package/dist/types/abstracts/components/abstract-dropdown.d.ts.map +1 -0
- package/dist/types/abstracts/components/abstract-pager.d.ts +51 -0
- package/dist/types/abstracts/components/abstract-pager.d.ts.map +1 -0
- package/dist/types/abstracts/components/abstract-toaster.d.ts +42 -0
- package/dist/types/abstracts/components/abstract-toaster.d.ts.map +1 -0
- package/dist/types/abstracts/components/index.d.ts +7 -0
- package/dist/types/abstracts/components/index.d.ts.map +1 -0
- package/dist/types/attributes/ajaxify-trigger.d.ts.map +1 -1
- package/dist/types/attributes/alert.d.ts.map +1 -1
- package/dist/types/attributes/badge.d.ts.map +1 -1
- package/dist/types/attributes/burger.d.ts.map +1 -1
- package/dist/types/attributes/dropdown.d.ts.map +1 -1
- package/dist/types/attributes/first-error.d.ts +19 -0
- package/dist/types/attributes/first-error.d.ts.map +1 -0
- package/dist/types/attributes/focus.d.ts +13 -0
- package/dist/types/attributes/focus.d.ts.map +1 -0
- package/dist/types/attributes/index.d.ts +2 -0
- package/dist/types/attributes/index.d.ts.map +1 -1
- package/dist/types/attributes/menu.d.ts +23 -1
- package/dist/types/attributes/menu.d.ts.map +1 -1
- package/dist/types/attributes/modal-trigger.d.ts +0 -1
- package/dist/types/attributes/modal-trigger.d.ts.map +1 -1
- package/dist/types/attributes/overlay.d.ts.map +1 -1
- package/dist/types/attributes/pager.d.ts.map +1 -1
- package/dist/types/attributes/password.d.ts.map +1 -1
- package/dist/types/attributes/popover-trigger.d.ts.map +1 -1
- package/dist/types/attributes/popover.d.ts.map +1 -1
- package/dist/types/attributes/profile.d.ts.map +1 -1
- package/dist/types/attributes/tabs.d.ts.map +1 -1
- package/dist/types/attributes/toast.d.ts +2 -2
- package/dist/types/attributes/toast.d.ts.map +1 -1
- package/dist/types/attributes/toaster-trigger.d.ts +3 -3
- package/dist/types/attributes/toaster-trigger.d.ts.map +1 -1
- package/dist/types/attributes/toaster.d.ts +2 -7
- package/dist/types/attributes/toaster.d.ts.map +1 -1
- package/dist/types/components/bleet-ajaxify.d.ts.map +1 -1
- package/dist/types/components/bleet-dialog.d.ts +15 -26
- package/dist/types/components/bleet-dialog.d.ts.map +1 -1
- package/dist/types/enums/event-aggregator.d.ts +14 -7
- package/dist/types/enums/event-aggregator.d.ts.map +1 -1
- package/dist/types/index.d.ts +7 -5
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/interfaces/dialog.d.ts +1 -2
- package/dist/types/interfaces/dialog.d.ts.map +1 -1
- package/dist/types/interfaces/event-aggregator.d.ts +12 -4
- package/dist/types/interfaces/event-aggregator.d.ts.map +1 -1
- package/dist/types/services/storage-service.d.ts.map +1 -1
- package/dist/types/services/transition-service.d.ts.map +1 -1
- package/dist/types/services/trap-focus-service.d.ts +17 -1
- package/dist/types/services/trap-focus-service.d.ts.map +1 -1
- package/dist/types/utils/toaster.d.ts +25 -0
- package/dist/types/utils/toaster.d.ts.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ widgets, styled with Tailwind classes written in the PHP markup. The HTTP layer
|
|
|
12
12
|
npm install @blackcube/aurelia-kit @blackcube/aurelia-api
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Peer dependencies: `aurelia` `^2.0.0-rc.2`, `@blackcube/aurelia-api` `^1.0.0`.
|
|
15
|
+
Peer dependencies: `aurelia` `^2.0.0-rc.2`, `@blackcube/aurelia-api` `^1.0.0-rc1`.
|
|
16
16
|
|
|
17
17
|
The package is ESM only (`dist/index.es.js`, built by Vite in library mode); the application bundler resolves it.
|
|
18
18
|
|
|
@@ -61,7 +61,35 @@ An element that must leave the layout when hidden also carries the HTML `hidden`
|
|
|
61
61
|
(Tailwind's preflight turns it into `display: none`); the attribute toggles it, never a class.
|
|
62
62
|
|
|
63
63
|
Attribute options use the Aurelia multi-binding syntax, as written by the PHP widgets:
|
|
64
|
-
`bleet-toaster-trigger="
|
|
64
|
+
`bleet-toaster-trigger="level: success; title: Saved; content: Done; duration: 3000"`.
|
|
65
|
+
|
|
66
|
+
## Abstract components
|
|
67
|
+
|
|
68
|
+
For a SPA, where the application renders its own markup, the kit gives the behavior of a component as an abstract
|
|
69
|
+
class, without markup: the application's element extends it and gives its template — its markup, its classes, its
|
|
70
|
+
texts, its icons. The abstract classes are not registered; the element that extends them is, by the application.
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { customElement } from 'aurelia';
|
|
74
|
+
import { AbstractDialog } from '@blackcube/aurelia-kit';
|
|
75
|
+
|
|
76
|
+
@customElement('app-drawer')
|
|
77
|
+
export class AppDrawer extends AbstractDialog {}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- The bindables and the lifecycle of the abstract class are inherited: Aurelia reads the bindables along the prototype
|
|
81
|
+
chain. An element that overrides `attached()` or `detaching()` calls `super`.
|
|
82
|
+
- The template binds the members of the instance directly (`${title}`, `toast of toasts`) and gives the refs the names
|
|
83
|
+
the class reads (`ref="dialogElement"`).
|
|
84
|
+
- The state classes of the host (`data-<name>-show`, `data-<name>-hide`) and its attributes (`popover`) go on the root
|
|
85
|
+
`<template>` of the template: Aurelia carries them to the host.
|
|
86
|
+
|
|
87
|
+
| Class | Component |
|
|
88
|
+
|---|---|
|
|
89
|
+
| `AbstractDialog` | a dialog: modal, drawer — see [Dialogs](#dialogs) |
|
|
90
|
+
| `AbstractDropdown` | a dropdown: searchable, multiple — see [`AbstractDropdown`](#abstractdropdown-spa) |
|
|
91
|
+
| `AbstractPager` | the pagination of a list — see [`AbstractPager`](#abstractpager-spa) |
|
|
92
|
+
| `AbstractToaster` | the toaster — see [Toasts](#toasts) |
|
|
65
93
|
|
|
66
94
|
## Overlay
|
|
67
95
|
|
|
@@ -101,6 +129,7 @@ sends. Its template holds no markup of its own:
|
|
|
101
129
|
<au-slot name="loader" if.bind="loading"><progress></progress></au-slot>
|
|
102
130
|
<template else>
|
|
103
131
|
<au-compose if.bind="contentView" template.bind="contentView"></au-compose>
|
|
132
|
+
<au-slot else></au-slot>
|
|
104
133
|
</template>
|
|
105
134
|
</dialog>
|
|
106
135
|
```
|
|
@@ -140,18 +169,22 @@ Behavior:
|
|
|
140
169
|
- `content` is the whole HTML of the dialog (frame, header, body, footer), rendered by the server; it becomes the
|
|
141
170
|
view of the `au-compose`, so the `bleet-*` attributes it holds work. A response without `content` keeps the
|
|
142
171
|
current view.
|
|
172
|
+
- `Open` (or `Toggle` when closed) with a `content` and no `url`: that HTML is shown as the server's would be, without
|
|
173
|
+
request (the same view, its `bleet-*` attributes, its forms posted by the element) — a page that already holds the
|
|
174
|
+
HTML (a confirmation, a `<template>` of the page).
|
|
143
175
|
- A `<form>` submitted inside the dialog is posted by the element (multipart, method from `_method` or the form,
|
|
144
176
|
POST by default, with the clicked submit button `name` / `value`); its `IDialogResponse` is applied the same way.
|
|
145
177
|
- A `<form>` marked `data-dialog="native"` is not intercepted: the browser submits it (download, plain post).
|
|
146
178
|
- A click on an element inside `[data-dialog="close"]`, the Escape key, or a click on the overlay closes the dialog
|
|
147
179
|
with the hide transition; closing empties the view. A `cancel` coming from an `<input type="file">` (file chooser
|
|
148
180
|
dismissed) does not close it. An `Open` received while the dialog is closing cancels the closing.
|
|
181
|
+
- Escape while a menu of the dialog is open (`[aria-expanded="true"]`: `bleet-dropdown`, `bleet-profile`) closes
|
|
182
|
+
that menu, not the dialog.
|
|
149
183
|
- Statuses `Opening`, `Opened`, `Closing`, `Closed` (with the `id`) on `Channels.ModalStatus`.
|
|
150
184
|
|
|
151
185
|
```typescript
|
|
152
186
|
interface IDialogResponse {
|
|
153
187
|
content?: string; // the whole HTML of the dialog
|
|
154
|
-
color?: UiColor; // not used by the element: the server HTML carries its own colors
|
|
155
188
|
icon?: string | null; // not used by the element: the server HTML carries its own icon
|
|
156
189
|
action?: DialogAction; // Close closes the dialog, RefreshAndClose closes it and reloads the page
|
|
157
190
|
toast?: IToast; // published on Channels.Toaster
|
|
@@ -161,7 +194,45 @@ interface IDialogResponse {
|
|
|
161
194
|
}
|
|
162
195
|
```
|
|
163
196
|
|
|
164
|
-
PHP side, `AureliaCommunication::dialogContent($content
|
|
197
|
+
PHP side, `AureliaCommunication::dialogContent($content)` builds the `content` part of it: the look of the dialog is
|
|
198
|
+
in its HTML.
|
|
199
|
+
|
|
200
|
+
**In a SPA**, the frame is the application's: its element extends `AbstractDialog` (see
|
|
201
|
+
[Abstract components](#abstract-components)), its template holds the `<dialog ref="dialogElement">`, and the dialog is
|
|
202
|
+
opened without `url`:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
@customElement('app-drawer')
|
|
206
|
+
export class AppDrawer extends AbstractDialog {}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```html
|
|
210
|
+
<template data-dialog-show="translate-x-0" data-dialog-hide="translate-x-full">
|
|
211
|
+
<dialog ref="dialogElement" class="… translate-x-full" aria-label.bind="title || null">
|
|
212
|
+
<header …>${title} <button type="button" data-dialog="close">…</button></header>
|
|
213
|
+
<au-slot></au-slot>
|
|
214
|
+
</dialog>
|
|
215
|
+
</template>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
resolve(IEventAggregator).publish(Channels.Modal, <IModal>{ action: ModalAction.Open, id: 'drawer', title: 'Edit user' });
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
| `AbstractDialog` | Meaning |
|
|
223
|
+
|---|---|
|
|
224
|
+
| `id` (bindable) | the id of the dialog on `Channels.Modal` |
|
|
225
|
+
| `title` | the one of the opening, kept until the dialog is closed |
|
|
226
|
+
| `dialogElement` | the `<dialog>` of the template (`ref`) |
|
|
227
|
+
| `show(data)`, `close()`, `clear()` | protected, to extend: an opening, a closing, what to clear once closed |
|
|
228
|
+
|
|
229
|
+
- `Open` (or `Toggle` when closed) opens the dialog; Escape, any `[data-dialog="close"]`, `Close` and `Toggle` close it
|
|
230
|
+
through the hide transition — a menu open inside (`aria-expanded`) is closed first.
|
|
231
|
+
- `Open` on a dialog already open only renews its `title`: the page shown in it changed (a creation turned into its
|
|
232
|
+
edition), the dialog stays.
|
|
233
|
+
- What the slot holds belongs to the page that projects it, its forms included.
|
|
234
|
+
- `ModalStatus.Closed` with the `id` tells the page the dialog is closed (its route can be left).
|
|
235
|
+
- `<bleet-dialog>` itself extends `AbstractDialog`, with the server HTML on top of it.
|
|
165
236
|
|
|
166
237
|
## Ajaxify
|
|
167
238
|
|
|
@@ -216,7 +287,7 @@ Markup it expects:
|
|
|
216
287
|
<div bleet-toaster popover="manual"
|
|
217
288
|
class="fixed inset-auto top-4 right-0 z-70 m-0 p-0 pr-4 border-0 bg-transparent overflow-visible flex flex-col gap-4 pointer-events-none">
|
|
218
289
|
|
|
219
|
-
<!-- one template per
|
|
290
|
+
<!-- one template per UiLevel: info, success, warning, danger -->
|
|
220
291
|
<template data-toaster="success">
|
|
221
292
|
<div class="… translate-x-full opacity-0 transition-all duration-500"
|
|
222
293
|
data-toaster-show="translate-x-0 opacity-100"
|
|
@@ -242,24 +313,24 @@ box of a popover (`inset`, `margin`, `padding`, `border`, `background`, `overflo
|
|
|
242
313
|
|
|
243
314
|
Behavior:
|
|
244
315
|
|
|
245
|
-
- `ToasterAction.Add`: clones `template[data-toaster=<
|
|
316
|
+
- `ToasterAction.Add`: clones `template[data-toaster=<level>]`, puts the icon of
|
|
246
317
|
`template[data-toaster=icon-<icon>]` in the `icon` slot, fills `title` and `content`, appends the card to the
|
|
247
318
|
container, then swaps its hide classes for its show classes. With `duration` > 0 the toast closes itself after
|
|
248
319
|
`duration` ms; `0` keeps it until it is closed.
|
|
249
320
|
- `ToasterAction.Remove` (or a click on `close`): swaps the show classes for the hide classes, waits for the end
|
|
250
321
|
of the transition, removes the card.
|
|
251
|
-
- A toast whose `id` is already displayed is ignored. A
|
|
322
|
+
- A toast whose `id` is already displayed is ignored. A level without template is logged and ignored.
|
|
252
323
|
|
|
253
324
|
Publishing a toast from TypeScript:
|
|
254
325
|
|
|
255
326
|
```typescript
|
|
256
327
|
import { resolve, IEventAggregator } from 'aurelia';
|
|
257
|
-
import { Channels, IToaster, ToasterAction,
|
|
328
|
+
import { Channels, IToaster, ToasterAction, UiLevel, UiToastIcon } from '@blackcube/aurelia-kit';
|
|
258
329
|
|
|
259
330
|
resolve(IEventAggregator).publish(Channels.Toaster, <IToaster>{
|
|
260
331
|
action: ToasterAction.Add,
|
|
261
332
|
toast: {
|
|
262
|
-
|
|
333
|
+
level: UiLevel.Success,
|
|
263
334
|
icon: UiToastIcon.Success,
|
|
264
335
|
title: 'Saved',
|
|
265
336
|
content: 'The page has been saved.',
|
|
@@ -276,7 +347,7 @@ interface IToaster {
|
|
|
276
347
|
|
|
277
348
|
interface IToast {
|
|
278
349
|
id?: string; // generated when absent
|
|
279
|
-
|
|
350
|
+
level: UiLevel; // default Info
|
|
280
351
|
icon?: UiToastIcon; // default Info
|
|
281
352
|
title?: string;
|
|
282
353
|
content: string; // HTML
|
|
@@ -290,20 +361,57 @@ Publishes one toast when the page loads (after `DOMContentLoaded`). Rendered by
|
|
|
290
361
|
as a hidden element:
|
|
291
362
|
|
|
292
363
|
```html
|
|
293
|
-
<div hidden bleet-toast="
|
|
364
|
+
<div hidden bleet-toast="level: success; icon: check-circle; title: Saved; content: Done; duration: 3000"></div>
|
|
294
365
|
```
|
|
295
366
|
|
|
296
|
-
Options: `id`, `
|
|
367
|
+
Options: `id`, `level`, `icon`, `title`, `content`, `duration` (same meaning as `IToast`).
|
|
297
368
|
|
|
298
369
|
### `bleet-toaster-trigger`
|
|
299
370
|
|
|
300
371
|
Publishes one toast on click. Rendered by `Bleet::toast()->…->trigger()` as attributes of another element:
|
|
301
372
|
|
|
302
373
|
```html
|
|
303
|
-
<button bleet-toaster-trigger="
|
|
374
|
+
<button bleet-toaster-trigger="level: success; content: Saved!">Save</button>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Options: `id`, `level`, `icon`, `title`, `content`, `duration`.
|
|
378
|
+
|
|
379
|
+
### `AbstractToaster` (SPA)
|
|
380
|
+
|
|
381
|
+
The toaster of a SPA, where the application renders the markup: the same channel, the same payload and the same state
|
|
382
|
+
classes as the `bleet-toaster` attribute, each toast rendered by the template of the application's element, which
|
|
383
|
+
extends `AbstractToaster` (see [Abstract components](#abstract-components)). The host is the stack.
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
@customElement('app-toaster')
|
|
387
|
+
export class AppToaster extends AbstractToaster {}
|
|
304
388
|
```
|
|
305
389
|
|
|
306
|
-
|
|
390
|
+
```html
|
|
391
|
+
<template popover="manual" class="fixed inset-auto top-4 right-0 … flex flex-col gap-4 pointer-events-none">
|
|
392
|
+
<div repeat.for="toast of toasts" data-toaster-id.bind="toast.id"
|
|
393
|
+
class="… translate-x-full opacity-0 transition-all duration-500"
|
|
394
|
+
data-toaster-show="translate-x-0 opacity-100"
|
|
395
|
+
data-toaster-hide="translate-x-full opacity-0">
|
|
396
|
+
… ${toast.title} … <div innerhtml.bind="toast.content"></div> …
|
|
397
|
+
<button type="button" click.trigger="onClickClose($event, toast.id)">…</button>
|
|
398
|
+
</div>
|
|
399
|
+
</template>
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
| `AbstractToaster` | Meaning |
|
|
403
|
+
|---|---|
|
|
404
|
+
| `toasts` | the toasts shown: `id`, `level`, `icon`, `title`, `content`, `duration`, with the defaults of the attribute |
|
|
405
|
+
| `onClickClose(event, id)` | closes the toast (its button) |
|
|
406
|
+
|
|
407
|
+
Behavior:
|
|
408
|
+
|
|
409
|
+
- The root of each rendered toast carries `data-toaster-id="${toast.id}"`: once rendered, its hide classes are
|
|
410
|
+
swapped for its show classes; its button, `ToasterAction.Remove` or the end of its `duration` swap them back, then the
|
|
411
|
+
toast leaves the list once the transition ends.
|
|
412
|
+
- With `popover="manual"` on the host, it is shown again on each toast and hidden once the last one is gone, as the
|
|
413
|
+
attribute's container.
|
|
414
|
+
- The texts, the icons and the classes are the application's: its i18n, its icon component, its theme.
|
|
307
415
|
|
|
308
416
|
## Alerts
|
|
309
417
|
|
|
@@ -390,7 +498,13 @@ Behavior:
|
|
|
390
498
|
|
|
391
499
|
- Listens on `Channels.Menu`: `MenuAction.Open` applies the show classes and opens the overlay, `MenuAction.Close`
|
|
392
500
|
applies the hide classes and closes the overlay, `MenuAction.Toggle` does one or the other. `aria-hidden` follows.
|
|
393
|
-
- A click on `[data-menu=close]` closes the menu; the menu closes too when the overlay closes
|
|
501
|
+
- A click on `[data-menu=close]` closes the menu; the menu closes too when the overlay closes, on `Escape` and on a
|
|
502
|
+
click on a link of the menu (the link is followed).
|
|
503
|
+
- Open, the focus is trapped in the menu (`ITrapFocusService`), on `[data-menu=close]` first; once closed, it goes back
|
|
504
|
+
to what opened the menu (the burger).
|
|
505
|
+
- The page follows: `<body>` takes the classes of the menu's `data-menu-page-show` while it is open and of its
|
|
506
|
+
`data-menu-page-hide` once closed — `data-menu-page-show="overflow-hidden"` keeps the page from scrolling under it.
|
|
507
|
+
Without these attributes, the page is left as is.
|
|
394
508
|
- Publishes `MenuStatus.Opening`, `Opened`, `Closing`, `Closed` on `Channels.MenuStatus`.
|
|
395
509
|
- `[data-menu=toggle-button-ID]` shows or hides `[data-menu=toggle-list-ID]` (`hidden` attribute), sets the
|
|
396
510
|
data-menu-show / data-menu-hide classes of its `svg[data-menu=icon]` and its `aria-expanded`. Accordion: opening a
|
|
@@ -459,6 +573,43 @@ Behavior:
|
|
|
459
573
|
select to `N`.
|
|
460
574
|
- Changing the select clicks the matching button.
|
|
461
575
|
|
|
576
|
+
### `AbstractPager` (SPA)
|
|
577
|
+
|
|
578
|
+
The pagination of a list in a SPA — bleet's `Pager` widget with buttons instead of links: the application's element
|
|
579
|
+
extends `AbstractPager` (see [Abstract components](#abstract-components)) and renders the window of numbers around the
|
|
580
|
+
current page. The pagination is `@blackcube/aurelia-api`'s (`IPagination`), given as it is by the page; a page chosen is
|
|
581
|
+
announced on the host (`PagerEvent.Change`, `bleet-page`, its `detail` an `IPagerDetail`): the page writes it in its URL
|
|
582
|
+
and reloads its list, whose new pagination comes back.
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
@customElement('app-pager')
|
|
586
|
+
export class AppPager extends AbstractPager {}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
```html
|
|
590
|
+
<template>
|
|
591
|
+
<nav if.bind="pagination.totalPages > 1">
|
|
592
|
+
<button if.bind="page > 1" type="button" click.trigger="onClickPage($event, page - 1)">…</button>
|
|
593
|
+
<button repeat.for="number of numbers" type="button" aria-current.bind="number === page ? 'page' : null"
|
|
594
|
+
click.trigger="onClickPage($event, number)">${number}</button>
|
|
595
|
+
<button if.bind="page < pagination.totalPages" type="button" click.trigger="onClickPage($event, page + 1)">…</button>
|
|
596
|
+
</nav>
|
|
597
|
+
</template>
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
```html
|
|
601
|
+
<app-pager pagination.bind="pagination" bleet-page.trigger="onChangePage($event)"></app-pager>
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
| `AbstractPager` | Meaning |
|
|
605
|
+
|---|---|
|
|
606
|
+
| `pagination` | `IPagination`: `limit`, `currentPage`, `totalPages`, `totalCount` |
|
|
607
|
+
| `maxButtonCount` | how many numbers at most (10) |
|
|
608
|
+
| `page`, `numbers`, `begin`, `end` | the page shown, the window of numbers around it, the range of the items shown |
|
|
609
|
+
| `selectedPage` | two-way with a dropdown of the pages (a narrow screen): a choice in it goes to that page |
|
|
610
|
+
| `onClickPage(event, number)` | a number, previous, next |
|
|
611
|
+
| `refreshed()` | protected, to extend: what the element adds once the pages are computed again |
|
|
612
|
+
|
|
462
613
|
## Forms
|
|
463
614
|
|
|
464
615
|
### `bleet-password`
|
|
@@ -538,6 +689,88 @@ Behavior:
|
|
|
538
689
|
is selected, the `count` template text beyond.
|
|
539
690
|
- The panel does not open while the native select is disabled.
|
|
540
691
|
|
|
692
|
+
### `AbstractDropdown` (SPA)
|
|
693
|
+
|
|
694
|
+
The dropdown of a SPA, driven by its data rather than by a native select: the application's element extends
|
|
695
|
+
`AbstractDropdown` (see [Abstract components](#abstract-components)) and renders its rows; the values are typed — an
|
|
696
|
+
id, an enum — and bound both ways.
|
|
697
|
+
|
|
698
|
+
```typescript
|
|
699
|
+
@customElement('app-select')
|
|
700
|
+
export class AppSelect<T> extends AbstractDropdown<T> {}
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
```html
|
|
704
|
+
<template>
|
|
705
|
+
<button ref="button" type="button" aria-expanded.bind="open ? 'true' : 'false'" click.trigger="onClickToggle($event)">
|
|
706
|
+
${selection.length > 0 ? selection[0].label : placeholder}
|
|
707
|
+
</button>
|
|
708
|
+
<div ref="panel" hidden.bind="open === false" role="listbox">
|
|
709
|
+
<input ref="search" value.bind="query">
|
|
710
|
+
<template repeat.for="group of groups">
|
|
711
|
+
<div if.bind="group.label">${group.label}</div>
|
|
712
|
+
<button repeat.for="row of group.rows" type="button" role="option" aria-selected.bind="row.selected ? 'true' : 'false'"
|
|
713
|
+
disabled.bind="row.disabled" click.trigger="onClickItem($event, row)">${row.label}</button>
|
|
714
|
+
</template>
|
|
715
|
+
<div if.bind="empty">…</div>
|
|
716
|
+
</div>
|
|
717
|
+
</template>
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
```html
|
|
721
|
+
<app-select options.bind="schools" value.two-way="model.schoolId" placeholder="Choose"></app-select>
|
|
722
|
+
<app-select multiple.bind="true" options.bind="roles" values.two-way="model.roles"></app-select>
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
| `AbstractDropdown` | Meaning |
|
|
726
|
+
|---|---|
|
|
727
|
+
| `options` | `IDropdownOption[]`: `value`, `label`, `group`, `disabled` |
|
|
728
|
+
| `value`, `values` | two-way: the choice, or the choices with `multiple` |
|
|
729
|
+
| `multiple`, `disabled`, `placeholder` | bindables |
|
|
730
|
+
| `open`, `query`, `empty` | the panel is open; the text searched (the search field binds it); nothing matches |
|
|
731
|
+
| `groups`, `selection` | the rows matching the query by group (`IDropdownRow`: `value`, `label`, `disabled`, `selected`); the rows chosen |
|
|
732
|
+
| `button`, `panel`, `search` | the button, the panel and the optional search field of the template (`ref`) |
|
|
733
|
+
| `onClickToggle(event)`, `onClickItem(event, row)`, `onClickRemove(event, row)` | the button, a row, the remove control of a tag |
|
|
734
|
+
|
|
735
|
+
- A single choice sets `value` and closes the panel; with a `placeholder`, a first row sets it back to `null`. A
|
|
736
|
+
multiple one toggles in `values`, the panel staying open.
|
|
737
|
+
- The rows are built once per set of options; a choice changes their state in place, the rendered rows stay. A list
|
|
738
|
+
computed again with the same content (a new array, the same options) is not a new set: the rows stay too, and so
|
|
739
|
+
does the one that has the focus.
|
|
740
|
+
- Open, the focus is trapped in the panel (`ITrapFocusService`): Tab loops, the arrows move, Escape and a click outside
|
|
741
|
+
close it. It opens on the search field, else on its selection, scrolled into view.
|
|
742
|
+
- A remove control inside the button is not a `<button>`: the HTML parser closes a button at the next one.
|
|
743
|
+
|
|
744
|
+
### `bleet-focus`
|
|
745
|
+
|
|
746
|
+
An element that takes the focus as it appears — a field or a message shown later (a condition of the page, a fragment
|
|
747
|
+
loaded by ajaxify):
|
|
748
|
+
|
|
749
|
+
```html
|
|
750
|
+
<input bleet-focus …>
|
|
751
|
+
```
|
|
752
|
+
|
|
753
|
+
When attached, the element is focused on the next frame; the browser brings it into view.
|
|
754
|
+
|
|
755
|
+
### `bleet-first-error`
|
|
756
|
+
|
|
757
|
+
A form whose first field in error takes the focus when the page says « refused » (a SPA validating its form before
|
|
758
|
+
posting it):
|
|
759
|
+
|
|
760
|
+
```html
|
|
761
|
+
<form bleet-first-error="account" submit.trigger="onSubmit($event)" novalidate>
|
|
762
|
+
<input aria-invalid.bind="…" …>
|
|
763
|
+
</form>
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
`Channels.Form` carries `IForm` (`{ action: FormAction.FirstError, id?: string }`): the form whose `id` is given,
|
|
767
|
+
every form without id, focuses its first `[aria-invalid=true]` on the next frame — once the invalid marks of the fields
|
|
768
|
+
are rendered. A form without a field in error does nothing.
|
|
769
|
+
|
|
770
|
+
```typescript
|
|
771
|
+
resolve(IEventAggregator).publish(Channels.Form, <IForm>{ action: FormAction.FirstError, id: 'account' });
|
|
772
|
+
```
|
|
773
|
+
|
|
541
774
|
## Upload channel
|
|
542
775
|
|
|
543
776
|
`Channels.Upload` carries `IUpload` (`{ action: UploadAction.Reset, id?: string }`) for the upload bridges
|
|
@@ -550,7 +783,8 @@ resolve(IEventAggregator).publish(Channels.Upload, <IUpload>{ action: UploadActi
|
|
|
550
783
|
## Exports
|
|
551
784
|
|
|
552
785
|
Attributes: `BleetToasterCustomAttribute`, `BleetToastCustomAttribute`, `BleetToasterTriggerCustomAttribute`,
|
|
553
|
-
and the attributes of the other widgets.
|
|
786
|
+
and the attributes of the other widgets. Abstract components: `AbstractDialog`, `AbstractDropdown`, `AbstractPager` (its `PagerEvent`, `IPagerDetail`),
|
|
787
|
+
`AbstractToaster`. Enums: `Channels`, `ToasterAction`, `ToasterStatus`, `UiLevel`,
|
|
554
788
|
`UiToastIcon`, `UiIcon`, and the actions and statuses of the other channels. Interfaces: `IToaster`, `IToast`,
|
|
555
789
|
and the payloads of the other channels. Services: `IStorageService`, `ITransitionService`, `ITrapFocusService`.
|
|
556
790
|
|