@crowdstrike/glide-core 0.31.0 → 0.31.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/dist/dropdown.js +2 -2
- package/dist/menu.js +52 -52
- package/dist/option.styles.js +3 -1
- package/dist/options.group.d.ts +30 -0
- package/dist/options.group.js +84 -0
- package/dist/options.group.styles.d.ts +2 -0
- package/dist/options.group.styles.js +31 -0
- package/dist/options.js +2 -1
- package/package.json +3 -3
package/dist/dropdown.js
CHANGED
@@ -1556,8 +1556,8 @@ let Dropdown = class Dropdown extends LitElement {
|
|
1556
1556
|
// Prevent page scroll. When filterable, also prevent the insertion point from
|
1557
1557
|
// moving to the beginning of the field.
|
1558
1558
|
event.preventDefault();
|
1559
|
-
const firstOption =
|
1560
|
-
.
|
1559
|
+
const firstOption = this.#optionElementsNotHiddenIncludingSelectAll
|
1560
|
+
.toReversed()
|
1561
1561
|
.findLast((option) => !option.disabled);
|
1562
1562
|
if (firstOption) {
|
1563
1563
|
this.#previouslyActiveOption = this.activeOption;
|
package/dist/menu.js
CHANGED
@@ -13,6 +13,7 @@ import { customElement, property } from 'lit/decorators.js';
|
|
13
13
|
import packageJson from '../package.json' with { type: 'json' };
|
14
14
|
import { LocalizeController } from './library/localize.js';
|
15
15
|
import Options from './options.js';
|
16
|
+
import OptionsGroup from './options.group.js';
|
16
17
|
import Option from './option.js';
|
17
18
|
import Input from './input.js';
|
18
19
|
import assertSlot from './library/assert-slot.js';
|
@@ -399,7 +400,9 @@ let Menu = class Menu extends LitElement {
|
|
399
400
|
.flatMap((element) => {
|
400
401
|
return element instanceof HTMLSlotElement
|
401
402
|
? element.assignedElements({ flatten: true })
|
402
|
-
: element
|
403
|
+
: element instanceof OptionsGroup
|
404
|
+
? [...element.children]
|
405
|
+
: element;
|
403
406
|
})
|
404
407
|
?.filter((element) => {
|
405
408
|
return element instanceof Option;
|
@@ -415,8 +418,14 @@ let Menu = class Menu extends LitElement {
|
|
415
418
|
// The "content" slot case.
|
416
419
|
':scope > glide-core-options > glide-core-option > [slot="content"] > glide-core-menu'),
|
417
420
|
...this.querySelectorAll(
|
421
|
+
// The "content" slot and Options Group case.
|
422
|
+
':scope > glide-core-options > glide-core-options-group > glide-core-option > [slot="content"] > glide-core-menu'),
|
423
|
+
...this.querySelectorAll(
|
418
424
|
// The "content" slot fallback case.
|
419
425
|
':scope > glide-core-options > glide-core-option > [slot="submenu"]'),
|
426
|
+
...this.querySelectorAll(
|
427
|
+
// The "content" slot fallback and Options Group case.
|
428
|
+
':scope > glide-core-options > glide-core-options-group > glide-core-option > [slot="submenu"]'),
|
420
429
|
];
|
421
430
|
}
|
422
431
|
get #targetElement() {
|
@@ -496,6 +505,15 @@ let Menu = class Menu extends LitElement {
|
|
496
505
|
if (this.#isSubMenu && event.target === this.#defaultSlotElementRef.value) {
|
497
506
|
event.stopPropagation();
|
498
507
|
}
|
508
|
+
// When Menu is in the shadow DOM of another component, `event.target` will be
|
509
|
+
// retargeted to the host of that component the moment the event bubbles out of
|
510
|
+
// it.
|
511
|
+
//
|
512
|
+
// So, when the timeout callback below is called, `event.target` will have been
|
513
|
+
// retargeted and the `instanceof` check will wrongly fail when an Option is
|
514
|
+
// clicked, and Menu will never close. So we store a reference to the original
|
515
|
+
// `event.target` and use it in the `instanceof` condition.
|
516
|
+
const originalEventTarget = event.target;
|
499
517
|
// The timeout gives consumers a chance to cancel the event to prevent Menu from
|
500
518
|
// closing.
|
501
519
|
setTimeout(() => {
|
@@ -510,7 +528,7 @@ let Menu = class Menu extends LitElement {
|
|
510
528
|
//
|
511
529
|
// When the default slot's padding is clicked, Menu should remain open because the
|
512
530
|
// user most likely meant to click an Option but missed.
|
513
|
-
if (!event.defaultPrevented &&
|
531
|
+
if (!event.defaultPrevented && originalEventTarget instanceof Option) {
|
514
532
|
this.open = false;
|
515
533
|
}
|
516
534
|
});
|
@@ -553,59 +571,41 @@ let Menu = class Menu extends LitElement {
|
|
553
571
|
event.preventDefault();
|
554
572
|
}
|
555
573
|
#onDefaultSlotMouseOver(event) {
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
//
|
562
|
-
//
|
563
|
-
//
|
564
|
-
//
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
// to keep an eye on: https://issues.chromium.org/issues/364669918.
|
570
|
-
const isOutOfBounds = this.#componentElementRef.value &&
|
571
|
-
event.y < this.#componentElementRef.value.getBoundingClientRect().y;
|
572
|
-
if (!isOutOfBounds) {
|
573
|
-
const option = event.target instanceof Element &&
|
574
|
-
event.target.closest('glide-core-option');
|
575
|
-
const isSubMenuTarget = event.target instanceof Element &&
|
576
|
-
event.target.closest('[slot="target"]');
|
577
|
-
const isOwnOption = option && this.#optionElements?.includes(option);
|
578
|
-
// This handler is also called when a sub-Menu Option is hovered because sub-Menu
|
579
|
-
// Option(s) are children of their super-Menu's default slot. And hovering a
|
580
|
-
// sub-Menu Option shouldn't deactivate the super-Menu's active Option. Thus
|
581
|
-
// `isOwnOption`.
|
582
|
-
if (isOwnOption && !isSubMenuTarget && !option.disabled) {
|
583
|
-
this.#previouslyActiveOption = this.#activeOption;
|
584
|
-
if (this.#activeOption) {
|
585
|
-
this.#activeOption.privateActive = false;
|
586
|
-
}
|
587
|
-
option.privateActive = true;
|
588
|
-
if (this.#optionsElement) {
|
589
|
-
this.#optionsElement.ariaActivedescendant = event.target.id;
|
590
|
-
}
|
591
|
-
}
|
592
|
-
if (this.#isSubMenu) {
|
593
|
-
// Allowing the event to propagate from a sub-Menu's parent Option means it would
|
594
|
-
// get picked up by the super-Menu Option's Tooltip "mouseover" handler. Then it
|
595
|
-
// would open the super-Menu's tooltip.
|
596
|
-
event.stopPropagation();
|
574
|
+
const option = event.target instanceof Element &&
|
575
|
+
event.target.closest('glide-core-option');
|
576
|
+
const isSubMenuTarget = event.target instanceof Element &&
|
577
|
+
event.target.closest('[slot="target"]');
|
578
|
+
const isOwnOption = option && this.#optionElements?.includes(option);
|
579
|
+
// This handler is also called when a sub-Menu Option is hovered because sub-Menu
|
580
|
+
// Option(s) are children of their super-Menu's default slot. And hovering a
|
581
|
+
// sub-Menu Option shouldn't deactivate the super-Menu's active Option. Thus
|
582
|
+
// `isOwnOption`.
|
583
|
+
if (isOwnOption && !isSubMenuTarget && !option.disabled) {
|
584
|
+
this.#previouslyActiveOption = this.#activeOption;
|
585
|
+
if (this.#activeOption) {
|
586
|
+
this.#activeOption.privateActive = false;
|
597
587
|
}
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
//
|
602
|
-
// The Option's tooltip will pick up both events and will remain open because the
|
603
|
-
// tooltip will be closed then immediately reopened. But we want the tooltip to
|
604
|
-
// close when a sub-Menu target is hovered. Canceling the event stops the tooltip
|
605
|
-
// from reopening.
|
606
|
-
event.preventDefault();
|
588
|
+
option.privateActive = true;
|
589
|
+
if (this.#optionsElement) {
|
590
|
+
this.#optionsElement.ariaActivedescendant = event.target.id;
|
607
591
|
}
|
608
592
|
}
|
593
|
+
if (this.#isSubMenu) {
|
594
|
+
// Allowing the event to propagate from a sub-Menu's parent Option means it would
|
595
|
+
// get picked up by the super-Menu Option's Tooltip "mouseover" handler. Then it
|
596
|
+
// would open the super-Menu's tooltip.
|
597
|
+
event.stopPropagation();
|
598
|
+
}
|
599
|
+
if (isSubMenuTarget && this.#activeOption) {
|
600
|
+
// When the cursor is already inside an Option and the user mouses to the Option's
|
601
|
+
// sub-Menu target, the browser will dispatch "mouseout" followed by "mouseover".
|
602
|
+
//
|
603
|
+
// The Option's tooltip will pick up both events and will remain open because the
|
604
|
+
// tooltip will be closed then immediately reopened. But we want the tooltip to
|
605
|
+
// close when a sub-Menu target is hovered. Canceling the event stops the tooltip
|
606
|
+
// from reopening.
|
607
|
+
event.preventDefault();
|
608
|
+
}
|
609
609
|
}
|
610
610
|
#onDefaultSlotMouseUp() {
|
611
611
|
this.#isDefaultSlotClick = true;
|
package/dist/option.styles.js
CHANGED
@@ -15,9 +15,11 @@ export default [
|
|
15
15
|
font-weight: var(--glide-core-typography-weight-regular);
|
16
16
|
inline-size: 100%;
|
17
17
|
max-inline-size: 21.875rem;
|
18
|
+
min-block-size: 1.75rem;
|
18
19
|
padding-block: var(--glide-core-spacing-base-xxs);
|
19
20
|
padding-inline: var(--glide-core-spacing-base-sm);
|
20
|
-
transition: background-color var(--glide-core-duration-fast-02)
|
21
|
+
transition: background-color var(--glide-core-duration-fast-02)
|
22
|
+
ease-in-out;
|
21
23
|
user-select: none;
|
22
24
|
|
23
25
|
&.active {
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import { LitElement } from 'lit';
|
2
|
+
declare global {
|
3
|
+
interface HTMLElementTagNameMap {
|
4
|
+
'glide-core-options-group': OptionsGroup;
|
5
|
+
}
|
6
|
+
}
|
7
|
+
/**
|
8
|
+
* @attr {string} label
|
9
|
+
* @attr {boolean} [hide-label=false]
|
10
|
+
* @attr {'group'|'optgroup'} [role='group']
|
11
|
+
*
|
12
|
+
* @readonly
|
13
|
+
* @attr {string} [version]
|
14
|
+
*
|
15
|
+
* @slot {Option}
|
16
|
+
*/
|
17
|
+
export default class OptionsGroup extends LitElement {
|
18
|
+
#private;
|
19
|
+
static shadowRootOptions: ShadowRootInit;
|
20
|
+
static styles: import("lit").CSSResult[];
|
21
|
+
/**
|
22
|
+
* @default undefined
|
23
|
+
*/
|
24
|
+
get label(): string | undefined;
|
25
|
+
set label(label: string);
|
26
|
+
hideLabel: boolean;
|
27
|
+
role: 'group' | 'optgroup';
|
28
|
+
readonly version: string;
|
29
|
+
render(): import("lit").TemplateResult<1>;
|
30
|
+
}
|
@@ -0,0 +1,84 @@
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
6
|
+
};
|
7
|
+
import { html, LitElement } from 'lit';
|
8
|
+
import { customElement, property } from 'lit/decorators.js';
|
9
|
+
import { classMap } from 'lit/directives/class-map.js';
|
10
|
+
import packageJson from '../package.json' with { type: 'json' };
|
11
|
+
import styles from './options.group.styles.js';
|
12
|
+
import shadowRootMode from './library/shadow-root-mode.js';
|
13
|
+
import final from './library/final.js';
|
14
|
+
import assertSlot from './library/assert-slot.js';
|
15
|
+
import Option from './option.js';
|
16
|
+
import required from './library/required.js';
|
17
|
+
/**
|
18
|
+
* @attr {string} label
|
19
|
+
* @attr {boolean} [hide-label=false]
|
20
|
+
* @attr {'group'|'optgroup'} [role='group']
|
21
|
+
*
|
22
|
+
* @readonly
|
23
|
+
* @attr {string} [version]
|
24
|
+
*
|
25
|
+
* @slot {Option}
|
26
|
+
*/
|
27
|
+
let OptionsGroup = class OptionsGroup extends LitElement {
|
28
|
+
constructor() {
|
29
|
+
super(...arguments);
|
30
|
+
this.hideLabel = false;
|
31
|
+
this.role = 'group';
|
32
|
+
this.version = packageJson.version;
|
33
|
+
}
|
34
|
+
static { this.shadowRootOptions = {
|
35
|
+
...LitElement.shadowRootOptions,
|
36
|
+
mode: shadowRootMode,
|
37
|
+
}; }
|
38
|
+
static { this.styles = styles; }
|
39
|
+
/**
|
40
|
+
* @default undefined
|
41
|
+
*/
|
42
|
+
get label() {
|
43
|
+
return this.#label;
|
44
|
+
}
|
45
|
+
set label(label) {
|
46
|
+
this.#label = label;
|
47
|
+
this.ariaLabel = label;
|
48
|
+
}
|
49
|
+
render() {
|
50
|
+
return html `
|
51
|
+
<div class="component">
|
52
|
+
<div
|
53
|
+
aria-hidden="true"
|
54
|
+
class=${classMap({ label: true, 'visually-hidden': this.hideLabel })}
|
55
|
+
>
|
56
|
+
${this.label}
|
57
|
+
</div>
|
58
|
+
|
59
|
+
<slot ${assertSlot([Option])}>
|
60
|
+
<!-- @type {Option} -->
|
61
|
+
</slot>
|
62
|
+
</div>
|
63
|
+
`;
|
64
|
+
}
|
65
|
+
#label;
|
66
|
+
};
|
67
|
+
__decorate([
|
68
|
+
property({ reflect: true }),
|
69
|
+
required
|
70
|
+
], OptionsGroup.prototype, "label", null);
|
71
|
+
__decorate([
|
72
|
+
property({ attribute: 'hide-label', reflect: true, type: Boolean })
|
73
|
+
], OptionsGroup.prototype, "hideLabel", void 0);
|
74
|
+
__decorate([
|
75
|
+
property({ reflect: true })
|
76
|
+
], OptionsGroup.prototype, "role", void 0);
|
77
|
+
__decorate([
|
78
|
+
property({ reflect: true })
|
79
|
+
], OptionsGroup.prototype, "version", void 0);
|
80
|
+
OptionsGroup = __decorate([
|
81
|
+
customElement('glide-core-options-group'),
|
82
|
+
final
|
83
|
+
], OptionsGroup);
|
84
|
+
export default OptionsGroup;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { css } from 'lit';
|
2
|
+
import visuallyHidden from './styles/visually-hidden.js';
|
3
|
+
export default [
|
4
|
+
css `
|
5
|
+
${visuallyHidden('.label.visually-hidden')}
|
6
|
+
`,
|
7
|
+
css `
|
8
|
+
:host(:not(:last-of-type)) .component::after {
|
9
|
+
background-color: var(--glide-core-color-static-stroke-secondary);
|
10
|
+
block-size: 1px;
|
11
|
+
border-radius: var(--glide-core-rounding-base-radius-sm);
|
12
|
+
content: '';
|
13
|
+
display: block;
|
14
|
+
margin-block: var(--glide-core-spacing-base-xxxs);
|
15
|
+
}
|
16
|
+
|
17
|
+
.label {
|
18
|
+
block-size: 1.75rem;
|
19
|
+
box-sizing: border-box;
|
20
|
+
font-family: var(--glide-core-typography-family-primary);
|
21
|
+
font-size: var(--glide-core-typography-size-body-small);
|
22
|
+
font-weight: var(--glide-core-typography-weight-bold);
|
23
|
+
max-inline-size: 21.875rem;
|
24
|
+
overflow: hidden;
|
25
|
+
padding-block: var(--glide-core-spacing-base-xxs);
|
26
|
+
padding-inline: var(--glide-core-spacing-base-sm);
|
27
|
+
text-overflow: ellipsis;
|
28
|
+
white-space: nowrap;
|
29
|
+
}
|
30
|
+
`,
|
31
|
+
];
|
package/dist/options.js
CHANGED
@@ -17,6 +17,7 @@ import final from './library/final.js';
|
|
17
17
|
import uniqueId from './library/unique-id.js';
|
18
18
|
import assertSlot from './library/assert-slot.js';
|
19
19
|
import Option from './option.js';
|
20
|
+
import OptionsGroup from './options.group.js';
|
20
21
|
// This component exists because Menu's target and its menu (`"role="menu"` or
|
21
22
|
// `role="listbox"`) both need to be in the light DOM so the target and menu can
|
22
23
|
// reference each other's IDs in ARIA attributes.
|
@@ -82,7 +83,7 @@ let Options = class Options extends LitElement {
|
|
82
83
|
loading: this.privateLoading,
|
83
84
|
})}
|
84
85
|
@slotchange=${this.#onDefaultSlotChange}
|
85
|
-
${assertSlot([Option, Text], true)}
|
86
|
+
${assertSlot([OptionsGroup, Option, Text], true)}
|
86
87
|
>
|
87
88
|
<!-- @type {Option | Text} -->
|
88
89
|
</slot>
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@crowdstrike/glide-core",
|
3
|
-
"version": "0.31.
|
3
|
+
"version": "0.31.2",
|
4
4
|
"description": "A Web Component design system",
|
5
5
|
"author": "CrowdStrike UX Team",
|
6
6
|
"license": "Apache-2.0",
|
@@ -94,13 +94,13 @@
|
|
94
94
|
"comment-parser": "^1.4.1",
|
95
95
|
"custom-elements-manifest": "^2.1.0",
|
96
96
|
"esbuild": "^0.25.0",
|
97
|
-
"eslint": "^9.
|
97
|
+
"eslint": "^9.31.0",
|
98
98
|
"eslint-config-prettier": "^10.1.5",
|
99
99
|
"eslint-plugin-import": "^2.31.0",
|
100
100
|
"eslint-plugin-lit": "^1.15.0",
|
101
101
|
"eslint-plugin-lit-a11y": "^4.1.4",
|
102
102
|
"eslint-plugin-sort-class-members": "^1.21.0",
|
103
|
-
"eslint-plugin-unicorn": "^
|
103
|
+
"eslint-plugin-unicorn": "^60.0.0",
|
104
104
|
"globals": "^15.13.0",
|
105
105
|
"globby": "^14.0.2",
|
106
106
|
"husky": "^8.0.3",
|