@api-client/ui 0.3.2 → 0.3.4
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/build/src/elements/environment/EnvironmentEditor.d.ts.map +1 -1
- package/build/src/elements/environment/EnvironmentEditor.js +8 -6
- package/build/src/elements/environment/EnvironmentEditor.js.map +1 -1
- package/build/src/elements/har/HarViewer.d.ts.map +1 -1
- package/build/src/elements/har/HarViewer.js +13 -15
- package/build/src/elements/har/HarViewer.js.map +1 -1
- package/build/src/elements/http/RequestEditor.d.ts +2 -1
- package/build/src/elements/http/RequestEditor.d.ts.map +1 -1
- package/build/src/elements/http/RequestEditor.js +17 -12
- package/build/src/elements/http/RequestEditor.js.map +1 -1
- package/build/src/elements/http/RequestLog.d.ts.map +1 -1
- package/build/src/elements/http/RequestLog.js +34 -8
- package/build/src/elements/http/RequestLog.js.map +1 -1
- package/build/src/md/button/internals/base.d.ts.map +1 -1
- package/build/src/md/button/internals/base.js +2 -6
- package/build/src/md/button/internals/base.js.map +1 -1
- package/build/src/md/button/internals/button.styles.js +4 -4
- package/build/src/md/button/internals/button.styles.js.map +1 -1
- package/build/src/md/chip/internals/Chip.d.ts +11 -15
- package/build/src/md/chip/internals/Chip.d.ts.map +1 -1
- package/build/src/md/chip/internals/Chip.js +66 -104
- package/build/src/md/chip/internals/Chip.js.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.d.ts.map +1 -1
- package/build/src/md/chip/internals/Chip.styles.js +114 -101
- package/build/src/md/chip/internals/Chip.styles.js.map +1 -1
- package/build/src/md/chip/internals/ChipSet.d.ts +16 -0
- package/build/src/md/chip/internals/ChipSet.d.ts.map +1 -0
- package/build/src/md/chip/internals/ChipSet.js +138 -0
- package/build/src/md/chip/internals/ChipSet.js.map +1 -0
- package/build/src/md/chip/internals/ChipSet.styles.d.ts +3 -0
- package/build/src/md/chip/internals/ChipSet.styles.d.ts.map +1 -0
- package/build/src/md/chip/internals/ChipSet.styles.js +9 -0
- package/build/src/md/chip/internals/ChipSet.styles.js.map +1 -0
- package/build/src/md/chip/ui-chip-set.d.ts +11 -0
- package/build/src/md/chip/ui-chip-set.d.ts.map +1 -0
- package/build/src/md/chip/ui-chip-set.js +27 -0
- package/build/src/md/chip/ui-chip-set.js.map +1 -0
- package/build/src/md/motion/animation.d.ts +5 -3
- package/build/src/md/motion/animation.d.ts.map +1 -1
- package/build/src/md/motion/animation.js +4 -2
- package/build/src/md/motion/animation.js.map +1 -1
- package/build/src/md/ripple/internals/ripple.styles.d.ts.map +1 -1
- package/build/src/md/ripple/internals/ripple.styles.js +20 -8
- package/build/src/md/ripple/internals/ripple.styles.js.map +1 -1
- package/build/src/md/switch/internals/Switch.styles.js +1 -1
- package/build/src/md/switch/internals/Switch.styles.js.map +1 -1
- package/build/src/md/tabs/internals/Tab.d.ts +25 -9
- package/build/src/md/tabs/internals/Tab.d.ts.map +1 -1
- package/build/src/md/tabs/internals/Tab.js +122 -53
- package/build/src/md/tabs/internals/Tab.js.map +1 -1
- package/build/src/md/tabs/internals/Tab.styles.d.ts.map +1 -1
- package/build/src/md/tabs/internals/Tab.styles.js +69 -64
- package/build/src/md/tabs/internals/Tab.styles.js.map +1 -1
- package/build/src/md/tabs/internals/Tabs.d.ts +52 -54
- package/build/src/md/tabs/internals/Tabs.d.ts.map +1 -1
- package/build/src/md/tabs/internals/Tabs.js +270 -330
- package/build/src/md/tabs/internals/Tabs.js.map +1 -1
- package/build/src/md/tabs/internals/Tabs.styles.d.ts.map +1 -1
- package/build/src/md/tabs/internals/Tabs.styles.js +13 -17
- package/build/src/md/tabs/internals/Tabs.styles.js.map +1 -1
- package/demo/md/chip/chip.html +33 -6
- package/demo/md/chip/chip.ts +111 -56
- package/demo/md/tabs/tabs.html +19 -0
- package/demo/md/tabs/tabs.ts +133 -83
- package/package.json +1 -1
- package/src/elements/environment/EnvironmentEditor.ts +8 -6
- package/src/elements/har/HarViewer.ts +13 -15
- package/src/elements/http/RequestEditor.ts +18 -13
- package/src/elements/http/RequestLog.ts +34 -8
- package/src/md/button/internals/base.ts +2 -6
- package/src/md/button/internals/button.styles.ts +4 -4
- package/src/md/chip/internals/Chip.styles.ts +114 -101
- package/src/md/chip/internals/Chip.ts +58 -88
- package/src/md/chip/internals/ChipSet.styles.ts +9 -0
- package/src/md/chip/internals/ChipSet.ts +142 -0
- package/src/md/chip/ui-chip-set.ts +15 -0
- package/src/md/motion/animation.ts +4 -2
- package/src/md/ripple/internals/ripple.styles.ts +20 -8
- package/src/md/switch/internals/Switch.styles.ts +1 -1
- package/src/md/tabs/internals/Tab.styles.ts +69 -64
- package/src/md/tabs/internals/Tab.ts +126 -43
- package/src/md/tabs/internals/Tabs.styles.ts +13 -17
- package/src/md/tabs/internals/Tabs.ts +259 -305
- package/test/elements/har/HarViewerElement.test.ts +1 -55
- package/test/ui/chip/UiChip.test.ts +18 -67
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { html, LitElement } from 'lit'
|
|
2
|
+
import { queryAssignedElements } from 'lit/decorators.js'
|
|
3
|
+
|
|
4
|
+
import Chip from './Chip.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A chip set component.
|
|
8
|
+
*/
|
|
9
|
+
export default class ChipSet extends LitElement {
|
|
10
|
+
get chips() {
|
|
11
|
+
return this.childElements.filter((child): child is Chip => child instanceof Chip)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@queryAssignedElements() private accessor childElements!: HTMLElement[]
|
|
15
|
+
private readonly internals = this.attachInternals()
|
|
16
|
+
|
|
17
|
+
constructor() {
|
|
18
|
+
super()
|
|
19
|
+
this.addEventListener('focusin', this.updateTabIndices.bind(this))
|
|
20
|
+
this.addEventListener('update-focus', this.updateTabIndices.bind(this))
|
|
21
|
+
this.addEventListener('keydown', this.handleKeyDown.bind(this))
|
|
22
|
+
this.internals.role = 'toolbar'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
override connectedCallback(): void {
|
|
26
|
+
super.connectedCallback()
|
|
27
|
+
if (!this.hasAttribute('role')) {
|
|
28
|
+
this.setAttribute('role', 'toolbar')
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected override render() {
|
|
33
|
+
return html`<slot @slotchange=${this.updateTabIndices}></slot>`
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private handleKeyDown(event: KeyboardEvent) {
|
|
37
|
+
const isLeft = event.key === 'ArrowLeft'
|
|
38
|
+
const isRight = event.key === 'ArrowRight'
|
|
39
|
+
const isHome = event.key === 'Home'
|
|
40
|
+
const isEnd = event.key === 'End'
|
|
41
|
+
// Ignore non-navigation keys
|
|
42
|
+
if (!isLeft && !isRight && !isHome && !isEnd) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { chips } = this as { chips: MaybeMultiActionChip[] }
|
|
47
|
+
// Don't try to select another chip if there aren't any.
|
|
48
|
+
if (chips.length < 2) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Prevent default interactions, such as scrolling.
|
|
53
|
+
event.preventDefault()
|
|
54
|
+
|
|
55
|
+
if (isHome || isEnd) {
|
|
56
|
+
const index = isHome ? 0 : chips.length - 1
|
|
57
|
+
chips[index].focus({ trailing: isEnd })
|
|
58
|
+
this.updateTabIndices()
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check if moving forwards or backwards
|
|
63
|
+
const isRtl = getComputedStyle(this).direction === 'rtl'
|
|
64
|
+
const forwards = isRtl ? isLeft : isRight
|
|
65
|
+
const focusedChip = chips.find((chip) => chip.matches(':focus-within'))
|
|
66
|
+
if (!focusedChip) {
|
|
67
|
+
// If there is not already a chip focused, select the first or last chip
|
|
68
|
+
// based on the direction we're traveling.
|
|
69
|
+
const nextChip = forwards ? chips[0] : chips[chips.length - 1]
|
|
70
|
+
nextChip.focus({ trailing: !forwards })
|
|
71
|
+
this.updateTabIndices()
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const currentIndex = chips.indexOf(focusedChip)
|
|
76
|
+
let nextIndex = forwards ? currentIndex + 1 : currentIndex - 1
|
|
77
|
+
// Search for the next sibling that is not disabled to select.
|
|
78
|
+
// If we return to the host index, there is nothing to select.
|
|
79
|
+
while (nextIndex !== currentIndex) {
|
|
80
|
+
if (nextIndex >= chips.length) {
|
|
81
|
+
// Return to start if moving past the last item.
|
|
82
|
+
nextIndex = 0
|
|
83
|
+
} else if (nextIndex < 0) {
|
|
84
|
+
// Go to end if moving before the first item.
|
|
85
|
+
nextIndex = chips.length - 1
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Check if the next sibling is disabled. If so,
|
|
89
|
+
// move the index and continue searching.
|
|
90
|
+
//
|
|
91
|
+
// Some toolbar items may be focusable when disabled for increased
|
|
92
|
+
// visibility.
|
|
93
|
+
const nextChip = chips[nextIndex]
|
|
94
|
+
if (nextChip.disabled) {
|
|
95
|
+
if (forwards) {
|
|
96
|
+
nextIndex++
|
|
97
|
+
} else {
|
|
98
|
+
nextIndex--
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
continue
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
nextChip.focus({ trailing: !forwards })
|
|
105
|
+
this.updateTabIndices()
|
|
106
|
+
break
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private updateTabIndices() {
|
|
111
|
+
// The chip that should be focusable is either the chip that currently has
|
|
112
|
+
// focus or the first chip that can be focused.
|
|
113
|
+
const { chips } = this
|
|
114
|
+
let chipToFocus: Chip | undefined
|
|
115
|
+
for (const chip of chips) {
|
|
116
|
+
const isChipFocusable = !chip.disabled
|
|
117
|
+
const chipIsFocused = chip.matches(':focus-within')
|
|
118
|
+
if (chipIsFocused && isChipFocusable) {
|
|
119
|
+
// Found the first chip that is actively focused. This overrides the
|
|
120
|
+
// first focusable chip found.
|
|
121
|
+
chipToFocus = chip
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (isChipFocusable && !chipToFocus) {
|
|
126
|
+
chipToFocus = chip
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Disable non-focused chips. If we disable all of them, we'll grant focus
|
|
130
|
+
// to the first focusable child that was found.
|
|
131
|
+
chip.tabIndex = -1
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (chipToFocus) {
|
|
135
|
+
chipToFocus.tabIndex = 0
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
interface MaybeMultiActionChip extends Chip {
|
|
141
|
+
focus(options?: FocusOptions & { trailing?: boolean }): void
|
|
142
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CSSResultOrNative } from 'lit'
|
|
2
|
+
import { customElement } from 'lit/decorators.js'
|
|
3
|
+
import Element from './internals/ChipSet.js'
|
|
4
|
+
import styles from './internals/ChipSet.styles.js'
|
|
5
|
+
|
|
6
|
+
@customElement('ui-chip-set')
|
|
7
|
+
export class UiChipSetElement extends Element {
|
|
8
|
+
static override styles: CSSResultOrNative[] = [styles]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
interface HTMLElementTagNameMap {
|
|
13
|
+
'ui-chip-set': UiChipSetElement
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -9,9 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
export enum Easing {
|
|
11
11
|
STANDARD = 'cubic-bezier(0.2, 0, 0, 1)',
|
|
12
|
-
ACCELERATION = 'cubic-bezier(0.
|
|
13
|
-
DECELERATION = 'cubic-bezier(0, 0, 0
|
|
12
|
+
ACCELERATION = 'cubic-bezier(0.3, 0, 1, 1)',
|
|
13
|
+
DECELERATION = 'cubic-bezier(0, 0, 0, 1)',
|
|
14
14
|
SHARP = 'cubic-bezier(0.4, 0, 0.6, 1)',
|
|
15
|
+
EMPHASIZED_ACCELERATE = 'cubic-bezier(0.3, 0, 0.8, 0.15)',
|
|
16
|
+
EMPHASIZED_DECELERATE = 'cubic-bezier(0.05, 0.7, 0.1, 1)',
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -22,7 +22,8 @@ export default css`
|
|
|
22
22
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
.surface::before
|
|
25
|
+
.surface::before,
|
|
26
|
+
.surface::after {
|
|
26
27
|
position: absolute;
|
|
27
28
|
opacity: 0;
|
|
28
29
|
pointer-events: none;
|
|
@@ -37,10 +38,19 @@ export default css`
|
|
|
37
38
|
inset: 0;
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
.surface::after {
|
|
42
|
+
transition: opacity 375ms linear;
|
|
43
|
+
transform-origin: center center;
|
|
44
|
+
}
|
|
45
|
+
|
|
40
46
|
.focused::before {
|
|
41
47
|
transition-duration: 75ms;
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
.pressed::after {
|
|
51
|
+
transition-duration: 105ms;
|
|
52
|
+
}
|
|
53
|
+
|
|
44
54
|
.surface {
|
|
45
55
|
border-radius: var(--md-ripple-state-layer-shape, 0);
|
|
46
56
|
}
|
|
@@ -49,6 +59,14 @@ export default css`
|
|
|
49
59
|
background-color: var(--md-ripple-hover-state-layer-color, var(--md-sys-color-primary));
|
|
50
60
|
}
|
|
51
61
|
|
|
62
|
+
.surface::after {
|
|
63
|
+
background: radial-gradient(
|
|
64
|
+
closest-side,
|
|
65
|
+
var(--md-ripple-pressed-state-layer-color, var(--md-sys-color-primary)) max(100% - 70px, 65%),
|
|
66
|
+
transparent 100%
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
.surface.hovered::before {
|
|
53
71
|
opacity: var(--md-ripple-hover-state-layer-opacity, var(--md-sys-state-hover-state-layer-opacity));
|
|
54
72
|
background-color: var(--md-ripple-hover-state-layer-color, var(--md-sys-color-primary));
|
|
@@ -59,14 +77,8 @@ export default css`
|
|
|
59
77
|
background-color: var(--md-ripple-focus-state-layer-color, var(--md-sys-color-primary));
|
|
60
78
|
}
|
|
61
79
|
|
|
62
|
-
.surface.pressed::
|
|
80
|
+
.surface.pressed::after {
|
|
63
81
|
opacity: var(--md-ripple-pressed-state-layer-opacity, var(--md-sys-state-pressed-state-layer-opacity));
|
|
64
|
-
/* background-color: var(--md-ripple-pressed-state-layer-color, var(--md-sys-color-primary)); */
|
|
65
|
-
background: radial-gradient(
|
|
66
|
-
closest-side,
|
|
67
|
-
var(--md-ripple-pressed-state-layer-color, var(--md-sys-color-primary)) max(100% - 70px, 65%),
|
|
68
|
-
transparent 100%
|
|
69
|
-
);
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
.surface.unbounded {
|
|
@@ -35,7 +35,7 @@ export default css`
|
|
|
35
35
|
|
|
36
36
|
border: 2px var(--md-sys-color-outline) solid;
|
|
37
37
|
border-radius: var(--md-sys-shape-corner-extra-large);
|
|
38
|
-
background-color: var(--md-sys-color-surface-
|
|
38
|
+
background-color: var(--md-sys-color-surface-container-highest);
|
|
39
39
|
|
|
40
40
|
transition:
|
|
41
41
|
opacity 90ms cubic-bezier(0.4, 0, 0.2, 1),
|
|
@@ -2,9 +2,15 @@ import { css } from 'lit'
|
|
|
2
2
|
|
|
3
3
|
export default css`
|
|
4
4
|
:host {
|
|
5
|
-
display: inline-
|
|
6
|
-
vertical-align:
|
|
5
|
+
display: inline-flex;
|
|
6
|
+
vertical-align: middle;
|
|
7
|
+
align-items: center;
|
|
8
|
+
justify-content: center;
|
|
9
|
+
|
|
10
|
+
position: relative;
|
|
7
11
|
box-sizing: content-box;
|
|
12
|
+
padding: 0 16px;
|
|
13
|
+
|
|
8
14
|
outline: none;
|
|
9
15
|
text-rendering: auto;
|
|
10
16
|
cursor: default;
|
|
@@ -18,55 +24,48 @@ export default css`
|
|
|
18
24
|
letter-spacing: var(--md-sys-typescale-title-small-tracking);
|
|
19
25
|
line-height: var(--md-sys-typescale-title-small-height);
|
|
20
26
|
|
|
21
|
-
--md-ripple-hover-state-layer-color:
|
|
22
|
-
--md-ripple-focus-state-layer-color:
|
|
23
|
-
--md-ripple-pressed-state-layer-color:
|
|
24
|
-
|
|
25
|
-
color: var(--md-sys-color-
|
|
27
|
+
--md-ripple-hover-state-layer-color: currentColor;
|
|
28
|
+
--md-ripple-focus-state-layer-color: currentColor;
|
|
29
|
+
--md-ripple-pressed-state-layer-color: currentColor;
|
|
30
|
+
|
|
31
|
+
--_background-color: var(--md-sys-color-surface);
|
|
32
|
+
--_color: var(--md-sys-color-on-surface-variant);
|
|
33
|
+
--_container-height: 48px;
|
|
34
|
+
--_active-indicator-color: var(--md-sys-color-primary);
|
|
35
|
+
--_active-indicator-shape: var(--md-primary-tab-active-indicator-shape, 3px 3px 0px 0px);
|
|
36
|
+
--_active-indicator-height: 2px;
|
|
37
|
+
--_active-indicator-opacity: 0;
|
|
38
|
+
--_with-icon-and-label-text-container-height: var(--md-primary-tab-with-icon-and-label-text-container-height, 64px);
|
|
39
|
+
--_icon-size: var(--md-secondary-tab-icon-size, 24px);
|
|
40
|
+
|
|
41
|
+
background-color: var(--_background-color);
|
|
42
|
+
color: var(--_color);
|
|
26
43
|
}
|
|
27
44
|
|
|
28
45
|
:host([hidden]) {
|
|
29
46
|
display: none;
|
|
30
47
|
}
|
|
31
48
|
|
|
32
|
-
.
|
|
33
|
-
|
|
34
|
-
height: inherit;
|
|
35
|
-
position: relative;
|
|
36
|
-
border-radius: inherit;
|
|
37
|
-
display: flex;
|
|
38
|
-
justify-content: center;
|
|
39
|
-
align-items: center;
|
|
40
|
-
height: 48px;
|
|
49
|
+
.ripple.activated {
|
|
50
|
+
z-index: 1;
|
|
41
51
|
}
|
|
42
52
|
|
|
43
|
-
.
|
|
44
|
-
|
|
53
|
+
.focus-ring {
|
|
54
|
+
border-radius: var(--md-sys-shape-corner-small);
|
|
55
|
+
transition: margin-bottom var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard);
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
inset: 0;
|
|
50
|
-
z-index: 1;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.content {
|
|
54
|
-
z-index: 2;
|
|
55
|
-
display: flex;
|
|
56
|
-
flex-direction: column;
|
|
57
|
-
justify-content: center;
|
|
58
|
-
align-items: center;
|
|
59
|
-
padding: 0px 20px;
|
|
58
|
+
:host([selected]) .focus-ring {
|
|
59
|
+
margin-bottom: calc(var(--_active-indicator-height) + 1px);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
.tab-content {
|
|
63
63
|
display: flex;
|
|
64
64
|
align-items: center;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
z-index: 3;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
flex-direction: row;
|
|
67
|
+
gap: 8px;
|
|
68
|
+
height: var(--_container-height);
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
.icon ::slotted(*) {
|
|
@@ -82,54 +81,60 @@ export default css`
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
:host([selected]) {
|
|
85
|
-
|
|
84
|
+
--_active-indicator-opacity: 1;
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
bottom: 0px;
|
|
91
|
-
left: 0px;
|
|
92
|
-
right: 0px;
|
|
93
|
-
height: 2px;
|
|
94
|
-
display: flex;
|
|
95
|
-
justify-content: center;
|
|
87
|
+
:host([priority='primary']) {
|
|
88
|
+
--_color: var(--md-primary-tab-label-text-color, var(--md-sys-color-on-surface-variant));
|
|
96
89
|
}
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
:host([priority='secondary']) {
|
|
92
|
+
--_color: var(--md-secondary-tab-label-text-color, var(--md-sys-color-on-surface-variant));
|
|
100
93
|
}
|
|
101
94
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
display: block;
|
|
105
|
-
background-color: var(--md-sys-color-primary);
|
|
95
|
+
:host([selected][priority='primary']) {
|
|
96
|
+
--_color: var(--md-primary-tab-active-label-text-color, var(--md-sys-color-primary));
|
|
106
97
|
}
|
|
107
98
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
width: 40px;
|
|
99
|
+
:host([selected][priority='secondary']) {
|
|
100
|
+
--_color: var(--md-secondary-tab-active-label-text-color, var(--md-sys-color-on-surface));
|
|
111
101
|
}
|
|
112
102
|
|
|
113
|
-
.
|
|
114
|
-
|
|
103
|
+
.surface {
|
|
104
|
+
position: relative;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.indicator {
|
|
108
|
+
position: absolute;
|
|
109
|
+
box-sizing: border-box;
|
|
110
|
+
transform-origin: bottom left;
|
|
111
|
+
background: var(--_active-indicator-color);
|
|
112
|
+
border-radius: var(--_active-indicator-shape);
|
|
113
|
+
height: var(--_active-indicator-height);
|
|
114
|
+
inset: auto 0 0 0;
|
|
115
|
+
opacity: var(--_active-indicator-opacity);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
:host([priority='primary']) {
|
|
119
|
+
--_active-indicator-height: 3px;
|
|
120
|
+
--_active-indicator-shape: 3px 3px 0px 0px;
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
:host([disabled]) {
|
|
118
124
|
pointer-events: none;
|
|
119
125
|
}
|
|
120
126
|
|
|
121
|
-
:host([disabled])
|
|
122
|
-
|
|
127
|
+
:host([disabled]) {
|
|
128
|
+
--_color: var(--md-sys-color-on-surface);
|
|
123
129
|
opacity: 0.38;
|
|
124
130
|
}
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
132
|
+
.stacked .tab-content {
|
|
133
|
+
flex-direction: column;
|
|
134
|
+
gap: 2px;
|
|
129
135
|
}
|
|
130
136
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
opacity: var(--md-sys-state-focus-state-layer-opacity);
|
|
137
|
+
.stacked.has-icon.has-label .tab-content {
|
|
138
|
+
height: var(--_with-icon-and-label-text-container-height);
|
|
134
139
|
}
|
|
135
140
|
`
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { html, nothing, TemplateResult } from 'lit'
|
|
2
|
-
import { property,
|
|
1
|
+
import { html, nothing, PropertyValues, TemplateResult } from 'lit'
|
|
2
|
+
import { property, query, state } from 'lit/decorators.js'
|
|
3
3
|
import { ClassInfo, classMap } from 'lit/directives/class-map.js'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import UiRipple from '../../ripple/internals/ripple.js'
|
|
7
|
-
import { ripple } from '../../effects/rippleDirective.js'
|
|
4
|
+
import type { BeginPressConfig, EndPressConfig } from '../../../controllers/ActionController.js'
|
|
5
|
+
import type UiRipple from '../../ripple/internals/ripple.js'
|
|
8
6
|
import { isDisabled, setDisabled } from '../../../lib/disabled.js'
|
|
9
7
|
import { UiElement } from '../../UiElement.js'
|
|
10
|
-
import type { TabsPriority } from './Tabs.js'
|
|
8
|
+
import type { SizingInfo, TabsPriority } from './Tabs.js'
|
|
11
9
|
import { Easing } from '../../motion/animation.js'
|
|
12
10
|
import '../../ripple/ui-ripple.js'
|
|
13
11
|
|
|
@@ -32,31 +30,38 @@ export default class UiTab extends UiElement {
|
|
|
32
30
|
* in the `ui-tabs`. This is only to render the tab in the selected state.
|
|
33
31
|
* @attribute
|
|
34
32
|
*/
|
|
35
|
-
@property({ reflect: true, type: Boolean }) accessor selected
|
|
33
|
+
@property({ reflect: true, type: Boolean }) accessor selected
|
|
36
34
|
|
|
37
35
|
/**
|
|
38
36
|
* @attribute
|
|
39
37
|
*/
|
|
40
|
-
@property({ reflect: true, type:
|
|
38
|
+
@property({ reflect: true, type: String }) accessor priority: TabsPriority
|
|
41
39
|
|
|
42
40
|
/**
|
|
43
41
|
* @attribute
|
|
44
42
|
*/
|
|
45
|
-
@property({ reflect: true, type: Boolean }) accessor indicated
|
|
43
|
+
@property({ reflect: true, type: Boolean }) accessor indicated
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Indicates whether the tab has an icon.
|
|
47
|
+
* This is set automatically when the "icon" slot is populated.
|
|
48
|
+
*/
|
|
49
|
+
@state() protected accessor hasIcon
|
|
50
|
+
/**
|
|
51
|
+
* Indicates whether the tab only has an icon and no text.
|
|
52
|
+
* This is set automatically when the default slot is populated with only an icon.
|
|
53
|
+
*/
|
|
54
|
+
@state() protected accessor iconOnly
|
|
50
55
|
|
|
51
|
-
@
|
|
52
|
-
|
|
53
|
-
protected readonly getRipple = (): Promise<UiRipple | null> => {
|
|
54
|
-
this.showRipple = true
|
|
55
|
-
return this.ripple
|
|
56
|
-
}
|
|
56
|
+
@query('ui-ripple') protected accessor ripple!: UiRipple | null
|
|
57
57
|
|
|
58
58
|
constructor() {
|
|
59
59
|
super()
|
|
60
|
+
this.priority = 'primary'
|
|
61
|
+
this.selected = false
|
|
62
|
+
this.indicated = false
|
|
63
|
+
this.hasIcon = false
|
|
64
|
+
this.iconOnly = true
|
|
60
65
|
this.actionController.cancelKeyboardEvents = true
|
|
61
66
|
this.addEventListener('keydown', this.handleKeyDown.bind(this))
|
|
62
67
|
this.addEventListener('keyup', this.handleKeyUp.bind(this))
|
|
@@ -65,6 +70,7 @@ export default class UiTab extends UiElement {
|
|
|
65
70
|
this.addEventListener('pointerup', this.handlePointerUp.bind(this))
|
|
66
71
|
this.addEventListener('pointercancel', this.handlePointerCancel.bind(this))
|
|
67
72
|
this.addEventListener('pointerleave', this.handlePointerLeave.bind(this))
|
|
73
|
+
this.addEventListener('pointerenter', this.handlePointerEnter.bind(this))
|
|
68
74
|
this.addEventListener('contextmenu', this.handleContextMenu.bind(this))
|
|
69
75
|
}
|
|
70
76
|
|
|
@@ -78,8 +84,23 @@ export default class UiTab extends UiElement {
|
|
|
78
84
|
}
|
|
79
85
|
}
|
|
80
86
|
|
|
87
|
+
protected override willUpdate(cp: PropertyValues<this>): void {
|
|
88
|
+
super.willUpdate(cp)
|
|
89
|
+
if (cp.has('selected')) {
|
|
90
|
+
this.setAttribute('aria-selected', String(this.selected))
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override beginPress(options: BeginPressConfig): void {
|
|
95
|
+
super.beginPress(options)
|
|
96
|
+
this.classList.add('pressed')
|
|
97
|
+
this.ripple?.beginPress(options.positionEvent)
|
|
98
|
+
}
|
|
99
|
+
|
|
81
100
|
override endPress(config: EndPressConfig): void {
|
|
82
101
|
super.endPress(config)
|
|
102
|
+
this.classList.remove('pressed')
|
|
103
|
+
this.ripple?.endPress()
|
|
83
104
|
const { cancelled, reason } = config
|
|
84
105
|
if (cancelled) {
|
|
85
106
|
return
|
|
@@ -89,18 +110,77 @@ export default class UiTab extends UiElement {
|
|
|
89
110
|
}
|
|
90
111
|
}
|
|
91
112
|
|
|
113
|
+
override handleClick(e: MouseEvent): void {
|
|
114
|
+
super.handleClick(e)
|
|
115
|
+
if (this.disabled) {
|
|
116
|
+
e.preventDefault()
|
|
117
|
+
e.stopPropagation()
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
this.endPress({ cancelled: false, actionData: { event: e } })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override handlePointerEnter(e: PointerEvent): void {
|
|
124
|
+
super.handlePointerEnter(e)
|
|
125
|
+
if (this.ripple) {
|
|
126
|
+
this.ripple.beginHover(e)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override handlePointerLeave(e: PointerEvent): void {
|
|
131
|
+
super.handlePointerLeave(e)
|
|
132
|
+
if (this.ripple) {
|
|
133
|
+
this.ripple.endHover()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
override handleKeyUp(e: KeyboardEvent): void {
|
|
138
|
+
super.handleKeyUp(e)
|
|
139
|
+
if (this.disabled) {
|
|
140
|
+
e.preventDefault()
|
|
141
|
+
e.stopPropagation()
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
92
146
|
/**
|
|
93
147
|
* Sets the `_hasIcon` state property when the "icon" slot change event is dispatched.
|
|
94
148
|
*/
|
|
95
|
-
protected
|
|
149
|
+
protected handleIconSlotChange(e: Event): void {
|
|
96
150
|
const slot = e.target as HTMLSlotElement
|
|
97
151
|
this.hasIcon = !!slot.assignedNodes().length
|
|
98
152
|
}
|
|
99
153
|
|
|
154
|
+
protected handleSlotChange(e: Event): void {
|
|
155
|
+
const slot = e.target as HTMLSlotElement
|
|
156
|
+
// Check if there's any label text or elements. If not, then there is only
|
|
157
|
+
// an icon.
|
|
158
|
+
for (const node of slot.assignedNodes()) {
|
|
159
|
+
const hasTextContent = node.nodeType === Node.TEXT_NODE && !!(node as Text).wholeText.match(/\S/)
|
|
160
|
+
if (node.nodeType === Node.ELEMENT_NODE || hasTextContent) {
|
|
161
|
+
this.iconOnly = false
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
this.iconOnly = true
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
public getIndicatorSizing(): SizingInfo {
|
|
169
|
+
const element = this.priority === 'primary' ? this.shadowRoot?.querySelector('.tab-content') : this
|
|
170
|
+
if (!element) {
|
|
171
|
+
return { width: 0, left: 0 }
|
|
172
|
+
}
|
|
173
|
+
const rect = element.getBoundingClientRect()
|
|
174
|
+
return {
|
|
175
|
+
width: rect.width,
|
|
176
|
+
left: rect.left,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
100
180
|
/**
|
|
101
181
|
* When `indicated` is `true` it animates the indicator to highlight the position of the tab.
|
|
102
182
|
*/
|
|
103
|
-
highlight(): void {
|
|
183
|
+
public highlight(): void {
|
|
104
184
|
if (!this.indicated) {
|
|
105
185
|
return
|
|
106
186
|
}
|
|
@@ -128,36 +208,41 @@ export default class UiTab extends UiElement {
|
|
|
128
208
|
}
|
|
129
209
|
|
|
130
210
|
protected override render(): TemplateResult {
|
|
131
|
-
const
|
|
211
|
+
const isPrimary = this.priority === 'primary'
|
|
132
212
|
const containerClasses = classMap({
|
|
133
|
-
surface: true,
|
|
134
|
-
|
|
135
|
-
|
|
213
|
+
'surface': true,
|
|
214
|
+
'has-icon': this.hasIcon,
|
|
215
|
+
'has-label': !this.iconOnly,
|
|
216
|
+
'stacked': isPrimary,
|
|
136
217
|
})
|
|
137
218
|
return html`
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
<div class="content">
|
|
219
|
+
${this.renderFocusRing()} ${this.renderRipple()}
|
|
220
|
+
<div class="${containerClasses}">
|
|
221
|
+
<div class="tab-content">
|
|
142
222
|
${this.renderIcon()}
|
|
143
|
-
<
|
|
223
|
+
<slot @slotchange="${this.handleSlotChange}"></slot>
|
|
144
224
|
</div>
|
|
145
|
-
${this.renderIndicator()}
|
|
225
|
+
${isPrimary ? this.renderIndicator() : nothing}
|
|
146
226
|
</div>
|
|
227
|
+
${!isPrimary ? this.renderIndicator() : nothing}
|
|
147
228
|
`
|
|
148
229
|
}
|
|
149
230
|
|
|
150
|
-
protected
|
|
151
|
-
return html
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
231
|
+
protected renderFocusRing(): TemplateResult {
|
|
232
|
+
return html`<md-focus-ring
|
|
233
|
+
part="focus-ring"
|
|
234
|
+
class="focus-ring"
|
|
235
|
+
inward
|
|
236
|
+
.control="${this as HTMLElement}"
|
|
237
|
+
></md-focus-ring>`
|
|
156
238
|
}
|
|
157
239
|
|
|
158
|
-
protected renderRipple
|
|
159
|
-
|
|
160
|
-
|
|
240
|
+
protected renderRipple(): TemplateResult {
|
|
241
|
+
return html`<ui-ripple class="ripple" ?disabled="${this.disabled}"></ui-ripple>`
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
protected renderIcon(): TemplateResult {
|
|
245
|
+
return html` <slot name="icon" @slotchange="${this.handleIconSlotChange}"></slot> `
|
|
161
246
|
}
|
|
162
247
|
|
|
163
248
|
protected renderIndicator(): TemplateResult | typeof nothing {
|
|
@@ -169,8 +254,6 @@ export default class UiTab extends UiElement {
|
|
|
169
254
|
indicator: true,
|
|
170
255
|
primary: priority === 'primary',
|
|
171
256
|
}
|
|
172
|
-
return html`<div class="${classMap(classes)}"
|
|
173
|
-
<span role="presentation" class="pointer"></span>
|
|
174
|
-
</div>`
|
|
257
|
+
return html`<div class="${classMap(classes)}"></div>`
|
|
175
258
|
}
|
|
176
259
|
}
|