kmaterialize 2.3.3-1.0.10 → 2.3.3-1.0.12
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/components/button/_buttons.scss +16 -0
- package/components/card/_cards.scss +6 -0
- package/components/collapsible/_collapsible.scss +49 -0
- package/components/list/_collection.scss +9 -2
- package/components/switch/_switches.scss +5 -0
- package/components/toolbar/_toolbar.scss +176 -0
- package/components/toolbar/toolbar.ts +137 -0
- package/dist/css/materialize.colors.min.css +2 -2
- package/dist/css/materialize.css +8741 -286
- package/dist/css/materialize.min.css +2 -2
- package/dist/css/materialize.min.css.map +1 -1
- package/dist/js/materialize.cjs.js +168 -43
- package/dist/js/materialize.d.ts +30 -2
- package/dist/js/materialize.js +168 -43
- package/dist/js/materialize.min.js +2 -2
- package/dist/js/materialize.mjs +168 -44
- package/package.json +1 -1
- package/sass/_colors.scss +182 -0
- package/sass/_global.scss +240 -1
- package/sass/materialize.scss +1 -0
|
@@ -256,6 +256,22 @@ button.btn-floating {
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
260
|
+
// Anchors the FAB (and its expanded menu) to the top of the viewport
|
|
261
|
+
// instead of the bottom, for layouts where the bottom edge is already
|
|
262
|
+
// occupied (e.g. a footer or bottom nav).
|
|
263
|
+
&.top {
|
|
264
|
+
top: 23px;
|
|
265
|
+
bottom: unset;
|
|
266
|
+
padding-bottom: 15px;
|
|
267
|
+
padding-top: 0;
|
|
268
|
+
|
|
269
|
+
> ul {
|
|
270
|
+
top: 64px;
|
|
271
|
+
bottom: unset;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
259
275
|
&.toolbar {
|
|
260
276
|
padding: 0;
|
|
261
277
|
height: 56px;
|
|
@@ -168,6 +168,12 @@
|
|
|
168
168
|
|
|
169
169
|
i {
|
|
170
170
|
line-height: 32px;
|
|
171
|
+
|
|
172
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
173
|
+
// Sits on the title's baseline with a small gap before the text,
|
|
174
|
+
// instead of floating flush against it mid-line.
|
|
175
|
+
vertical-align: text-bottom;
|
|
176
|
+
margin-right: 6px;
|
|
171
177
|
}
|
|
172
178
|
|
|
173
179
|
&.activator {
|
|
@@ -9,7 +9,19 @@
|
|
|
9
9
|
border-right: 1px solid var(--md-sys-color-outline-variant);
|
|
10
10
|
border-left: 1px solid var(--md-sys-color-outline-variant);
|
|
11
11
|
margin: $element-top-margin 0 $element-bottom-margin 0;
|
|
12
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
13
|
+
// Matches the 12px corner radius used across cards, dropdowns and
|
|
14
|
+
// collections instead of square corners.
|
|
15
|
+
border-radius: 12px;
|
|
12
16
|
@extend .z-depth-1;
|
|
17
|
+
|
|
18
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
19
|
+
// Only clip to the rounded corners while every item is closed - an open
|
|
20
|
+
// last item's body needs to be able to grow past the list's own bottom
|
|
21
|
+
// edge instead of being cut off by it.
|
|
22
|
+
&:not(:has(li:last-child.active)) {
|
|
23
|
+
overflow: hidden;
|
|
24
|
+
}
|
|
13
25
|
}
|
|
14
26
|
|
|
15
27
|
.collapsible-header {
|
|
@@ -62,6 +74,43 @@
|
|
|
62
74
|
overflow: hidden;
|
|
63
75
|
}
|
|
64
76
|
|
|
77
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
78
|
+
// Round the corners of the first/last item's own header/body so they
|
|
79
|
+
// follow the list's rounded outline instead of showing a square edge
|
|
80
|
+
// inside it. Longhand corners (not the `border-radius` shorthand) so that
|
|
81
|
+
// a single-item accordion - where the same <li> is both first and last -
|
|
82
|
+
// gets all 4 corners rounded instead of the last-child rule's shorthand
|
|
83
|
+
// wiping out the top corners the first-child rule just set.
|
|
84
|
+
.collapsible > li:first-child .collapsible-header {
|
|
85
|
+
border-top-left-radius: 12px;
|
|
86
|
+
border-top-right-radius: 12px;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.collapsible > li:last-child {
|
|
90
|
+
&:not(.active) .collapsible-header {
|
|
91
|
+
border-bottom-left-radius: 12px;
|
|
92
|
+
border-bottom-right-radius: 12px;
|
|
93
|
+
transition-delay: 250ms;
|
|
94
|
+
transition-property: border-radius;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
&.active .collapsible-body {
|
|
98
|
+
border-radius: 0 0 12px 12px;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// CSS resolves border-radius per-corner, not per-rule: once the
|
|
103
|
+
// :not(.active) rule above stops matching, nothing here fixes the bottom
|
|
104
|
+
// corners back to square, so a more specific rule elsewhere (e.g. the
|
|
105
|
+
// sidenav's `li > a { border-radius: 100px; }` pill styling, which also
|
|
106
|
+
// matches .collapsible-header since it's an <a> inside a sidenav <li>)
|
|
107
|
+
// is free to fill that gap and leave the header rounded on all 4 corners
|
|
108
|
+
// while expanded. Force it explicitly instead of leaving it implicit.
|
|
109
|
+
.collapsible > li.active .collapsible-header {
|
|
110
|
+
border-bottom-left-radius: 0;
|
|
111
|
+
border-bottom-right-radius: 0;
|
|
112
|
+
}
|
|
113
|
+
|
|
65
114
|
// Popout Collapsible
|
|
66
115
|
|
|
67
116
|
.collapsible.popout {
|
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
background-color: var(--md-sys-color-surface);
|
|
7
7
|
margin: $element-top-margin 0 $element-bottom-margin 0;
|
|
8
8
|
border: 1px solid var(--md-sys-color-outline-variant);
|
|
9
|
-
|
|
9
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
10
|
+
// Matches the 12px corner radius used across cards, dropdowns and
|
|
11
|
+
// collapsibles instead of the near-square 2px default.
|
|
12
|
+
border-radius: 12px;
|
|
10
13
|
overflow: hidden;
|
|
11
14
|
position: relative;
|
|
12
15
|
|
|
@@ -90,7 +93,11 @@
|
|
|
90
93
|
|
|
91
94
|
&.with-header {
|
|
92
95
|
.collection-header {
|
|
93
|
-
|
|
96
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
97
|
+
// A tinted header reads as its own row instead of blending into the
|
|
98
|
+
// list; it also isn't text, so it shouldn't be selectable.
|
|
99
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
100
|
+
user-select: none;
|
|
94
101
|
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
95
102
|
padding: 10px 20px;
|
|
96
103
|
}
|
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
opacity: 0;
|
|
24
24
|
width: 0;
|
|
25
25
|
height: 0;
|
|
26
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/component_enhance.scss
|
|
27
|
+
// Without this, an inherited padding can push the (invisible, 0x0)
|
|
28
|
+
// checkbox out of alignment with the lever it's meant to sit under.
|
|
29
|
+
box-sizing: border-box;
|
|
30
|
+
padding: 0 !important;
|
|
26
31
|
|
|
27
32
|
// CHECKED
|
|
28
33
|
// Track
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
@use '../../sass/variables' as *;
|
|
2
|
+
|
|
3
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Front/style/scss/partial/_planning_toolbar.scss
|
|
4
|
+
// A rounded "pill" action bar: a full-width flex row split into groups
|
|
5
|
+
// (buttons, a segmented "track" control, an expandable search field) laid
|
|
6
|
+
// out with space-between, sitting inline above the content it controls -
|
|
7
|
+
// not fixed/sticky, just a normal flex row like the source component.
|
|
8
|
+
//
|
|
9
|
+
// :not(.fixed-action-btn) - "toolbar" is already a FAB display mode
|
|
10
|
+
// (.fixed-action-btn.toolbar, see components/button/_buttons.scss); this
|
|
11
|
+
// guards against also painting pill styles onto that unrelated element.
|
|
12
|
+
.toolbar:not(.fixed-action-btn) {
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
width: 100%;
|
|
17
|
+
gap: 8px;
|
|
18
|
+
padding: 5px 8px;
|
|
19
|
+
margin: $element-top-margin 0 $element-bottom-margin 0;
|
|
20
|
+
border-radius: 27px;
|
|
21
|
+
background-color: var(--md-sys-color-surface-variant);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// A cluster of controls inside the toolbar (buttons, a track, a search
|
|
25
|
+
// field, ...) kept together and spaced from the next group.
|
|
26
|
+
.toolbar-group {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 5px;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/// Track (segmented control)
|
|
33
|
+
///////////////////////////////////////////////////////
|
|
34
|
+
///
|
|
35
|
+
|
|
36
|
+
// A pill-shaped segmented control - e.g. "Today / Previous / Next" or a
|
|
37
|
+
// "Week / Month / Year" range picker - with a sliding highlight behind
|
|
38
|
+
// whichever item is active.
|
|
39
|
+
.toolbar-track {
|
|
40
|
+
position: relative;
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
height: 34px;
|
|
44
|
+
padding: 3px;
|
|
45
|
+
border-radius: 999px;
|
|
46
|
+
background-color: var(--md-sys-color-surface);
|
|
47
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// A track with no background/border of its own - just for spacing a group
|
|
51
|
+
// of items that don't need the pill-within-a-pill look.
|
|
52
|
+
.toolbar-track.toolbar-track-bare {
|
|
53
|
+
padding: 3px 0;
|
|
54
|
+
background-color: transparent;
|
|
55
|
+
border-color: transparent;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Positioned/sized by Toolbar's JS (offsetLeft/offsetWidth of the active
|
|
59
|
+
// .toolbar-track-item), not by CSS - the transition below is what makes
|
|
60
|
+
// that JS-driven move/resize animate instead of jumping.
|
|
61
|
+
.toolbar-track-indicator {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: 3px;
|
|
64
|
+
bottom: 3px;
|
|
65
|
+
left: 0;
|
|
66
|
+
z-index: 0;
|
|
67
|
+
width: 0;
|
|
68
|
+
border-radius: 999px;
|
|
69
|
+
background-color: var(--md-sys-color-primary);
|
|
70
|
+
opacity: 0;
|
|
71
|
+
pointer-events: none;
|
|
72
|
+
transition: transform .22s cubic-bezier(.2, .7, .3, 1), width .22s cubic-bezier(.2, .7, .3, 1), opacity .14s ease;
|
|
73
|
+
|
|
74
|
+
// Only fade in once Toolbar's JS has measured and placed it for the
|
|
75
|
+
// first time - otherwise it flashes at 0 width in the top-left corner
|
|
76
|
+
// for a frame before the first real position is set.
|
|
77
|
+
.toolbar-track.is-armed & {
|
|
78
|
+
opacity: 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.toolbar-track-item {
|
|
83
|
+
position: relative;
|
|
84
|
+
z-index: 1;
|
|
85
|
+
display: inline-flex;
|
|
86
|
+
align-items: center;
|
|
87
|
+
height: 28px;
|
|
88
|
+
padding: 0 13px;
|
|
89
|
+
border: 0;
|
|
90
|
+
border-radius: 999px;
|
|
91
|
+
background-color: transparent;
|
|
92
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
93
|
+
font-size: 12.5px;
|
|
94
|
+
line-height: 1;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
cursor: pointer;
|
|
97
|
+
transition: color .16s ease;
|
|
98
|
+
|
|
99
|
+
&:hover {
|
|
100
|
+
color: var(--md-sys-color-on-surface);
|
|
101
|
+
}
|
|
102
|
+
&.is-active {
|
|
103
|
+
font-weight: 600;
|
|
104
|
+
color: var(--md-sys-color-on-primary);
|
|
105
|
+
}
|
|
106
|
+
&:focus-visible {
|
|
107
|
+
outline: 2px solid var(--md-sys-color-primary);
|
|
108
|
+
outline-offset: 2px;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/// Expandable search
|
|
113
|
+
///////////////////////////////////////////////////////
|
|
114
|
+
///
|
|
115
|
+
|
|
116
|
+
// A search field that starts as just an icon button and expands into a
|
|
117
|
+
// full text input once focused (or once it has a value) - Toolbar's JS
|
|
118
|
+
// toggles the .is-expanded class.
|
|
119
|
+
.toolbar-search {
|
|
120
|
+
display: inline-flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
width: 34px;
|
|
123
|
+
height: 34px;
|
|
124
|
+
padding: 2px;
|
|
125
|
+
overflow: hidden;
|
|
126
|
+
border-radius: 999px;
|
|
127
|
+
background-color: var(--md-sys-color-surface);
|
|
128
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
129
|
+
transition: width .2s cubic-bezier(.2, .7, .3, 1);
|
|
130
|
+
|
|
131
|
+
&.is-expanded {
|
|
132
|
+
width: 190px;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.toolbar-search-toggle {
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
flex: 0 0 28px;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
width: 28px;
|
|
141
|
+
height: 28px;
|
|
142
|
+
padding: 0;
|
|
143
|
+
border: 0;
|
|
144
|
+
border-radius: 50%;
|
|
145
|
+
background-color: transparent;
|
|
146
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
147
|
+
cursor: pointer;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.toolbar-search-input {
|
|
151
|
+
width: 0;
|
|
152
|
+
min-width: 0;
|
|
153
|
+
height: 28px;
|
|
154
|
+
margin: 0;
|
|
155
|
+
padding: 0;
|
|
156
|
+
border: 0;
|
|
157
|
+
outline: 0;
|
|
158
|
+
background-color: transparent;
|
|
159
|
+
color: var(--md-sys-color-on-surface);
|
|
160
|
+
font-size: 12px;
|
|
161
|
+
opacity: 0;
|
|
162
|
+
transition: opacity .12s ease;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
&.is-expanded .toolbar-search-input {
|
|
166
|
+
width: 150px;
|
|
167
|
+
padding-right: 5px;
|
|
168
|
+
opacity: 1;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
@media (prefers-reduced-motion: reduce) {
|
|
173
|
+
.toolbar-track-indicator {
|
|
174
|
+
transition: opacity .14s ease;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { Component, BaseOptions, InitElements, MElement } from '../../src/component';
|
|
2
|
+
|
|
3
|
+
export interface ToolbarOptions extends BaseOptions {}
|
|
4
|
+
|
|
5
|
+
const _defaults: ToolbarOptions = {};
|
|
6
|
+
|
|
7
|
+
// @implement /Users/kzarshenas/Sites/RodeoFx/rodeo_toolkit_2/app/Environment/Partials/PlanningToolbar.ts
|
|
8
|
+
// Generic version of that file's track-indicator sliding and expandable
|
|
9
|
+
// search behavior, decoupled from the page-specific filter/state-binding
|
|
10
|
+
// system (UtilityFilter) the source used - here it's just plain DOM state.
|
|
11
|
+
export class Toolbar extends Component<ToolbarOptions> {
|
|
12
|
+
_tracks: HTMLElement[];
|
|
13
|
+
|
|
14
|
+
constructor(el: HTMLElement, options: Partial<ToolbarOptions>) {
|
|
15
|
+
super(el, options, Toolbar);
|
|
16
|
+
(this.el as any).M_Toolbar = this;
|
|
17
|
+
|
|
18
|
+
this.options = {
|
|
19
|
+
...Toolbar.defaults,
|
|
20
|
+
...options
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
this._tracks = Array.from(this.el.querySelectorAll<HTMLElement>('.toolbar-track'));
|
|
24
|
+
this._setupEventHandlers();
|
|
25
|
+
this._tracks.forEach((track) => this._moveIndicator(track, false));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static get defaults(): ToolbarOptions {
|
|
29
|
+
return _defaults;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static init(el: HTMLElement, options?: Partial<ToolbarOptions>): Toolbar;
|
|
33
|
+
static init(els: InitElements<MElement>, options?: Partial<ToolbarOptions>): Toolbar[];
|
|
34
|
+
static init(
|
|
35
|
+
els: HTMLElement | InitElements<MElement>,
|
|
36
|
+
options: Partial<ToolbarOptions> = {}
|
|
37
|
+
): Toolbar | Toolbar[] {
|
|
38
|
+
return super.init(els, options, Toolbar);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static getInstance(el: HTMLElement): Toolbar {
|
|
42
|
+
return (el as any).M_Toolbar;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
destroy() {
|
|
46
|
+
this._removeEventHandlers();
|
|
47
|
+
(this.el as any).M_Toolbar = undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Re-measure and reposition every track's sliding indicator - call this
|
|
52
|
+
* after changing which .toolbar-track-item is active from your own code,
|
|
53
|
+
* or after anything that could have changed the toolbar's layout/width.
|
|
54
|
+
*/
|
|
55
|
+
updateIndicators() {
|
|
56
|
+
this._tracks.forEach((track) => this._moveIndicator(track, true));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
_setupEventHandlers() {
|
|
60
|
+
this.el.addEventListener('click', this._handleTrackClick);
|
|
61
|
+
this.el.addEventListener('focusin', this._handleSearchFocusIn);
|
|
62
|
+
this.el.addEventListener('focusout', this._handleSearchFocusOut);
|
|
63
|
+
this.el.addEventListener('input', this._handleSearchInput);
|
|
64
|
+
this.el.addEventListener('keydown', this._handleSearchKeydown);
|
|
65
|
+
window.addEventListener('resize', this._handleWindowResize);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
_removeEventHandlers() {
|
|
69
|
+
this.el.removeEventListener('click', this._handleTrackClick);
|
|
70
|
+
this.el.removeEventListener('focusin', this._handleSearchFocusIn);
|
|
71
|
+
this.el.removeEventListener('focusout', this._handleSearchFocusOut);
|
|
72
|
+
this.el.removeEventListener('input', this._handleSearchInput);
|
|
73
|
+
this.el.removeEventListener('keydown', this._handleSearchKeydown);
|
|
74
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_handleTrackClick = (e: MouseEvent) => {
|
|
78
|
+
const item = (e.target as HTMLElement).closest('.toolbar-track-item') as HTMLElement;
|
|
79
|
+
if (!item) return;
|
|
80
|
+
const track = item.closest('.toolbar-track') as HTMLElement;
|
|
81
|
+
if (!track || !this._tracks.includes(track)) return;
|
|
82
|
+
|
|
83
|
+
track.querySelectorAll('.toolbar-track-item.is-active').forEach((el) => el.classList.remove('is-active'));
|
|
84
|
+
item.classList.add('is-active');
|
|
85
|
+
this._moveIndicator(track, true);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
_handleSearchFocusIn = (e: FocusEvent) => {
|
|
89
|
+
const search = (e.target as HTMLElement).closest('.toolbar-search');
|
|
90
|
+
if (search) search.classList.add('is-expanded');
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
_handleSearchFocusOut = (e: FocusEvent) => {
|
|
94
|
+
const search = (e.target as HTMLElement).closest('.toolbar-search');
|
|
95
|
+
if (!search) return;
|
|
96
|
+
const input = search.querySelector<HTMLInputElement>('.toolbar-search-input');
|
|
97
|
+
if (input && input.value === '') search.classList.remove('is-expanded');
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
_handleSearchInput = (e: Event) => {
|
|
101
|
+
const input = e.target as HTMLInputElement;
|
|
102
|
+
if (!input.classList.contains('toolbar-search-input')) return;
|
|
103
|
+
const search = input.closest('.toolbar-search');
|
|
104
|
+
if (search) search.classList.toggle('is-expanded', input.value !== '' || document.activeElement === input);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
_handleSearchKeydown = (e: KeyboardEvent) => {
|
|
108
|
+
if (e.key !== 'Escape') return;
|
|
109
|
+
const input = (e.target as HTMLElement).closest('.toolbar-search')?.querySelector<HTMLInputElement>('.toolbar-search-input');
|
|
110
|
+
if (!input) return;
|
|
111
|
+
input.value = '';
|
|
112
|
+
input.blur();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
_handleWindowResize = () => {
|
|
116
|
+
this._tracks.forEach((track) => this._moveIndicator(track, false));
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
_moveIndicator(track: HTMLElement, animate: boolean) {
|
|
120
|
+
const indicator = track.querySelector<HTMLElement>('.toolbar-track-indicator');
|
|
121
|
+
const active = track.querySelector<HTMLElement>('.toolbar-track-item.is-active');
|
|
122
|
+
if (!indicator || !active) return;
|
|
123
|
+
|
|
124
|
+
if (!animate) indicator.style.transition = 'none';
|
|
125
|
+
indicator.style.transform = `translateX(${active.offsetLeft - track.clientLeft}px)`;
|
|
126
|
+
indicator.style.width = `${active.offsetWidth}px`;
|
|
127
|
+
track.classList.add('is-armed');
|
|
128
|
+
if (!animate) {
|
|
129
|
+
// Force layout so the transition-less move above actually applies
|
|
130
|
+
// before re-enabling transitions on the next frame.
|
|
131
|
+
void indicator.offsetWidth;
|
|
132
|
+
requestAnimationFrame(() => {
|
|
133
|
+
indicator.style.transition = '';
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|