@crowdstrike/glide-core 0.29.1 → 0.30.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.
- package/dist/accordion.js +240 -1
- package/dist/accordion.styles.js +13 -7
- package/dist/button-group.button.js +143 -1
- package/dist/button-group.button.styles.js +43 -15
- package/dist/button-group.js +249 -1
- package/dist/button-group.styles.js +10 -5
- package/dist/button.js +206 -1
- package/dist/button.styles.js +12 -7
- package/dist/checkbox-group.js +479 -14
- package/dist/checkbox-group.styles.js +5 -2
- package/dist/checkbox.js +519 -32
- package/dist/checkbox.styles.js +10 -5
- package/dist/drawer.js +168 -1
- package/dist/drawer.styles.js +5 -2
- package/dist/dropdown.js +2423 -123
- package/dist/dropdown.option.js +536 -1
- package/dist/dropdown.option.styles.js +5 -2
- package/dist/dropdown.styles.js +14 -7
- package/dist/form-controls-layout.js +102 -1
- package/dist/form-controls-layout.styles.js +5 -2
- package/dist/icon-button.js +139 -1
- package/dist/icon-button.styles.js +19 -7
- package/dist/icons/checked.js +28 -1
- package/dist/icons/chevron.js +21 -1
- package/dist/icons/magnifying-glass.js +23 -1
- package/dist/icons/pencil.js +21 -1
- package/dist/icons/severity-critical.js +20 -1
- package/dist/icons/severity-informational.js +20 -1
- package/dist/icons/severity-medium.js +20 -1
- package/dist/icons/x.js +21 -1
- package/dist/inline-alert.js +118 -1
- package/dist/inline-alert.styles.js +5 -2
- package/dist/input.d.ts +8 -2
- package/dist/input.js +505 -41
- package/dist/input.styles.js +25 -4
- package/dist/label.js +303 -1
- package/dist/label.styles.js +11 -5
- package/dist/library/assert-slot.js +136 -1
- package/dist/library/expect-unhandled-rejection.js +14 -1
- package/dist/library/expect-window-error.js +26 -1
- package/dist/library/final.js +18 -1
- package/dist/library/form-control.js +1 -1
- package/dist/library/localize.js +10 -1
- package/dist/library/mouse.js +35 -1
- package/dist/library/on-resize.js +24 -1
- package/dist/library/required.js +35 -1
- package/dist/library/shadow-root-mode.js +4 -1
- package/dist/library/unique-id.js +3 -1
- package/dist/link.js +92 -1
- package/dist/link.styles.js +10 -5
- package/dist/menu.d.ts +3 -2
- package/dist/menu.js +1259 -1
- package/dist/menu.styles.js +34 -17
- package/dist/modal.d.ts +4 -0
- package/dist/modal.icon-button.js +60 -1
- package/dist/modal.icon-button.styles.js +5 -2
- package/dist/modal.js +473 -1
- package/dist/modal.styles.js +71 -22
- package/dist/option.d.ts +74 -0
- package/dist/option.js +498 -0
- package/dist/option.styles.js +140 -0
- package/dist/{menu.options.d.ts → options.d.ts} +5 -6
- package/dist/options.js +130 -0
- package/dist/options.styles.js +21 -0
- package/dist/popover.js +620 -1
- package/dist/popover.styles.js +11 -5
- package/dist/radio-group.js +624 -17
- package/dist/radio-group.radio.js +211 -1
- package/dist/radio-group.radio.styles.js +9 -4
- package/dist/radio-group.styles.js +5 -2
- package/dist/slider.js +1040 -61
- package/dist/slider.styles.js +9 -4
- package/dist/spinner.js +60 -1
- package/dist/spinner.styles.js +5 -2
- package/dist/split-button.js +116 -1
- package/dist/split-button.primary-button.js +100 -1
- package/dist/split-button.primary-button.styles.js +13 -6
- package/dist/split-button.primary-link.js +102 -1
- package/dist/split-button.secondary-button.d.ts +2 -3
- package/dist/split-button.secondary-button.js +121 -1
- package/dist/split-button.secondary-button.styles.js +12 -7
- package/dist/split-button.styles.js +9 -4
- package/dist/styles/focus-outline.js +9 -3
- package/dist/styles/fonts.css +6 -1
- package/dist/styles/opacity-and-scale-animation.js +6 -3
- package/dist/styles/skeleton.js +6 -3
- package/dist/styles/variables.css +410 -1
- package/dist/styles/visually-hidden.js +6 -3
- package/dist/tab.group.js +386 -1
- package/dist/tab.group.styles.js +5 -2
- package/dist/tab.js +133 -1
- package/dist/tab.panel.js +93 -1
- package/dist/tab.panel.styles.js +11 -5
- package/dist/tab.styles.js +9 -4
- package/dist/tag.js +207 -1
- package/dist/tag.styles.js +10 -5
- package/dist/textarea.js +353 -19
- package/dist/textarea.styles.js +23 -4
- package/dist/toast.js +130 -1
- package/dist/toast.toasts.js +248 -25
- package/dist/toast.toasts.styles.js +9 -4
- package/dist/toggle.js +178 -1
- package/dist/toggle.styles.js +25 -5
- package/dist/tooltip.container.d.ts +2 -0
- package/dist/tooltip.container.js +130 -1
- package/dist/tooltip.container.styles.js +18 -4
- package/dist/tooltip.d.ts +6 -0
- package/dist/tooltip.js +484 -1
- package/dist/tooltip.styles.js +21 -5
- package/dist/translations/en.js +36 -1
- package/dist/translations/fr.js +37 -1
- package/dist/translations/ja.js +37 -1
- package/package.json +8 -12
- package/dist/menu.button.d.ts +0 -42
- package/dist/menu.button.js +0 -1
- package/dist/menu.button.styles.js +0 -32
- package/dist/menu.link.d.ts +0 -44
- package/dist/menu.link.js +0 -1
- package/dist/menu.link.styles.js +0 -35
- package/dist/menu.options.js +0 -1
- package/dist/menu.options.styles.d.ts +0 -2
- package/dist/menu.options.styles.js +0 -20
- /package/dist/{menu.button.styles.d.ts → option.styles.d.ts} +0 -0
- /package/dist/{menu.link.styles.d.ts → options.styles.d.ts} +0 -0
package/dist/option.js
ADDED
@@ -0,0 +1,498 @@
|
|
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 { LitElement } from 'lit';
|
8
|
+
import { classMap } from 'lit/directives/class-map.js';
|
9
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
10
|
+
import { createRef, ref } from 'lit/directives/ref.js';
|
11
|
+
import { when } from 'lit/directives/when.js';
|
12
|
+
import { html, unsafeStatic } from 'lit/static-html.js';
|
13
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
14
|
+
import packageJson from '../package.json' with { type: 'json' };
|
15
|
+
import styles from './option.styles.js';
|
16
|
+
import assertSlot from './library/assert-slot.js';
|
17
|
+
import checkedIcon from './icons/checked.js';
|
18
|
+
import shadowRootMode from './library/shadow-root-mode.js';
|
19
|
+
import final from './library/final.js';
|
20
|
+
import uniqueId from './library/unique-id.js';
|
21
|
+
import Menu from './menu.js';
|
22
|
+
import './options.js';
|
23
|
+
import Tooltip from './tooltip.js';
|
24
|
+
import required from './library/required.js';
|
25
|
+
/**
|
26
|
+
* @attr {string} label
|
27
|
+
* @attr {string} [description]
|
28
|
+
* @attr {boolean} [disabled=false]
|
29
|
+
* @attr {string} [href]
|
30
|
+
*
|
31
|
+
* @readonly
|
32
|
+
* @attr {string} [id]
|
33
|
+
*
|
34
|
+
* @attr {'menuitem'|'option'} [role='menuitem']
|
35
|
+
* @attr {boolean} [selected=false]
|
36
|
+
*
|
37
|
+
* @readonly
|
38
|
+
* @attr {number} [tabindex=-1]
|
39
|
+
*
|
40
|
+
* @attr {string} [value='']
|
41
|
+
*
|
42
|
+
* @readonly
|
43
|
+
* @attr {string} [version]
|
44
|
+
*
|
45
|
+
* @slot {Element | Text} [content] - This is the unhappy path. It's the escape hatch where you can render arbitrary content and lay it out however you need to. If you go this route, `slot="icon"` and `slot="submenu"` will become unavailable. And the `label` and `description` attributes won't be rendered. The `label` attribute is still required. We'll show it in a tooltip when your content overflows. If you need a second line of text in the tooltip, provide you can provide it via the `description` attribute.
|
46
|
+
* @slot {Element} [icon]
|
47
|
+
* @slot {Menu} [submenu]
|
48
|
+
*/
|
49
|
+
let Option = class Option extends LitElement {
|
50
|
+
constructor() {
|
51
|
+
super(...arguments);
|
52
|
+
// On the host so screenreaders can find it when Options refers to it via
|
53
|
+
// `aria-activedescendant`.
|
54
|
+
this.id = uniqueId();
|
55
|
+
this.privateTooltipOpen = false;
|
56
|
+
this.role = 'menuitem';
|
57
|
+
this.tabIndex = -1;
|
58
|
+
this.value = '';
|
59
|
+
this.version = packageJson.version;
|
60
|
+
this.hasContentSlot = false;
|
61
|
+
this.hasIconSlot = false;
|
62
|
+
this.hasSubMenuSlot = false;
|
63
|
+
this.isContentSlotOverflow = false;
|
64
|
+
// Set in `#onSubmenuToggle()`. Used to toggle the sub-Menu's target as open
|
65
|
+
// or closed visually.
|
66
|
+
this.isSubmenuOpen = false;
|
67
|
+
this.#containerElementRef = createRef();
|
68
|
+
this.#contentSlotElementRef = createRef();
|
69
|
+
this.#descriptionElementRef = createRef();
|
70
|
+
this.#isActive = false;
|
71
|
+
this.#isDisabled = false;
|
72
|
+
this.#isSelected = false;
|
73
|
+
this.#labelElementRef = createRef();
|
74
|
+
this.#tooltipElementRef = createRef();
|
75
|
+
}
|
76
|
+
static { this.shadowRootOptions = {
|
77
|
+
...LitElement.shadowRootOptions,
|
78
|
+
mode: shadowRootMode,
|
79
|
+
}; }
|
80
|
+
static { this.styles = styles; }
|
81
|
+
// Consumers may chose not to take the happy path and instead use the "content"
|
82
|
+
// slot. In that case, we don't render `label`. But `label` still needs to be
|
83
|
+
// supplied so we can pass it to Tooltip. That's why it's required.
|
84
|
+
/**
|
85
|
+
* @default undefined
|
86
|
+
*/
|
87
|
+
get label() {
|
88
|
+
return this.#label;
|
89
|
+
}
|
90
|
+
set label(label) {
|
91
|
+
this.#label = label;
|
92
|
+
// Wait for the label to render. A rerender won't be scheduled by Lit until after
|
93
|
+
// this setter finishes. So awaiting `this.updateComplete` won't fly.
|
94
|
+
setTimeout(() => {
|
95
|
+
this.#updateContentSlotOverflow();
|
96
|
+
});
|
97
|
+
}
|
98
|
+
/**
|
99
|
+
* @default undefined
|
100
|
+
*/
|
101
|
+
get description() {
|
102
|
+
return this.#description;
|
103
|
+
}
|
104
|
+
set description(description) {
|
105
|
+
this.#description = description;
|
106
|
+
// Wait for the description to render. A rerender won't be scheduled by Lit until
|
107
|
+
// after this setter finishes. So awaiting `this.updateComplete` won't fly.
|
108
|
+
setTimeout(() => {
|
109
|
+
this.#updateContentSlotOverflow();
|
110
|
+
});
|
111
|
+
}
|
112
|
+
/**
|
113
|
+
* @default false
|
114
|
+
*/
|
115
|
+
get disabled() {
|
116
|
+
return this.#isDisabled;
|
117
|
+
}
|
118
|
+
set disabled(isDisabled) {
|
119
|
+
this.#isDisabled = isDisabled;
|
120
|
+
this.ariaDisabled = isDisabled ? 'true' : 'false';
|
121
|
+
this.dispatchEvent(new Event('private-disabled-change', { bubbles: true }));
|
122
|
+
}
|
123
|
+
get privateActive() {
|
124
|
+
return this.#isActive;
|
125
|
+
}
|
126
|
+
set privateActive(isActive) {
|
127
|
+
this.#isActive = isActive;
|
128
|
+
// Options are arbitrarily shown and hidden when Menu is opened and closed. So
|
129
|
+
// calling the below method in the `label` or `description` setters isn't enough
|
130
|
+
// because `scrollWidth` and `clientWidth` of the `label` and `description` will
|
131
|
+
// both be zero until Menu is open. Rather than expose a pseudo-private method for
|
132
|
+
// Menu to call on open, we simply check for overflow when an Option becomes
|
133
|
+
// active. But first we wait a tick for Menu to open.
|
134
|
+
setTimeout(() => {
|
135
|
+
this.#updateContentSlotOverflow();
|
136
|
+
});
|
137
|
+
}
|
138
|
+
/**
|
139
|
+
* @default false
|
140
|
+
*/
|
141
|
+
get selected() {
|
142
|
+
return this.#isSelected;
|
143
|
+
}
|
144
|
+
set selected(isSelected) {
|
145
|
+
this.ariaSelected = isSelected.toString();
|
146
|
+
this.#isSelected = isSelected;
|
147
|
+
}
|
148
|
+
click() {
|
149
|
+
// You'd think this condition wouldn't be needed because `#onTooltipClick()` has a
|
150
|
+
// `this.disabled` condition that stops propagation of the event. But Menu's
|
151
|
+
// `#onDocumentClick()` handles "click" events in their capture phase, which means
|
152
|
+
// that handler will pick up the event and close Menu before `#onTooltipClick()`
|
153
|
+
// has stopped it from propagating. So we have to avoid dispatching the event
|
154
|
+
// altogether.
|
155
|
+
if (!this.disabled) {
|
156
|
+
this.#tooltipElementRef.value?.click();
|
157
|
+
}
|
158
|
+
}
|
159
|
+
connectedCallback() {
|
160
|
+
super.connectedCallback();
|
161
|
+
this.ariaDisabled = this.disabled.toString();
|
162
|
+
}
|
163
|
+
render() {
|
164
|
+
const tag = this.href === undefined || this.role === 'option' ? 'div' : 'a';
|
165
|
+
// We have a "content" slot instead of the usual default slot because whitespace
|
166
|
+
// in a slot will prevent the slot from falling back.
|
167
|
+
//
|
168
|
+
// So even a single stray space in the default slot will take the consumer off the
|
169
|
+
// happy path and prevent the `label` and `description` attributes, as well as the
|
170
|
+
// "icon" and "submenu" slots, from rendering.
|
171
|
+
//
|
172
|
+
// More importantly, even if the consumer doesn't have stray whitespace in the
|
173
|
+
// default slot, but provides "icon" or "submenu" slots, then whitespace will
|
174
|
+
// necessarily be introduced into the default slot via the whitespace around that
|
175
|
+
// content. And neither the "icon" or "default" slots nor the `label` or
|
176
|
+
// `description` attributes will be rendered.
|
177
|
+
//
|
178
|
+
// A slot dedicated to arbitrary content is the only way we can avoid these false
|
179
|
+
// positives.
|
180
|
+
// Both rules are disabled because of `unsafeStatic()`.
|
181
|
+
/*
|
182
|
+
eslint-disable lit/binding-positions, lit/no-invalid-html
|
183
|
+
*/
|
184
|
+
return html `
|
185
|
+
<glide-core-tooltip
|
186
|
+
class="tooltip"
|
187
|
+
data-test="tooltip"
|
188
|
+
description=${ifDefined(this.description)}
|
189
|
+
label=${ifDefined(this.label)}
|
190
|
+
screenreader-hidden
|
191
|
+
?disabled=${!this.isContentSlotOverflow || this.disabled}
|
192
|
+
?open=${this.privateTooltipOpen}
|
193
|
+
@click=${this.#onTooltipClick}
|
194
|
+
@toggle=${this.#onTooltipToggle}
|
195
|
+
${ref(this.#tooltipElementRef)}>
|
196
|
+
|
197
|
+
<${unsafeStatic(tag)}
|
198
|
+
class=${classMap({
|
199
|
+
container: true,
|
200
|
+
active: this.privateActive,
|
201
|
+
disabled: this.disabled,
|
202
|
+
href: Boolean(this.href),
|
203
|
+
})}
|
204
|
+
data-test="container"
|
205
|
+
href=${ifDefined(tag === 'a' ? this.href : undefined)}
|
206
|
+
slot="target"
|
207
|
+
${ref(this.#containerElementRef)}
|
208
|
+
>
|
209
|
+
<slot
|
210
|
+
class=${classMap({
|
211
|
+
'content-slot': true,
|
212
|
+
fallback: !this.hasContentSlot,
|
213
|
+
icon: this.hasIconSlot,
|
214
|
+
selected: this.selected && this.role === 'option',
|
215
|
+
submenu: this.hasSubMenuSlot,
|
216
|
+
})}
|
217
|
+
data-test="content-slot"
|
218
|
+
name="content"
|
219
|
+
@slotchange=${this.#onContentSlotChange}
|
220
|
+
${ref(this.#contentSlotElementRef)}
|
221
|
+
>
|
222
|
+
<!--
|
223
|
+
This is the unhappy path. It's the escape hatch where you can render arbitrary
|
224
|
+
content and lay it out however you need to.
|
225
|
+
|
226
|
+
If you go this route, \`slot="icon"\` and \`slot="submenu"\` will become
|
227
|
+
unavailable. And the \`label\` and \`description\` attributes won't be rendered.
|
228
|
+
|
229
|
+
The \`label\` attribute is still required. We'll show it in a tooltip when your
|
230
|
+
content overflows. If you need a second line of text in the tooltip, provide you
|
231
|
+
can provide it via the \`description\` attribute.
|
232
|
+
|
233
|
+
@type {Element | Text}
|
234
|
+
-->
|
235
|
+
|
236
|
+
<slot
|
237
|
+
data-test="icon-slot"
|
238
|
+
name="icon"
|
239
|
+
@slotchange=${this.#onIconSlotChange}
|
240
|
+
${assertSlot([Element], true)}
|
241
|
+
>
|
242
|
+
<!-- @type {Element} -->
|
243
|
+
</slot>
|
244
|
+
|
245
|
+
<div
|
246
|
+
class=${classMap({
|
247
|
+
label: true,
|
248
|
+
bold: Boolean(this.description),
|
249
|
+
})}
|
250
|
+
${ref(this.#labelElementRef)}
|
251
|
+
>
|
252
|
+
${this.label}
|
253
|
+
</div>
|
254
|
+
|
255
|
+
<slot
|
256
|
+
class=${classMap({
|
257
|
+
'submenu-slot': true,
|
258
|
+
active: this.privateActive,
|
259
|
+
disabled: this.disabled,
|
260
|
+
open: this.isSubmenuOpen,
|
261
|
+
})}
|
262
|
+
data-test="submenu-slot"
|
263
|
+
name="submenu"
|
264
|
+
@slotchange=${this.#onSubmenuSlotChange}
|
265
|
+
@toggle=${this.#onSubmenuToggle}
|
266
|
+
${assertSlot([Menu], true)}
|
267
|
+
>
|
268
|
+
<!-- @type {Menu} -->
|
269
|
+
</slot>
|
270
|
+
|
271
|
+
${when(this.selected && !this.disabled && this.role === 'option', () => {
|
272
|
+
return html `<div
|
273
|
+
class="checked-icon-container"
|
274
|
+
data-test="checked-icon-container"
|
275
|
+
>
|
276
|
+
${checkedIcon}
|
277
|
+
</div>`;
|
278
|
+
})}
|
279
|
+
|
280
|
+
${when(this.description, () => {
|
281
|
+
return html `
|
282
|
+
<div
|
283
|
+
class=${classMap({
|
284
|
+
description: true,
|
285
|
+
icon: this.hasIconSlot,
|
286
|
+
})}
|
287
|
+
data-test="description"
|
288
|
+
${ref(this.#descriptionElementRef)}
|
289
|
+
>
|
290
|
+
${this.description}
|
291
|
+
</div>
|
292
|
+
`;
|
293
|
+
})}
|
294
|
+
</slot>
|
295
|
+
</${unsafeStatic(tag)}>
|
296
|
+
</glide-core-tooltip>
|
297
|
+
`;
|
298
|
+
}
|
299
|
+
updated() {
|
300
|
+
// `this.ariaSelected` needs updating whenever `this.disabled`, `this.selected`, or
|
301
|
+
// `this.role` changes.
|
302
|
+
//
|
303
|
+
// It's updated here to avoid creating a getter and setter for each of those
|
304
|
+
// properties.
|
305
|
+
this.ariaSelected =
|
306
|
+
this.selected && this.role === 'option' && !this.disabled
|
307
|
+
? 'true'
|
308
|
+
: this.role === 'option'
|
309
|
+
? 'false'
|
310
|
+
: null;
|
311
|
+
}
|
312
|
+
#containerElementRef;
|
313
|
+
#contentSlotElementRef;
|
314
|
+
#description;
|
315
|
+
#descriptionElementRef;
|
316
|
+
#isActive;
|
317
|
+
#isDisabled;
|
318
|
+
#isSelected;
|
319
|
+
#label;
|
320
|
+
#labelElementRef;
|
321
|
+
#tooltipElementRef;
|
322
|
+
get #contentSlotElements() {
|
323
|
+
// These are the elements that are checked for overflow in
|
324
|
+
// `#updateContentSlotOverflow()`.
|
325
|
+
//
|
326
|
+
// The slot fallback case is straightforward. And simply returning its assigned
|
327
|
+
// elements is sufficient. With the non-fallback case, however, consumers give us
|
328
|
+
// arbitrary markup. So it may not be to the assigned element that's overflowing.
|
329
|
+
// Instead, One or more children of the assigned element may be overflowing.
|
330
|
+
//
|
331
|
+
// For example, the assigned element might be DIV. And that DIV might contain an
|
332
|
+
// icon and another DIV with some text. In this case, it's the nested DIV that's
|
333
|
+
// likely to overflow, similar to how this slot's fallback content has an icon with
|
334
|
+
// a DIV that can overflow.
|
335
|
+
//
|
336
|
+
// Thus we query for every element.
|
337
|
+
if (this.#contentSlotElementRef.value) {
|
338
|
+
return [
|
339
|
+
...this.#contentSlotElementRef.value.assignedElements({
|
340
|
+
flatten: true,
|
341
|
+
}),
|
342
|
+
].flatMap((element) => [element, ...element.querySelectorAll('*')]);
|
343
|
+
}
|
344
|
+
}
|
345
|
+
#onContentSlotChange(event) {
|
346
|
+
if (event.target === this.#contentSlotElementRef.value) {
|
347
|
+
this.hasContentSlot =
|
348
|
+
this.#contentSlotElementRef.value.assignedNodes().length > 0;
|
349
|
+
}
|
350
|
+
this.#updateContentSlotOverflow();
|
351
|
+
}
|
352
|
+
#onIconSlotChange(event) {
|
353
|
+
if (event.target instanceof HTMLSlotElement) {
|
354
|
+
this.hasIconSlot = event.target.assignedElements().length > 0;
|
355
|
+
}
|
356
|
+
}
|
357
|
+
#onSubmenuSlotChange(event) {
|
358
|
+
if (event.target instanceof HTMLSlotElement) {
|
359
|
+
this.hasSubMenuSlot = event.target.assignedElements().length > 0;
|
360
|
+
}
|
361
|
+
}
|
362
|
+
#onSubmenuToggle(event) {
|
363
|
+
const isOwnSubmenu = this.querySelector(':scope > glide-core-menu') === event.target;
|
364
|
+
// Nested sub-Menus can be opened or closed and the target of the Option's
|
365
|
+
// own sub-Menu shouldn't change visually. As long as the Option's own
|
366
|
+
// sub-Menu is open, the sub-Menu's target should appear open. Similar for
|
367
|
+
// when a nested sub-Menu is closed.
|
368
|
+
if (isOwnSubmenu && event.target instanceof Menu && event.target.open) {
|
369
|
+
this.isSubmenuOpen = true;
|
370
|
+
}
|
371
|
+
else if (isOwnSubmenu) {
|
372
|
+
this.isSubmenuOpen = false;
|
373
|
+
}
|
374
|
+
}
|
375
|
+
#onTooltipClick(event) {
|
376
|
+
if (this.disabled) {
|
377
|
+
if (this.href) {
|
378
|
+
event.preventDefault();
|
379
|
+
}
|
380
|
+
// Consumers listen for "click" events to know when an Option is selected. Letting
|
381
|
+
// the event propagate would result in a false positive event bubbling up to the
|
382
|
+
// consumer. It would also get picked up by Menu, which would close.
|
383
|
+
event.stopPropagation();
|
384
|
+
return;
|
385
|
+
}
|
386
|
+
const isTooltipClick = Boolean(event.target === this.#tooltipElementRef.value);
|
387
|
+
const isContainerClick = Boolean(event.target === this.#containerElementRef.value);
|
388
|
+
const isTargetClick = Boolean(event.target instanceof Element &&
|
389
|
+
event.target.closest('[slot="target"]'));
|
390
|
+
const isSubmenuOption = event.target instanceof Element &&
|
391
|
+
event.target.closest('glide-core-option') !== this;
|
392
|
+
// Consumers rely on an Option's `value` property to determine which Option was
|
393
|
+
// clicked. Options, however, can have an icon or arbitrary content. So
|
394
|
+
// `event.target` may not always be the Option itself.
|
395
|
+
//
|
396
|
+
// Stopping the event and calling `this.click()` retargets `event.target` to the
|
397
|
+
// Option, which means consumers don't have to call `closest('glide-core-option')`
|
398
|
+
// on `event.target` in their click handler to get the Option before checking its
|
399
|
+
// `value`.
|
400
|
+
//
|
401
|
+
// `!isTooltipClick` because events that originate from inside the shadow DOM will
|
402
|
+
// be retarged to the host for us by the browser. Same for `!isContainerClick`.
|
403
|
+
//
|
404
|
+
// `!isTargetClick` because Menu needs to know if a target of a sub-Menu was
|
405
|
+
// clicked to avoid closing itself.
|
406
|
+
//
|
407
|
+
// `!isSubmenuOption` so we don't retarget sub-Menu Option clicks to their super-
|
408
|
+
// Menu Option.
|
409
|
+
if (!isTooltipClick &&
|
410
|
+
!isContainerClick &&
|
411
|
+
!isTargetClick &&
|
412
|
+
!isSubmenuOption) {
|
413
|
+
event.stopImmediatePropagation();
|
414
|
+
event.stopPropagation();
|
415
|
+
if (this.href) {
|
416
|
+
event.preventDefault();
|
417
|
+
}
|
418
|
+
this.click();
|
419
|
+
return;
|
420
|
+
}
|
421
|
+
}
|
422
|
+
#onTooltipToggle(event) {
|
423
|
+
if (event.target instanceof Tooltip) {
|
424
|
+
// Menu dispatches its own "toggle" event. Letting Tooltip's "toggle" event
|
425
|
+
// propagate would mean that consumers listening for the event on Menu would have
|
426
|
+
// to filter out the one coming from Tooltip.
|
427
|
+
//
|
428
|
+
// But first they'll probably file a bug. It's likely no consumer will be
|
429
|
+
// interested in knowing when an Option's tooltip is open. So it's probably best to
|
430
|
+
// stop the event from propagating.
|
431
|
+
event.stopPropagation();
|
432
|
+
}
|
433
|
+
}
|
434
|
+
#updateContentSlotOverflow() {
|
435
|
+
const isContentSlotOverflow = this.#contentSlotElements !== undefined &&
|
436
|
+
this.#contentSlotElements.some((element) => {
|
437
|
+
return element.scrollWidth > element.clientWidth;
|
438
|
+
});
|
439
|
+
this.isContentSlotOverflow = isContentSlotOverflow;
|
440
|
+
}
|
441
|
+
};
|
442
|
+
__decorate([
|
443
|
+
property({ reflect: true }),
|
444
|
+
required
|
445
|
+
], Option.prototype, "label", null);
|
446
|
+
__decorate([
|
447
|
+
property({ reflect: true })
|
448
|
+
], Option.prototype, "description", null);
|
449
|
+
__decorate([
|
450
|
+
property({ reflect: true, type: Boolean, useDefault: true })
|
451
|
+
], Option.prototype, "disabled", null);
|
452
|
+
__decorate([
|
453
|
+
property({ reflect: true })
|
454
|
+
], Option.prototype, "href", void 0);
|
455
|
+
__decorate([
|
456
|
+
property({ reflect: true })
|
457
|
+
], Option.prototype, "id", void 0);
|
458
|
+
__decorate([
|
459
|
+
property({ type: Boolean, useDefault: true })
|
460
|
+
], Option.prototype, "privateActive", null);
|
461
|
+
__decorate([
|
462
|
+
property({ type: Boolean })
|
463
|
+
], Option.prototype, "privateTooltipOpen", void 0);
|
464
|
+
__decorate([
|
465
|
+
property({ reflect: true })
|
466
|
+
], Option.prototype, "role", void 0);
|
467
|
+
__decorate([
|
468
|
+
property({ type: Boolean })
|
469
|
+
], Option.prototype, "selected", null);
|
470
|
+
__decorate([
|
471
|
+
property({ attribute: 'tabindex', reflect: true, type: Number })
|
472
|
+
], Option.prototype, "tabIndex", void 0);
|
473
|
+
__decorate([
|
474
|
+
property({ reflect: true, useDefault: true })
|
475
|
+
], Option.prototype, "value", void 0);
|
476
|
+
__decorate([
|
477
|
+
property({ reflect: true })
|
478
|
+
], Option.prototype, "version", void 0);
|
479
|
+
__decorate([
|
480
|
+
state()
|
481
|
+
], Option.prototype, "hasContentSlot", void 0);
|
482
|
+
__decorate([
|
483
|
+
state()
|
484
|
+
], Option.prototype, "hasIconSlot", void 0);
|
485
|
+
__decorate([
|
486
|
+
state()
|
487
|
+
], Option.prototype, "hasSubMenuSlot", void 0);
|
488
|
+
__decorate([
|
489
|
+
state()
|
490
|
+
], Option.prototype, "isContentSlotOverflow", void 0);
|
491
|
+
__decorate([
|
492
|
+
state()
|
493
|
+
], Option.prototype, "isSubmenuOpen", void 0);
|
494
|
+
Option = __decorate([
|
495
|
+
customElement('glide-core-option'),
|
496
|
+
final
|
497
|
+
], Option);
|
498
|
+
export default Option;
|
@@ -0,0 +1,140 @@
|
|
1
|
+
import { css } from 'lit';
|
2
|
+
export default [
|
3
|
+
css `
|
4
|
+
.tooltip {
|
5
|
+
display: block;
|
6
|
+
}
|
7
|
+
|
8
|
+
.container {
|
9
|
+
border-radius: var(--glide-core-rounding-base-radius-xs);
|
10
|
+
box-sizing: border-box;
|
11
|
+
color: var(--glide-core-color-static-text-default);
|
12
|
+
cursor: pointer;
|
13
|
+
font-family: var(--glide-core-typography-family-primary);
|
14
|
+
font-size: var(--glide-core-typography-size-body-default);
|
15
|
+
font-weight: var(--glide-core-typography-weight-regular);
|
16
|
+
inline-size: 100%;
|
17
|
+
max-inline-size: 21.875rem;
|
18
|
+
padding-block: var(--glide-core-spacing-base-xxs);
|
19
|
+
padding-inline: var(--glide-core-spacing-base-sm);
|
20
|
+
transition: background-color var(--glide-core-duration-fast-02) ease-in-out;
|
21
|
+
user-select: none;
|
22
|
+
|
23
|
+
&.active {
|
24
|
+
background-color: var(
|
25
|
+
--glide-core-color-interactive-surface-container--hover
|
26
|
+
);
|
27
|
+
}
|
28
|
+
|
29
|
+
&.disabled {
|
30
|
+
color: var(--glide-core-color-interactive-icon-default--disabled);
|
31
|
+
|
32
|
+
/*
|
33
|
+
If this is an Option in a sub-Menu, "cursor: pointer" (above) will be inherited
|
34
|
+
from the parent Option (the one the contains the sub-Menu). So we make sure we
|
35
|
+
override it.
|
36
|
+
*/
|
37
|
+
cursor: default;
|
38
|
+
}
|
39
|
+
|
40
|
+
&.href {
|
41
|
+
display: block;
|
42
|
+
text-decoration: none;
|
43
|
+
|
44
|
+
&:not(.disabled) {
|
45
|
+
color: inherit;
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
.content-slot {
|
51
|
+
&.fallback {
|
52
|
+
align-items: center;
|
53
|
+
display: grid;
|
54
|
+
grid-column-gap: var(--glide-core-spacing-base-xs);
|
55
|
+
|
56
|
+
&.icon {
|
57
|
+
grid-template-columns: auto 1fr;
|
58
|
+
|
59
|
+
&.submenu {
|
60
|
+
display: grid;
|
61
|
+
grid-template-columns: auto 1fr auto;
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
&.submenu {
|
66
|
+
grid-template-columns: 1fr auto;
|
67
|
+
}
|
68
|
+
|
69
|
+
&.selected {
|
70
|
+
grid-template-columns: 1fr auto;
|
71
|
+
|
72
|
+
&.icon {
|
73
|
+
grid-template-columns: auto 1fr auto;
|
74
|
+
|
75
|
+
&.submenu {
|
76
|
+
grid-template-columns: auto 1fr auto auto;
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
&.submenu {
|
81
|
+
grid-template-columns: 1fr auto auto;
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
.label {
|
88
|
+
overflow-x: hidden;
|
89
|
+
text-overflow: ellipsis;
|
90
|
+
white-space: nowrap;
|
91
|
+
|
92
|
+
&.bold {
|
93
|
+
font-weight: var(--glide-core-typography-weight-bold);
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
.description {
|
98
|
+
grid-column: 1 / 2;
|
99
|
+
overflow-x: hidden;
|
100
|
+
text-overflow: ellipsis;
|
101
|
+
white-space: nowrap;
|
102
|
+
|
103
|
+
&.icon {
|
104
|
+
grid-column: 2 / 3;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
.checked-icon-container {
|
109
|
+
--private-size: 1rem;
|
110
|
+
|
111
|
+
display: contents;
|
112
|
+
}
|
113
|
+
|
114
|
+
.submenu-slot {
|
115
|
+
&::slotted(*) {
|
116
|
+
opacity: 0;
|
117
|
+
}
|
118
|
+
|
119
|
+
&.active {
|
120
|
+
&::slotted(*) {
|
121
|
+
opacity: 1;
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
&:hover {
|
126
|
+
&::slotted(*) {
|
127
|
+
color: var(--glide-core-color-interactive-text-link--hover);
|
128
|
+
opacity: 1;
|
129
|
+
}
|
130
|
+
}
|
131
|
+
|
132
|
+
&.open {
|
133
|
+
&::slotted(*) {
|
134
|
+
color: var(--glide-core-color-interactive-text-link--hover);
|
135
|
+
opacity: 1;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
`,
|
140
|
+
];
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { LitElement } from 'lit';
|
2
2
|
declare global {
|
3
3
|
interface HTMLElementTagNameMap {
|
4
|
-
'glide-core-
|
4
|
+
'glide-core-options': Options;
|
5
5
|
}
|
6
6
|
}
|
7
7
|
/**
|
@@ -11,8 +11,7 @@ declare global {
|
|
11
11
|
* @readonly
|
12
12
|
* @attr {string} [id]
|
13
13
|
*
|
14
|
-
* @
|
15
|
-
* @attr {string} [role='menu']
|
14
|
+
* @attr {'menu'|'listbox'} [role='menu']
|
16
15
|
*
|
17
16
|
* @readonly
|
18
17
|
* @attr {number} [tabindex=-1]
|
@@ -20,9 +19,9 @@ declare global {
|
|
20
19
|
* @readonly
|
21
20
|
* @attr {string} [version]
|
22
21
|
*
|
23
|
-
* @slot {
|
22
|
+
* @slot {Option | Text}
|
24
23
|
*/
|
25
|
-
export default class
|
24
|
+
export default class Options extends LitElement {
|
26
25
|
#private;
|
27
26
|
static shadowRootOptions: ShadowRootInit;
|
28
27
|
static styles: import("lit").CSSResult[];
|
@@ -30,7 +29,7 @@ export default class MenuOptions extends LitElement {
|
|
30
29
|
ariaLabelledby: string;
|
31
30
|
readonly id: string;
|
32
31
|
privateLoading: boolean;
|
33
|
-
|
32
|
+
role: 'menu' | 'listbox';
|
34
33
|
readonly tabIndex = -1;
|
35
34
|
readonly version: string;
|
36
35
|
render(): import("lit").TemplateResult<1>;
|