@optionfactory/fml 9.0.0-rc14 → 9.0.0-rc15
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/dist/custom-elements.json +26 -0
- package/dist/fml.css +1 -0
- package/dist/fml.css.map +1 -1
- package/dist/fml.iife.js +215 -90
- package/dist/fml.iife.js.map +1 -1
- package/dist/fml.iife.min.js +1 -1
- package/dist/fml.iife.min.js.map +1 -1
- package/dist/ful.css +1 -0
- package/dist/ful.css.map +1 -1
- package/dist/ful.d.mts +36 -6
- package/dist/ful.iife.js +214 -90
- package/dist/ful.iife.js.map +1 -1
- package/dist/ful.iife.min.js +1 -1
- package/dist/ful.iife.min.js.map +1 -1
- package/dist/ful.min.mjs +1 -1
- package/dist/ful.min.mjs.map +1 -1
- package/dist/ful.mjs +214 -91
- package/dist/ful.mjs.map +1 -1
- package/dist/vscode.html-custom-data.json +10 -0
- package/dist/web-types.json +17 -1
- package/package.json +1 -1
package/dist/ful.mjs
CHANGED
|
@@ -2304,7 +2304,7 @@ const unplace = (popover) => {
|
|
|
2304
2304
|
|
|
2305
2305
|
const showOncePlaced = (popover, anchored) => {
|
|
2306
2306
|
popover.style.visibility = 'hidden';
|
|
2307
|
-
|
|
2307
|
+
queueMicrotask(() => {
|
|
2308
2308
|
if (popover.matches(':popover-open')) {
|
|
2309
2309
|
place(popover, anchored);
|
|
2310
2310
|
}
|
|
@@ -2443,8 +2443,9 @@ class Anchors {
|
|
|
2443
2443
|
* start-aligned, clamped into the viewport; `flip` moves it above the
|
|
2444
2444
|
* anchor where the viewport leaves no room below, as the css
|
|
2445
2445
|
* `position-try-fallbacks` the paired popovers declare. A popover not
|
|
2446
|
-
* already open is shown before
|
|
2447
|
-
* sees is the right one
|
|
2446
|
+
* already open is shown and placed before the call returns, so the first
|
|
2447
|
+
* position anybody sees is the right one and the caller can move the focus
|
|
2448
|
+
* into it on the next line.
|
|
2448
2449
|
*
|
|
2449
2450
|
* @param {HTMLElement} popover the `[popover]` element to show, or an
|
|
2450
2451
|
* already-shown element to place
|
|
@@ -2470,16 +2471,10 @@ class Anchors {
|
|
|
2470
2471
|
popover.style.left = `${clamp(box.left, PAD, Math.max(PAD, viewport.clientWidth - here.width - PAD))}px`;
|
|
2471
2472
|
popover.style.top = `${clamp(top, PAD, Math.max(PAD, viewport.clientHeight - here.height - PAD))}px`;
|
|
2472
2473
|
};
|
|
2473
|
-
if (popover.matches(':popover-open')
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
popover.style.visibility = 'hidden';
|
|
2478
|
-
popover.showPopover();
|
|
2479
|
-
requestAnimationFrame(() => {
|
|
2480
|
-
placeShown();
|
|
2481
|
-
popover.style.removeProperty('visibility');
|
|
2482
|
-
});
|
|
2474
|
+
if (typeof popover.showPopover === 'function' && !popover.matches(':popover-open')) {
|
|
2475
|
+
popover.showPopover();
|
|
2476
|
+
}
|
|
2477
|
+
placeShown();
|
|
2483
2478
|
return popover;
|
|
2484
2479
|
}
|
|
2485
2480
|
}
|
|
@@ -2771,6 +2766,11 @@ class Dropdown extends ParsedElement {
|
|
|
2771
2766
|
//from the host when it gave one, since it has to set aria-controls before
|
|
2772
2767
|
//this element upgrades
|
|
2773
2768
|
this.#menu.id = this.declared('listbox') || Attributes.uid('ful-listbox');
|
|
2769
|
+
this.#menu.addEventListener('mousedown', (evt) => {
|
|
2770
|
+
if (evt.target.closest('li')) {
|
|
2771
|
+
evt.preventDefault();
|
|
2772
|
+
}
|
|
2773
|
+
});
|
|
2774
2774
|
this.#menu.addEventListener('click', (evt) => {
|
|
2775
2775
|
evt.stopPropagation();
|
|
2776
2776
|
const li = evt.target.closest('li');
|
|
@@ -2778,7 +2778,7 @@ class Dropdown extends ParsedElement {
|
|
|
2778
2778
|
this.hide();
|
|
2779
2779
|
return;
|
|
2780
2780
|
}
|
|
2781
|
-
if (li.
|
|
2781
|
+
if (li.matches('[aria-disabled="true"]')) {
|
|
2782
2782
|
return;
|
|
2783
2783
|
}
|
|
2784
2784
|
this.#change(li);
|
|
@@ -2787,8 +2787,8 @@ class Dropdown extends ParsedElement {
|
|
|
2787
2787
|
}
|
|
2788
2788
|
#selected() {
|
|
2789
2789
|
return (
|
|
2790
|
-
this.#menu?.querySelector('[selected]:not([aria-disabled])') ??
|
|
2791
|
-
this.#menu?.querySelector('li:not([aria-disabled])') ??
|
|
2790
|
+
this.#menu?.querySelector('[selected]:not([aria-disabled="true"])') ??
|
|
2791
|
+
this.#menu?.querySelector('li:not([aria-disabled="true"])') ??
|
|
2792
2792
|
null
|
|
2793
2793
|
);
|
|
2794
2794
|
}
|
|
@@ -2830,7 +2830,11 @@ class Dropdown extends ParsedElement {
|
|
|
2830
2830
|
li.setAttribute('aria-selected', picked ? 'true' : 'false');
|
|
2831
2831
|
//metadata.disabled refuses the entry, metadata.reason saying why
|
|
2832
2832
|
const disabled = !!entry?.metadata?.disabled;
|
|
2833
|
-
|
|
2833
|
+
if (disabled) {
|
|
2834
|
+
li.setAttribute('aria-disabled', 'true');
|
|
2835
|
+
} else {
|
|
2836
|
+
li.removeAttribute('aria-disabled');
|
|
2837
|
+
}
|
|
2834
2838
|
if (disabled && entry.metadata.reason) {
|
|
2835
2839
|
li.setAttribute('title', String(entry.metadata.reason));
|
|
2836
2840
|
} else {
|
|
@@ -2919,7 +2923,7 @@ class Dropdown extends ParsedElement {
|
|
|
2919
2923
|
}
|
|
2920
2924
|
#walk(from, forward) {
|
|
2921
2925
|
for (let li = from; li; li = li[`${forward ? 'next' : 'previous'}ElementSibling`]) {
|
|
2922
|
-
if (!li.
|
|
2926
|
+
if (!li.matches('[aria-disabled="true"]')) {
|
|
2923
2927
|
return li;
|
|
2924
2928
|
}
|
|
2925
2929
|
}
|
|
@@ -4363,15 +4367,110 @@ class Table extends ParsedElement {
|
|
|
4363
4367
|
}
|
|
4364
4368
|
}
|
|
4365
4369
|
|
|
4370
|
+
/** how long a type-ahead buffer stands before the next character starts a new search */
|
|
4371
|
+
const TYPEAHEAD_WINDOW = 500;
|
|
4372
|
+
|
|
4373
|
+
/**
|
|
4374
|
+
* The keyboard protocol of a menu popover: the focus moving into the menu as it
|
|
4375
|
+
* opens and back to the invoker as it closes, roving focus over the items with
|
|
4376
|
+
* the arrows, Home and End, type-ahead over their text, and Enter and Space
|
|
4377
|
+
* activating the focused one.
|
|
4378
|
+
*
|
|
4379
|
+
* The menu owns what an item is: `items` answers them in the order they are
|
|
4380
|
+
* walked, read on every gesture so a menu that fills itself later needs no
|
|
4381
|
+
* rewiring, and `current` names the one an opening focuses, the first otherwise.
|
|
4382
|
+
*
|
|
4383
|
+
* @param {HTMLElement} invoker the element the menu belongs to, and where the focus goes back
|
|
4384
|
+
* @param {HTMLElement} menu the `[popover]` holding the items
|
|
4385
|
+
* @param {{items: () => HTMLElement[], current?: () => HTMLElement|null|undefined}} conf
|
|
4386
|
+
*/
|
|
4387
|
+
const wireMenuKeys = (invoker, menu, { items, current = () => null }) => {
|
|
4388
|
+
let typed = '';
|
|
4389
|
+
let typedAt = 0;
|
|
4390
|
+
menu.addEventListener('toggle', (/** @type any */ evt) => {
|
|
4391
|
+
if (evt.newState !== 'open') {
|
|
4392
|
+
//give the invoker back the focus the menu had borrowed, without
|
|
4393
|
+
//stealing it from wherever else the close came from
|
|
4394
|
+
if (menu.contains(document.activeElement)) {
|
|
4395
|
+
invoker.focus();
|
|
4396
|
+
}
|
|
4397
|
+
return;
|
|
4398
|
+
}
|
|
4399
|
+
const all = items();
|
|
4400
|
+
(current() ?? all[0])?.focus();
|
|
4401
|
+
});
|
|
4402
|
+
menu.addEventListener('keydown', (evt) => {
|
|
4403
|
+
const target = /** @type HTMLElement */ (evt.target);
|
|
4404
|
+
const all = items();
|
|
4405
|
+
const item = all.find((candidate) => candidate === target || candidate.contains(target));
|
|
4406
|
+
if (!item) {
|
|
4407
|
+
return;
|
|
4408
|
+
}
|
|
4409
|
+
const at = all.indexOf(item);
|
|
4410
|
+
switch (evt.code) {
|
|
4411
|
+
case 'ArrowDown': {
|
|
4412
|
+
evt.preventDefault();
|
|
4413
|
+
all[(at + 1) % all.length]?.focus();
|
|
4414
|
+
return;
|
|
4415
|
+
}
|
|
4416
|
+
case 'ArrowUp': {
|
|
4417
|
+
evt.preventDefault();
|
|
4418
|
+
all[(at - 1 + all.length) % all.length]?.focus();
|
|
4419
|
+
return;
|
|
4420
|
+
}
|
|
4421
|
+
case 'Home': {
|
|
4422
|
+
evt.preventDefault();
|
|
4423
|
+
all[0]?.focus();
|
|
4424
|
+
return;
|
|
4425
|
+
}
|
|
4426
|
+
case 'End': {
|
|
4427
|
+
evt.preventDefault();
|
|
4428
|
+
all[all.length - 1]?.focus();
|
|
4429
|
+
return;
|
|
4430
|
+
}
|
|
4431
|
+
case 'Enter':
|
|
4432
|
+
case 'Space': {
|
|
4433
|
+
evt.preventDefault();
|
|
4434
|
+
item.click();
|
|
4435
|
+
invoker.focus();
|
|
4436
|
+
return;
|
|
4437
|
+
}
|
|
4438
|
+
case 'Escape': {
|
|
4439
|
+
//the platform's close request hides the menu, the focus is placed
|
|
4440
|
+
//on the invoker before the focused item is detached from it
|
|
4441
|
+
invoker.focus();
|
|
4442
|
+
return;
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
if (evt.key.length !== 1 || evt.ctrlKey || evt.altKey || evt.metaKey) {
|
|
4446
|
+
return;
|
|
4447
|
+
}
|
|
4448
|
+
const now = Date.now();
|
|
4449
|
+
typed = now - typedAt > TYPEAHEAD_WINDOW ? evt.key : typed + evt.key;
|
|
4450
|
+
typedAt = now;
|
|
4451
|
+
const wanted = typed.toLowerCase();
|
|
4452
|
+
const repeated = [...wanted].every((character) => character === wanted[0]);
|
|
4453
|
+
const search = repeated ? wanted[0] : wanted;
|
|
4454
|
+
const from = repeated ? at + 1 : at;
|
|
4455
|
+
const found = [...all.slice(from), ...all.slice(0, from)].find((candidate) =>
|
|
4456
|
+
(candidate.textContent ?? '').trim().toLowerCase().startsWith(search),
|
|
4457
|
+
);
|
|
4458
|
+
if (found) {
|
|
4459
|
+
evt.preventDefault();
|
|
4460
|
+
found.focus();
|
|
4461
|
+
}
|
|
4462
|
+
});
|
|
4463
|
+
};
|
|
4464
|
+
|
|
4366
4465
|
/**
|
|
4367
4466
|
* An invoker button paired with the `ul[popover][role=menu]` that follows it:
|
|
4368
4467
|
* the chrome behind every filter's operator, sensitivity and boolean value.
|
|
4369
4468
|
*
|
|
4370
4469
|
* It fills the menu from a vocabulary, wires it the first time more than one
|
|
4371
4470
|
* choice survives the whitelist, pins the button to a static glyph when a
|
|
4372
|
-
* single one does,
|
|
4373
|
-
*
|
|
4374
|
-
*
|
|
4471
|
+
* single one does, takes the shared menu keyboard protocol, and keeps the
|
|
4472
|
+
* button's value, glyph and aria-label in step. A pick that changes the value
|
|
4473
|
+
* calls back; the host decides what that means.
|
|
4375
4474
|
*
|
|
4376
4475
|
* The button's `value` attribute is the store, as it is for a native control:
|
|
4377
4476
|
* the menu protocol finds the current item by it, and nothing mirrors it.
|
|
@@ -4529,64 +4628,10 @@ class ChoiceButton {
|
|
|
4529
4628
|
return Array.from(this.#menu.querySelectorAll('li > a'), (a) => /** @type HTMLAnchorElement */ (a));
|
|
4530
4629
|
}
|
|
4531
4630
|
#wire() {
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
if (evt.newState !== 'open') {
|
|
4537
|
-
//give the invoker back the focus the menu had borrowed, without
|
|
4538
|
-
//stealing it from wherever else the close came from
|
|
4539
|
-
if (menu.contains(document.activeElement)) {
|
|
4540
|
-
button.focus();
|
|
4541
|
-
}
|
|
4542
|
-
return;
|
|
4543
|
-
}
|
|
4544
|
-
const items = this.#items();
|
|
4545
|
-
(items.find((a) => a.getAttribute('value') === this.value) ?? items[0])?.focus();
|
|
4546
|
-
});
|
|
4547
|
-
menu.addEventListener('keydown', (evt) => {
|
|
4548
|
-
const target = /** @type HTMLElement */ (evt.target);
|
|
4549
|
-
const item = /** @type HTMLAnchorElement | null */ (target.closest('li > a'));
|
|
4550
|
-
if (!item) {
|
|
4551
|
-
return;
|
|
4552
|
-
}
|
|
4553
|
-
const items = this.#items();
|
|
4554
|
-
const at = items.indexOf(item);
|
|
4555
|
-
switch (evt.code) {
|
|
4556
|
-
case 'ArrowDown': {
|
|
4557
|
-
evt.preventDefault();
|
|
4558
|
-
items[(at + 1) % items.length]?.focus();
|
|
4559
|
-
break;
|
|
4560
|
-
}
|
|
4561
|
-
case 'ArrowUp': {
|
|
4562
|
-
evt.preventDefault();
|
|
4563
|
-
items[(at - 1 + items.length) % items.length]?.focus();
|
|
4564
|
-
break;
|
|
4565
|
-
}
|
|
4566
|
-
case 'Home': {
|
|
4567
|
-
evt.preventDefault();
|
|
4568
|
-
items[0]?.focus();
|
|
4569
|
-
break;
|
|
4570
|
-
}
|
|
4571
|
-
case 'End': {
|
|
4572
|
-
evt.preventDefault();
|
|
4573
|
-
items[items.length - 1]?.focus();
|
|
4574
|
-
break;
|
|
4575
|
-
}
|
|
4576
|
-
case 'Enter':
|
|
4577
|
-
case 'Space': {
|
|
4578
|
-
evt.preventDefault();
|
|
4579
|
-
item.click();
|
|
4580
|
-
button.focus();
|
|
4581
|
-
break;
|
|
4582
|
-
}
|
|
4583
|
-
case 'Escape': {
|
|
4584
|
-
//the platform's close request hides the menu, the focus is placed
|
|
4585
|
-
//on the invoker before the focused item is detached from it
|
|
4586
|
-
button.focus();
|
|
4587
|
-
break;
|
|
4588
|
-
}
|
|
4589
|
-
}
|
|
4631
|
+
Anchors.wire(this.#button, this.#menu, { prefix: 'ful-filter-menu', invoke: true, expanded: true });
|
|
4632
|
+
wireMenuKeys(this.#button, this.#menu, {
|
|
4633
|
+
items: () => this.#items(),
|
|
4634
|
+
current: () => this.#items().find((a) => a.getAttribute('value') === this.value),
|
|
4590
4635
|
});
|
|
4591
4636
|
}
|
|
4592
4637
|
}
|
|
@@ -5561,15 +5606,22 @@ class Dialog extends ParsedElement {
|
|
|
5561
5606
|
*
|
|
5562
5607
|
* The body is text or a node: a string is never read as markup, an
|
|
5563
5608
|
* `innerHTML` sink being a thing the library does not carry. Each button is
|
|
5564
|
-
* a `[result, label, className?]` tuple
|
|
5609
|
+
* a `[result, label, className?]` tuple rendered as its answer, or a node
|
|
5610
|
+
* the caller has built itself, which is how a button carries anything a
|
|
5611
|
+
* tuple cannot say and how a footer gets a spacer.
|
|
5565
5612
|
* @param {string} header
|
|
5566
5613
|
* @param {string|Node} body
|
|
5567
|
-
* @param {[string, string, string?][]} [buttons]
|
|
5614
|
+
* @param {([string, string, string?]|Node)[]} [buttons]
|
|
5615
|
+
* @param {{ className?: string }} [options] `className` dresses the dialog,
|
|
5616
|
+
* the way a declared `ful-dialog` is given one to size it
|
|
5568
5617
|
* @returns {Promise<DialogOutcome>}
|
|
5569
5618
|
*/
|
|
5570
|
-
static async ask(header, body, buttons = []) {
|
|
5619
|
+
static async ask(header, body, buttons = [], { className } = {}) {
|
|
5571
5620
|
const dialog = document.createElement('ful-dialog');
|
|
5572
5621
|
dialog.setAttribute('header', header);
|
|
5622
|
+
if (className) {
|
|
5623
|
+
dialog.className = className;
|
|
5624
|
+
}
|
|
5573
5625
|
if (typeof body === 'string') {
|
|
5574
5626
|
dialog.textContent = body;
|
|
5575
5627
|
} else if (body) {
|
|
@@ -5580,12 +5632,17 @@ class Dialog extends ParsedElement {
|
|
|
5580
5632
|
if (buttons.length > 0) {
|
|
5581
5633
|
const choices = document.createElement('template');
|
|
5582
5634
|
choices.setAttribute('slot', 'buttons');
|
|
5583
|
-
for (const
|
|
5635
|
+
for (const choice of buttons) {
|
|
5636
|
+
if (choice instanceof Node) {
|
|
5637
|
+
choices.content.append(choice);
|
|
5638
|
+
continue;
|
|
5639
|
+
}
|
|
5640
|
+
const [result, label, buttonClass] = choice;
|
|
5584
5641
|
const button = document.createElement('button');
|
|
5585
5642
|
button.type = 'button';
|
|
5586
5643
|
button.dataset.result = result;
|
|
5587
|
-
if (
|
|
5588
|
-
button.className =
|
|
5644
|
+
if (buttonClass) {
|
|
5645
|
+
button.className = buttonClass;
|
|
5589
5646
|
}
|
|
5590
5647
|
button.textContent = label;
|
|
5591
5648
|
choices.content.append(button);
|
|
@@ -5607,13 +5664,19 @@ class Dialog extends ParsedElement {
|
|
|
5607
5664
|
* @param {string} header
|
|
5608
5665
|
* @param {string|Node} body
|
|
5609
5666
|
* @param {{ confirm?: string, cancel?: string }} [labels] overriding the localized defaults
|
|
5667
|
+
* @param {{ className?: string }} [options] as `ask` takes them
|
|
5610
5668
|
* @returns {Promise<boolean>}
|
|
5611
5669
|
*/
|
|
5612
|
-
static async confirm(header, body, labels = {}) {
|
|
5613
|
-
const outcome = await Dialog.ask(
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5670
|
+
static async confirm(header, body, labels = {}, options = {}) {
|
|
5671
|
+
const outcome = await Dialog.ask(
|
|
5672
|
+
header,
|
|
5673
|
+
body,
|
|
5674
|
+
[
|
|
5675
|
+
['cancel', labels.cancel ?? Localization.of().t('dialog.cancel')],
|
|
5676
|
+
['confirm', labels.confirm ?? Localization.of().t('dialog.confirm'), 'ful-button'],
|
|
5677
|
+
],
|
|
5678
|
+
options,
|
|
5679
|
+
);
|
|
5617
5680
|
return !outcome.dismissed && outcome.result === 'confirm';
|
|
5618
5681
|
}
|
|
5619
5682
|
}
|
|
@@ -5820,6 +5883,65 @@ class Drawer extends ParsedElement {
|
|
|
5820
5883
|
}
|
|
5821
5884
|
}
|
|
5822
5885
|
|
|
5886
|
+
/**
|
|
5887
|
+
* A menu of commands on the native popover platform: the invoker named by
|
|
5888
|
+
* `for` toggles it and light dismiss and Escape come from the browser, while
|
|
5889
|
+
* the element adds the menu semantics and the keyboard protocol.
|
|
5890
|
+
*
|
|
5891
|
+
* The items are the buttons and links written inside it, kept as they are and
|
|
5892
|
+
* given their menu role and their place in the roving focus. Activation is the
|
|
5893
|
+
* item's own click, which the element only follows by closing.
|
|
5894
|
+
*/
|
|
5895
|
+
class Menu extends ParsedElement {
|
|
5896
|
+
static attributes = ['for'];
|
|
5897
|
+
render() {
|
|
5898
|
+
const named = this.declared('for');
|
|
5899
|
+
const invoker = named ? document.getElementById(named) : null;
|
|
5900
|
+
if (!invoker) {
|
|
5901
|
+
throw new Error(
|
|
5902
|
+
`ful-menu names no invoker: for="${named ?? ''}" must name an element already in the page when the menu upgrades`,
|
|
5903
|
+
);
|
|
5904
|
+
}
|
|
5905
|
+
this.setAttribute('role', 'menu');
|
|
5906
|
+
if (!this.hasAttribute('popover')) {
|
|
5907
|
+
this.setAttribute('popover', '');
|
|
5908
|
+
}
|
|
5909
|
+
invoker.setAttribute('aria-haspopup', 'menu');
|
|
5910
|
+
Anchors.wire(invoker, this, { prefix: 'ful-menu', invoke: true, expanded: true });
|
|
5911
|
+
wireMenuKeys(invoker, this, { items: () => this.items });
|
|
5912
|
+
this.#mark();
|
|
5913
|
+
this.addEventListener('click', (evt) => {
|
|
5914
|
+
const target = /** @type HTMLElement */ (evt.target);
|
|
5915
|
+
if (this.items.some((item) => item === target || item.contains(target))) {
|
|
5916
|
+
this.hidePopover();
|
|
5917
|
+
}
|
|
5918
|
+
});
|
|
5919
|
+
}
|
|
5920
|
+
/**
|
|
5921
|
+
* The commands, in the order the arrows walk them: the menu's own buttons
|
|
5922
|
+
* and links, minus the disabled and hidden ones, which keep their role and
|
|
5923
|
+
* leave the walk.
|
|
5924
|
+
*/
|
|
5925
|
+
get items() {
|
|
5926
|
+
return this.#mark().filter((item) => !item.hasAttribute('disabled') && !item.hasAttribute('hidden'));
|
|
5927
|
+
}
|
|
5928
|
+
/**
|
|
5929
|
+
* Gives every button and link of the menu its item role and takes it out of
|
|
5930
|
+
* the tab order, and answers them. Read on every gesture as well as at the
|
|
5931
|
+
* render, so a menu a page fills or empties later needs no rewiring.
|
|
5932
|
+
*/
|
|
5933
|
+
#mark() {
|
|
5934
|
+
const all = /** @type HTMLElement[] */ ([...this.querySelectorAll(':scope > button, :scope > a[href]')]);
|
|
5935
|
+
for (const item of all) {
|
|
5936
|
+
if (!item.hasAttribute('role')) {
|
|
5937
|
+
item.setAttribute('role', 'menuitem');
|
|
5938
|
+
}
|
|
5939
|
+
item.tabIndex = -1;
|
|
5940
|
+
}
|
|
5941
|
+
return all;
|
|
5942
|
+
}
|
|
5943
|
+
}
|
|
5944
|
+
|
|
5823
5945
|
const SEVERITIES = ['info', 'success', 'warning', 'error'];
|
|
5824
5946
|
|
|
5825
5947
|
//the regions alive in the document: the show-toast listener is wired once and
|
|
@@ -6465,6 +6587,7 @@ class Plugin {
|
|
|
6465
6587
|
.defineElement('ful-tooltip', Tooltip)
|
|
6466
6588
|
.defineElement('ful-dialog', Dialog)
|
|
6467
6589
|
.defineElement('ful-drawer', Drawer)
|
|
6590
|
+
.defineElement('ful-menu', Menu)
|
|
6468
6591
|
.defineElement('ful-toasts', Toasts)
|
|
6469
6592
|
.defineElement('ful-tabs', Tabs)
|
|
6470
6593
|
.defineElement('ful-accordion', Accordion)
|
|
@@ -6504,5 +6627,5 @@ class Plugin {
|
|
|
6504
6627
|
}
|
|
6505
6628
|
}
|
|
6506
6629
|
|
|
6507
|
-
export { Accordion, Anchors, AsyncEvents, Bindings, BooleanFilter, Checkbox, Claims, CompareFilter, Dialog, Drawer, Dropdown, Field, Form, FormLoader, InFilter, Input, InputFile, InputInstant, InputLocalDate, InputLocalTime, Instant, InstantFilter, LocalDate, LocalDateFilter, LocalStorage, NumberFilter, Pagination, Plugin, RadioGroup, Select, SelectLoader, SessionStorage, SortButton, Table, TableLoader, TableSchemaParser, Tabs, TextFilter, Timing, Toasts, Tooltip, VersionedLocalStorage, VersionedSessionStorage, Wizard, describable };
|
|
6630
|
+
export { Accordion, Anchors, AsyncEvents, Bindings, BooleanFilter, Checkbox, Claims, CompareFilter, Dialog, Drawer, Dropdown, Field, Form, FormLoader, InFilter, Input, InputFile, InputInstant, InputLocalDate, InputLocalTime, Instant, InstantFilter, LocalDate, LocalDateFilter, LocalStorage, Menu, NumberFilter, Pagination, Plugin, RadioGroup, Select, SelectLoader, SessionStorage, SortButton, Table, TableLoader, TableSchemaParser, Tabs, TextFilter, Timing, Toasts, Tooltip, VersionedLocalStorage, VersionedSessionStorage, Wizard, describable };
|
|
6508
6631
|
//# sourceMappingURL=ful.mjs.map
|