kmaterialize 2.3.3-1.0.11 → 2.3.3-1.0.13
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 +7 -1
- package/components/slider/_range.scss +5 -1
- package/components/toolbar/_toolbar.scss +219 -0
- package/components/toolbar/toolbar.ts +152 -0
- package/dist/css/materialize.colors.min.css +2 -2
- package/dist/css/materialize.css +8296 -217
- package/dist/css/materialize.min.css +2 -2
- package/dist/css/materialize.min.css.map +1 -1
- package/dist/js/materialize.cjs.js +182 -43
- package/dist/js/materialize.d.ts +30 -2
- package/dist/js/materialize.js +182 -43
- package/dist/js/materialize.min.js +2 -2
- package/dist/js/materialize.mjs +182 -44
- package/package.json +1 -1
- package/sass/_colors.scss +182 -0
- package/sass/_global.scss +148 -0
- package/sass/materialize.scss +1 -0
|
@@ -128,7 +128,13 @@
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
&:focus {
|
|
131
|
-
|
|
131
|
+
// Was $button-floating-background-focus (--md-ref-palette-secondary80,
|
|
132
|
+
// a raw MD3 secondary tone) - it doesn't track a custom theme's
|
|
133
|
+
// primary/container colors the way :hover's color-mix above does, so
|
|
134
|
+
// it could show as a clashing color (e.g. blue) on a themed palette.
|
|
135
|
+
// Same formula as :hover, just mixed a bit stronger for the stronger
|
|
136
|
+
// state.
|
|
137
|
+
background-color: color-mix(in srgb, var(--md-sys-color-primary-container), var(--md-sys-color-on-primary-container) 24%);
|
|
132
138
|
@extend .z-depth-1-half;
|
|
133
139
|
}
|
|
134
140
|
|
|
@@ -114,7 +114,11 @@ input[type=range]::-moz-focus-inner {
|
|
|
114
114
|
|
|
115
115
|
input[type=range]::-moz-range-thumb {
|
|
116
116
|
@include range-thumb;
|
|
117
|
-
margin-top
|
|
117
|
+
// No margin-top here, unlike the -webkit-slider-thumb rule above:
|
|
118
|
+
// Firefox already vertically centers ::-moz-range-thumb on the track
|
|
119
|
+
// by default. Copying webkit's -5px correction (needed there because
|
|
120
|
+
// Chrome/Safari top-align the thumb by default) over-corrects in
|
|
121
|
+
// Firefox, pushing an already-centered thumb noticeably too high.
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
// hide the outline behind the border
|
|
@@ -0,0 +1,219 @@
|
|
|
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
|
+
/// Slider
|
|
113
|
+
///////////////////////////////////////////////////////
|
|
114
|
+
///
|
|
115
|
+
|
|
116
|
+
// A pill container for a range-field, matching .toolbar-track's look -
|
|
117
|
+
// also fixes two things a bare range-field has inside a .toolbar: the
|
|
118
|
+
// native track line is var(--md-sys-color-surface-variant), the exact
|
|
119
|
+
// same color as .toolbar's own background, so it's invisible without a
|
|
120
|
+
// container giving it something else to contrast against; and this also
|
|
121
|
+
// vertically centers any label text/icons placed next to it.
|
|
122
|
+
.toolbar-slider {
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
gap: 8px;
|
|
126
|
+
height: 34px;
|
|
127
|
+
padding: 0 13px;
|
|
128
|
+
border-radius: 999px;
|
|
129
|
+
background-color: var(--md-sys-color-surface);
|
|
130
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
131
|
+
|
|
132
|
+
.range-field {
|
|
133
|
+
width: 120px;
|
|
134
|
+
margin-top: 18px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Material icons default to 24px, which crowds a 34px-tall pill and
|
|
138
|
+
// reads a lot heavier than adjacent 13-14px text labels - match the
|
|
139
|
+
// track item's scale instead.
|
|
140
|
+
i.material-icons {
|
|
141
|
+
font-size: 18px;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
span {
|
|
145
|
+
line-height: 1;
|
|
146
|
+
// Measured against the actual track/thumb center: a "-"/"+" glyph's
|
|
147
|
+
// ink sits a few px below the middle of its own line box in this
|
|
148
|
+
// font, even though the box itself is perfectly flex-centered - nudge
|
|
149
|
+
// the glyph up to compensate.
|
|
150
|
+
position: relative;
|
|
151
|
+
top: -3px;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Expandable search
|
|
156
|
+
///////////////////////////////////////////////////////
|
|
157
|
+
///
|
|
158
|
+
|
|
159
|
+
// A search field that starts as just an icon button and expands into a
|
|
160
|
+
// full text input once focused (or once it has a value) - Toolbar's JS
|
|
161
|
+
// toggles the .is-expanded class.
|
|
162
|
+
.toolbar-search {
|
|
163
|
+
display: inline-flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
width: 34px;
|
|
166
|
+
height: 34px;
|
|
167
|
+
padding: 2px;
|
|
168
|
+
overflow: hidden;
|
|
169
|
+
border-radius: 999px;
|
|
170
|
+
background-color: var(--md-sys-color-surface);
|
|
171
|
+
border: 1px solid var(--md-sys-color-outline-variant);
|
|
172
|
+
transition: width .2s cubic-bezier(.2, .7, .3, 1);
|
|
173
|
+
|
|
174
|
+
&.is-expanded {
|
|
175
|
+
width: 190px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.toolbar-search-toggle {
|
|
179
|
+
display: inline-flex;
|
|
180
|
+
flex: 0 0 28px;
|
|
181
|
+
align-items: center;
|
|
182
|
+
justify-content: center;
|
|
183
|
+
width: 28px;
|
|
184
|
+
height: 28px;
|
|
185
|
+
padding: 0;
|
|
186
|
+
border: 0;
|
|
187
|
+
border-radius: 50%;
|
|
188
|
+
background-color: transparent;
|
|
189
|
+
color: var(--md-sys-color-on-surface-variant);
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.toolbar-search-input {
|
|
194
|
+
width: 0;
|
|
195
|
+
min-width: 0;
|
|
196
|
+
height: 28px;
|
|
197
|
+
margin: 0;
|
|
198
|
+
padding: 0;
|
|
199
|
+
border: 0;
|
|
200
|
+
outline: 0;
|
|
201
|
+
background-color: transparent;
|
|
202
|
+
color: var(--md-sys-color-on-surface);
|
|
203
|
+
font-size: 12px;
|
|
204
|
+
opacity: 0;
|
|
205
|
+
transition: opacity .12s ease;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
&.is-expanded .toolbar-search-input {
|
|
209
|
+
width: 150px;
|
|
210
|
+
padding-right: 5px;
|
|
211
|
+
opacity: 1;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@media (prefers-reduced-motion: reduce) {
|
|
216
|
+
.toolbar-track-indicator {
|
|
217
|
+
transition: opacity .14s ease;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
// A custom/web font can still be loading at construction time; its
|
|
28
|
+
// metrics landing after this first measurement would leave the
|
|
29
|
+
// indicator very slightly offset from the (now differently-sized)
|
|
30
|
+
// text it's supposed to sit behind. Re-measure once fonts are ready.
|
|
31
|
+
if (typeof document !== 'undefined' && document.fonts) {
|
|
32
|
+
document.fonts.ready.then(() => {
|
|
33
|
+
this._tracks.forEach((track) => this._moveIndicator(track, false));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static get defaults(): ToolbarOptions {
|
|
39
|
+
return _defaults;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static init(el: HTMLElement, options?: Partial<ToolbarOptions>): Toolbar;
|
|
43
|
+
static init(els: InitElements<MElement>, options?: Partial<ToolbarOptions>): Toolbar[];
|
|
44
|
+
static init(
|
|
45
|
+
els: HTMLElement | InitElements<MElement>,
|
|
46
|
+
options: Partial<ToolbarOptions> = {}
|
|
47
|
+
): Toolbar | Toolbar[] {
|
|
48
|
+
return super.init(els, options, Toolbar);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static getInstance(el: HTMLElement): Toolbar {
|
|
52
|
+
return (el as any).M_Toolbar;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
destroy() {
|
|
56
|
+
this._removeEventHandlers();
|
|
57
|
+
(this.el as any).M_Toolbar = undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Re-measure and reposition every track's sliding indicator - call this
|
|
62
|
+
* after changing which .toolbar-track-item is active from your own code,
|
|
63
|
+
* or after anything that could have changed the toolbar's layout/width.
|
|
64
|
+
*/
|
|
65
|
+
updateIndicators() {
|
|
66
|
+
this._tracks.forEach((track) => this._moveIndicator(track, true));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_setupEventHandlers() {
|
|
70
|
+
this.el.addEventListener('click', this._handleTrackClick);
|
|
71
|
+
this.el.addEventListener('focusin', this._handleSearchFocusIn);
|
|
72
|
+
this.el.addEventListener('focusout', this._handleSearchFocusOut);
|
|
73
|
+
this.el.addEventListener('input', this._handleSearchInput);
|
|
74
|
+
this.el.addEventListener('keydown', this._handleSearchKeydown);
|
|
75
|
+
window.addEventListener('resize', this._handleWindowResize);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
_removeEventHandlers() {
|
|
79
|
+
this.el.removeEventListener('click', this._handleTrackClick);
|
|
80
|
+
this.el.removeEventListener('focusin', this._handleSearchFocusIn);
|
|
81
|
+
this.el.removeEventListener('focusout', this._handleSearchFocusOut);
|
|
82
|
+
this.el.removeEventListener('input', this._handleSearchInput);
|
|
83
|
+
this.el.removeEventListener('keydown', this._handleSearchKeydown);
|
|
84
|
+
window.removeEventListener('resize', this._handleWindowResize);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
_handleTrackClick = (e: MouseEvent) => {
|
|
88
|
+
const item = (e.target as HTMLElement).closest('.toolbar-track-item') as HTMLElement;
|
|
89
|
+
if (!item) return;
|
|
90
|
+
const track = item.closest('.toolbar-track') as HTMLElement;
|
|
91
|
+
if (!track || !this._tracks.includes(track)) return;
|
|
92
|
+
|
|
93
|
+
track.querySelectorAll('.toolbar-track-item.is-active').forEach((el) => el.classList.remove('is-active'));
|
|
94
|
+
item.classList.add('is-active');
|
|
95
|
+
this._moveIndicator(track, true);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
_handleSearchFocusIn = (e: FocusEvent) => {
|
|
99
|
+
const search = (e.target as HTMLElement).closest('.toolbar-search');
|
|
100
|
+
if (search) search.classList.add('is-expanded');
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
_handleSearchFocusOut = (e: FocusEvent) => {
|
|
104
|
+
const search = (e.target as HTMLElement).closest('.toolbar-search');
|
|
105
|
+
if (!search) return;
|
|
106
|
+
const input = search.querySelector<HTMLInputElement>('.toolbar-search-input');
|
|
107
|
+
if (input && input.value === '') search.classList.remove('is-expanded');
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
_handleSearchInput = (e: Event) => {
|
|
111
|
+
const input = e.target as HTMLInputElement;
|
|
112
|
+
if (!input.classList.contains('toolbar-search-input')) return;
|
|
113
|
+
const search = input.closest('.toolbar-search');
|
|
114
|
+
if (search) search.classList.toggle('is-expanded', input.value !== '' || document.activeElement === input);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
_handleSearchKeydown = (e: KeyboardEvent) => {
|
|
118
|
+
if (e.key !== 'Escape') return;
|
|
119
|
+
const input = (e.target as HTMLElement).closest('.toolbar-search')?.querySelector<HTMLInputElement>('.toolbar-search-input');
|
|
120
|
+
if (!input) return;
|
|
121
|
+
input.value = '';
|
|
122
|
+
input.blur();
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
_handleWindowResize = () => {
|
|
126
|
+
this._tracks.forEach((track) => this._moveIndicator(track, false));
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
_moveIndicator(track: HTMLElement, animate: boolean) {
|
|
130
|
+
const indicator = track.querySelector<HTMLElement>('.toolbar-track-indicator');
|
|
131
|
+
const active = track.querySelector<HTMLElement>('.toolbar-track-item.is-active');
|
|
132
|
+
if (!indicator || !active) return;
|
|
133
|
+
|
|
134
|
+
if (!animate) indicator.style.transition = 'none';
|
|
135
|
+
// No "- track.clientLeft" here: offsetLeft is already relative to the
|
|
136
|
+
// offsetParent's padding edge, same origin as the indicator's own
|
|
137
|
+
// `left: 0` (an absolutely positioned element's containing block is
|
|
138
|
+
// its ancestor's padding box) - subtracting the border width again
|
|
139
|
+
// was double-counting it, nudging the indicator a px too far left.
|
|
140
|
+
indicator.style.transform = `translateX(${active.offsetLeft}px)`;
|
|
141
|
+
indicator.style.width = `${active.offsetWidth}px`;
|
|
142
|
+
track.classList.add('is-armed');
|
|
143
|
+
if (!animate) {
|
|
144
|
+
// Force layout so the transition-less move above actually applies
|
|
145
|
+
// before re-enabling transitions on the next frame.
|
|
146
|
+
void indicator.offsetWidth;
|
|
147
|
+
requestAnimationFrame(() => {
|
|
148
|
+
indicator.style.transition = '';
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|