kmaterialize 2.3.3-1.0.35 → 2.3.3-1.0.36
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/code-card/_code-card.scss +0 -1
- package/components/code-card/codeCardSpec.js +11 -0
- package/components/editor/editorSpec.js +3 -1
- package/components/extensions/web/crazy-button.ts +3 -1
- package/components/password-input/password-input.ts +14 -5
- package/components/rich-textarea/_rich-textarea.scss +13 -1
- package/components/rich-textarea/rich-textarea.ts +3 -0
- package/components/slider/_range.scss +205 -0
- package/components/slider/range.ts +359 -2
- package/components/textfield/_input-fields.scss +31 -0
- package/components/textfield/forms.ts +14 -60
- package/dist/css/materialize.colors.min.css +1 -1
- package/dist/css/materialize.css +313 -2
- package/dist/css/materialize.min.css +2 -2
- package/dist/css/materialize.min.css.map +1 -1
- package/dist/js/materialize.cjs.js +379 -80
- package/dist/js/materialize.d.ts +57 -3
- package/dist/js/materialize.js +379 -80
- package/dist/js/materialize.min.js +3 -3
- package/dist/js/materialize.mjs +379 -81
- package/package.json +1 -1
|
@@ -89,6 +89,17 @@ describe("CodeCard", function(){
|
|
|
89
89
|
});
|
|
90
90
|
|
|
91
91
|
it("highlights JavaScript while preserving the source text", async function(){
|
|
92
|
+
// Load this test's peer explicitly instead of depending on Editor test order.
|
|
93
|
+
if(!window.Prism){
|
|
94
|
+
await new Promise((resolve, reject) => {
|
|
95
|
+
const script = document.createElement("script");
|
|
96
|
+
script.dataset.manual = "";
|
|
97
|
+
script.src = "/__spec__/node_modules/prismjs/prism.js";
|
|
98
|
+
script.onload = resolve;
|
|
99
|
+
script.onerror = () => reject(new Error("Failed to load Prism"));
|
|
100
|
+
document.head.append(script);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
92
103
|
instance = M.CodeCard.init(host);
|
|
93
104
|
await instance.ready;
|
|
94
105
|
expect(host.querySelectorAll(".token").length).toBeGreaterThan(0);
|
|
@@ -273,7 +273,9 @@ describe('Editor Handlebars workspace', function () {
|
|
|
273
273
|
script.onload = () => resolve(); script.onerror = () => reject(new Error('Failed to load Prism'));
|
|
274
274
|
document.head.append(script);
|
|
275
275
|
});
|
|
276
|
-
|
|
276
|
+
// Preserve the Prism instance already used by other components and the peer cache.
|
|
277
|
+
if (!window.Prism) await load('prism.js');
|
|
278
|
+
await load('components/prism-markup-templating.js'); await load('components/prism-handlebars.js');
|
|
277
279
|
const template = '<h1>{{name}}</h1>\n<style>h1 { color: red; }</style>\n' + 'x'.repeat(200);
|
|
278
280
|
await start({ template, data: { name: 'Ada' }, highlight: true });
|
|
279
281
|
const input = host.querySelector('textarea'), mirror = host.querySelector('.editor-highlight');
|
|
@@ -33,6 +33,7 @@ export default class CrazyButton extends Kmcomponent {
|
|
|
33
33
|
'icon-position': string('right', ['left', 'right']),
|
|
34
34
|
'color-primary': string(), 'color-secondary': string(),
|
|
35
35
|
'data-view': string(), 'aria-current': string(),
|
|
36
|
+
'aria-controls': string(), 'aria-pressed': string(),
|
|
36
37
|
href: string(), target: string('_self', ['_self', '_blank']),
|
|
37
38
|
variant: string('', ['', 'filled', 'tonal', 'outlined', 'elevated', 'text', 'standard']),
|
|
38
39
|
disabled: boolean(), toggle: boolean(), pressed: boolean(),
|
|
@@ -84,7 +85,8 @@ export default class CrazyButton extends Kmcomponent {
|
|
|
84
85
|
${a['aria-label'] || iconOnly ? `aria-label="${escape(a['aria-label'] || a.label || a['icon-text'] || 'Button')}"` : ''}
|
|
85
86
|
${a['data-view'] ? `data-view="${escape(a['data-view'])}"` : ''}
|
|
86
87
|
${a['aria-current'] ? `aria-current="${escape(a['aria-current'])}"` : ''}
|
|
87
|
-
${a.toggle ? `aria-pressed="${a.pressed}"` : ''
|
|
88
|
+
${a.toggle ? `aria-pressed="${a.pressed}"` : a['aria-pressed'] ? `aria-pressed="${escape(a['aria-pressed'])}"` : ''}
|
|
89
|
+
${a['aria-controls'] && !menu ? `aria-controls="${escape(a['aria-controls'])}"` : ''} ${menu ? menuAttrs : ''}>${content}</${tag}>`;
|
|
88
90
|
return a.split && a['menu-target']
|
|
89
91
|
? `<span class="btn-split">${button}<button type="button" class="${escape(classes)} btn-icon btn-menu-trigger dropdown-trigger no-autoinit" ${menuAttrs} aria-label="${escape(a['menu-label'])}" ${a.disabled ? 'disabled' : ''}><i class="material-icons" aria-hidden="true">arrow_drop_down</i></button></span>`
|
|
90
92
|
: button;
|
|
@@ -31,12 +31,12 @@ export class PasswordInput extends Component<PasswordInputOptions> {
|
|
|
31
31
|
this._suffixEl = this.el.parentElement?.querySelector<HTMLElement>('[data-password-toggle-icon]') ?? null;
|
|
32
32
|
|
|
33
33
|
if (this._suffixEl) {
|
|
34
|
-
for (const name of ['type', 'role', 'tabindex', 'aria-label', 'aria-pressed', 'aria-controls', 'aria-disabled', 'disabled'])
|
|
34
|
+
for (const name of ['type', 'role', 'tabindex', 'aria-label', 'aria-pressed', 'aria-controls', 'aria-disabled', 'disabled', 'icon-text'])
|
|
35
35
|
this._originalAttributes.set(name, this._suffixEl.getAttribute(name));
|
|
36
36
|
|
|
37
37
|
if (this._suffixEl instanceof HTMLButtonElement) {
|
|
38
38
|
this._suffixEl.type = 'button';
|
|
39
|
-
} else {
|
|
39
|
+
} else if (!this._isCrazyButton()) {
|
|
40
40
|
this._suffixEl.setAttribute('role', 'button');
|
|
41
41
|
this._suffixEl.tabIndex = 0;
|
|
42
42
|
}
|
|
@@ -96,6 +96,10 @@ export class PasswordInput extends Component<PasswordInputOptions> {
|
|
|
96
96
|
this._syncState();
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
private _isCrazyButton(): boolean {
|
|
100
|
+
return this._suffixEl?.matches('crazy-button, regular-btn') ?? false;
|
|
101
|
+
}
|
|
102
|
+
|
|
99
103
|
private _syncState() {
|
|
100
104
|
const visible = this.el.type === 'text';
|
|
101
105
|
const disabled = this.el.matches(':disabled');
|
|
@@ -104,8 +108,13 @@ export class PasswordInput extends Component<PasswordInputOptions> {
|
|
|
104
108
|
this._suffixEl?.setAttribute('aria-pressed', String(visible));
|
|
105
109
|
this._suffixEl?.setAttribute('aria-disabled', String(disabled));
|
|
106
110
|
if (this._suffixEl instanceof HTMLButtonElement) this._suffixEl.disabled = disabled;
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
if (this._isCrazyButton()) {
|
|
112
|
+
this._suffixEl?.toggleAttribute('disabled', disabled);
|
|
113
|
+
this._suffixEl?.setAttribute('icon-text', visible ? 'visibility_off' : 'visibility');
|
|
114
|
+
} else {
|
|
115
|
+
const icon = this._suffixEl?.querySelector('i');
|
|
116
|
+
if (icon) icon.textContent = visible ? 'visibility_off' : 'visibility';
|
|
117
|
+
}
|
|
109
118
|
}
|
|
110
119
|
|
|
111
120
|
private _handleToggleClick = (event: MouseEvent) => {
|
|
@@ -115,7 +124,7 @@ export class PasswordInput extends Component<PasswordInputOptions> {
|
|
|
115
124
|
|
|
116
125
|
private _handleToggleKeydown = (event: KeyboardEvent) => {
|
|
117
126
|
// Native buttons already dispatch clicks for Enter and Space.
|
|
118
|
-
if (this._suffixEl instanceof HTMLButtonElement || !['Enter', ' '].includes(event.key)) return;
|
|
127
|
+
if (this._suffixEl instanceof HTMLButtonElement || this._isCrazyButton() || !['Enter', ' '].includes(event.key)) return;
|
|
119
128
|
event.preventDefault();
|
|
120
129
|
this.toggle();
|
|
121
130
|
};
|
|
@@ -65,16 +65,28 @@
|
|
|
65
65
|
button:hover .ql-fill, button.ql-active .ql-fill { fill: currentColor; }
|
|
66
66
|
a { color: var(--md-sys-color-primary); }
|
|
67
67
|
}
|
|
68
|
-
.ql-container.ql-snow {
|
|
68
|
+
.ql-container.ql-snow {
|
|
69
|
+
height: auto;
|
|
70
|
+
border: 0;
|
|
71
|
+
border-radius: 0 0 11px 11px;
|
|
72
|
+
font-family: inherit;
|
|
73
|
+
font-size: 1rem;
|
|
74
|
+
&:first-child { border-radius: 11px; }
|
|
75
|
+
}
|
|
69
76
|
.ql-editor {
|
|
70
77
|
min-height: var(--rich-textarea-min-height);
|
|
71
78
|
max-height: var(--rich-textarea-max-height, 480px);
|
|
72
79
|
padding: 16px;
|
|
73
80
|
line-height: 1.6;
|
|
81
|
+
font-family: var(--rich-textarea-font-family, inherit);
|
|
74
82
|
color: inherit;
|
|
83
|
+
// Keep the editor surface inside the rounded border without clipping toolbar popups.
|
|
84
|
+
background: transparent;
|
|
85
|
+
border-radius: inherit;
|
|
75
86
|
overflow-wrap: anywhere;
|
|
76
87
|
outline: none;
|
|
77
88
|
&.ql-blank::before { left: 16px; right: 16px; color: var(--md-sys-color-on-surface-variant); font-style: normal; }
|
|
89
|
+
strong, b { font-weight: 800; }
|
|
78
90
|
blockquote { border-color: var(--md-sys-color-outline); }
|
|
79
91
|
}
|
|
80
92
|
.ql-tooltip {
|
|
@@ -9,6 +9,8 @@ export interface RichTextareaOptions extends BaseOptions {
|
|
|
9
9
|
toolbar?: false | Array<ToolbarItem | ToolbarItem[]>;
|
|
10
10
|
formats?: string[];
|
|
11
11
|
placeholder?: string;
|
|
12
|
+
/** Default font stack for editor content; load any web fonts separately. */
|
|
13
|
+
fontFamily?: string;
|
|
12
14
|
label?: string;
|
|
13
15
|
}
|
|
14
16
|
let sequence = 0;
|
|
@@ -80,6 +82,7 @@ export class RichTextarea extends Component<RichTextareaOptions> {
|
|
|
80
82
|
const wrapper = document.createElement('div');
|
|
81
83
|
this._wrapper = wrapper;
|
|
82
84
|
wrapper.className = 'rich-textarea';
|
|
85
|
+
if (this.options.fontFamily) wrapper.style.setProperty('--rich-textarea-font-family', this.options.fontFamily);
|
|
83
86
|
const editor = document.createElement('div');
|
|
84
87
|
const error = document.createElement('div');
|
|
85
88
|
this._error = error;
|
|
@@ -150,3 +150,208 @@ input[type='range'] + .thumb {
|
|
|
150
150
|
input[type='range']::-moz-range-thumb { border: 1px solid CanvasText; }
|
|
151
151
|
input[type='range'] + .thumb { border: 1px solid CanvasText; }
|
|
152
152
|
}
|
|
153
|
+
|
|
154
|
+
/// Two-handle interval. Native inputs retain focus, keyboard, touch and form behavior.
|
|
155
|
+
.range-interval {
|
|
156
|
+
position: relative;
|
|
157
|
+
display: grid;
|
|
158
|
+
min-width: 0;
|
|
159
|
+
isolation: isolate;
|
|
160
|
+
|
|
161
|
+
--range-direction: to right;
|
|
162
|
+
--range-outer-color: var(--range-muted-color, var(--md-sys-color-surface-variant));
|
|
163
|
+
--range-inner-color: var(--md-sys-color-primary);
|
|
164
|
+
&:dir(rtl) { --range-direction: to left; }
|
|
165
|
+
|
|
166
|
+
&::before {
|
|
167
|
+
content: '';
|
|
168
|
+
grid-area: 1 / 1;
|
|
169
|
+
align-self: center;
|
|
170
|
+
height: var(--range-track-height, 4px);
|
|
171
|
+
border-radius: 999px;
|
|
172
|
+
background: linear-gradient(var(--range-direction),
|
|
173
|
+
var(--range-outer-color) 0 var(--range-start),
|
|
174
|
+
var(--range-inner-color) var(--range-start) var(--range-end),
|
|
175
|
+
var(--range-outer-color) var(--range-end) 100%);
|
|
176
|
+
}
|
|
177
|
+
&:has(input:disabled)::before { opacity: .38; }
|
|
178
|
+
&.range-compact { --range-track-height: 2px; }
|
|
179
|
+
&.range-expressive { --range-track-height: 12px; }
|
|
180
|
+
|
|
181
|
+
> input[type='range'] {
|
|
182
|
+
grid-area: 1 / 1;
|
|
183
|
+
pointer-events: none;
|
|
184
|
+
z-index: var(--range-start-layer, 1);
|
|
185
|
+
|
|
186
|
+
~ input[type='range'] { z-index: 1; }
|
|
187
|
+
&:focus-visible { z-index: 2; }
|
|
188
|
+
|
|
189
|
+
&::-webkit-slider-runnable-track { background: transparent; }
|
|
190
|
+
&::-moz-range-track { background: transparent; }
|
|
191
|
+
&::-webkit-slider-thumb { pointer-events: auto; }
|
|
192
|
+
&::-moz-range-thumb { pointer-events: auto; }
|
|
193
|
+
&:disabled::-webkit-slider-thumb { pointer-events: none; }
|
|
194
|
+
&:disabled::-moz-range-thumb { pointer-events: none; }
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Interval steps use native snapping; single-value ranges provide tick marks.
|
|
198
|
+
> .range-ticks { display: none; }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/// Size variants preserve the native 48px interaction area.
|
|
202
|
+
.range-field.range-compact input[type='range'],
|
|
203
|
+
.range-interval.range-compact > input[type='range'] {
|
|
204
|
+
--range-track-height: 2px;
|
|
205
|
+
--range-handle-size: 16px;
|
|
206
|
+
}
|
|
207
|
+
.range-field.range-expressive input[type='range'],
|
|
208
|
+
.range-interval.range-expressive > input[type='range'] {
|
|
209
|
+
--range-track-height: 12px;
|
|
210
|
+
--range-handle-size: 24px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@media (forced-colors: active) {
|
|
214
|
+
.range-interval::before { background: CanvasText; }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/// Highlight the two outside segments, with visible end stops.
|
|
218
|
+
.range-interval.range-outside {
|
|
219
|
+
--range-outer-color: var(--md-sys-color-primary);
|
|
220
|
+
--range-inner-color: var(--range-muted-color, var(--md-sys-color-surface-variant));
|
|
221
|
+
|
|
222
|
+
&::before { clip-path: inset(0 var(--range-edge, 10px)); }
|
|
223
|
+
&::after {
|
|
224
|
+
content: '';
|
|
225
|
+
position: absolute;
|
|
226
|
+
inset-inline: calc(var(--range-edge, 10px) - 1px);
|
|
227
|
+
top: 50%;
|
|
228
|
+
height: 16px;
|
|
229
|
+
transform: translateY(-50%);
|
|
230
|
+
border-inline: 2px solid var(--md-sys-color-primary);
|
|
231
|
+
pointer-events: none;
|
|
232
|
+
}
|
|
233
|
+
&:has(input:disabled)::after { opacity: .38; }
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// Editable values use the flat, segmented appearance of number controls.
|
|
237
|
+
.range-control {
|
|
238
|
+
--range-muted-color: color-mix(in srgb, var(--md-sys-color-on-surface) 24%, transparent);
|
|
239
|
+
display: grid;
|
|
240
|
+
grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr) minmax(0, 4.5rem);
|
|
241
|
+
align-items: stretch;
|
|
242
|
+
gap: 0;
|
|
243
|
+
padding: 0;
|
|
244
|
+
border: 0;
|
|
245
|
+
border-bottom: 1px solid var(--md-sys-color-outline);
|
|
246
|
+
border-radius: 0;
|
|
247
|
+
background: var(--md-sys-color-surface-variant);
|
|
248
|
+
color: var(--md-sys-color-on-surface);
|
|
249
|
+
min-width: 0;
|
|
250
|
+
|
|
251
|
+
&.range-control-single { grid-template-columns: minmax(0, 4.5rem) minmax(0, 1fr); }
|
|
252
|
+
&:focus-within {
|
|
253
|
+
outline: 2px solid var(--md-sys-color-primary);
|
|
254
|
+
outline-offset: -2px;
|
|
255
|
+
}
|
|
256
|
+
&[data-range-variant='outlined'] {
|
|
257
|
+
border: 1px solid var(--md-sys-color-outline);
|
|
258
|
+
border-radius: 4px;
|
|
259
|
+
background: var(--md-sys-color-surface);
|
|
260
|
+
}
|
|
261
|
+
&[data-range-variant='rounded'] {
|
|
262
|
+
border: 1px solid var(--md-sys-color-outline);
|
|
263
|
+
border-radius: 16px;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
> .range-interval,
|
|
267
|
+
> .range-control-track {
|
|
268
|
+
margin-inline: 16px;
|
|
269
|
+
align-self: center;
|
|
270
|
+
min-width: 0;
|
|
271
|
+
|
|
272
|
+
> input[type='range'] { height: 56px; margin-block: 0; }
|
|
273
|
+
}
|
|
274
|
+
> .range-control-value {
|
|
275
|
+
position: static;
|
|
276
|
+
display: flex;
|
|
277
|
+
flex-direction: column-reverse;
|
|
278
|
+
justify-content: center;
|
|
279
|
+
min-width: 0;
|
|
280
|
+
min-height: 56px;
|
|
281
|
+
margin: 0;
|
|
282
|
+
padding: 4px;
|
|
283
|
+
box-sizing: border-box;
|
|
284
|
+
border-radius: inherit;
|
|
285
|
+
background: var(--md-sys-color-primary-container);
|
|
286
|
+
color: var(--md-sys-color-on-primary-container);
|
|
287
|
+
font-size: 10px;
|
|
288
|
+
line-height: 1.2;
|
|
289
|
+
text-align: center;
|
|
290
|
+
transform: none;
|
|
291
|
+
|
|
292
|
+
&:first-child { border-start-end-radius: 0; border-end-end-radius: 0; }
|
|
293
|
+
&:last-child { border-start-start-radius: 0; border-end-start-radius: 0; }
|
|
294
|
+
> span { overflow-wrap: anywhere; }
|
|
295
|
+
> .range-control-number > input[type='number'] {
|
|
296
|
+
appearance: textfield;
|
|
297
|
+
box-sizing: border-box;
|
|
298
|
+
width: 100%;
|
|
299
|
+
min-width: 0;
|
|
300
|
+
height: 30px;
|
|
301
|
+
margin: 0;
|
|
302
|
+
padding: 0 2px;
|
|
303
|
+
border: 0;
|
|
304
|
+
border-radius: 0;
|
|
305
|
+
background: transparent;
|
|
306
|
+
color: inherit;
|
|
307
|
+
box-shadow: none;
|
|
308
|
+
font: inherit;
|
|
309
|
+
font-size: 16px;
|
|
310
|
+
text-align: center;
|
|
311
|
+
font-variant-numeric: tabular-nums;
|
|
312
|
+
|
|
313
|
+
&::-webkit-inner-spin-button,
|
|
314
|
+
&::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
|
315
|
+
color: transparent;
|
|
316
|
+
-webkit-text-fill-color: transparent;
|
|
317
|
+
&:focus {
|
|
318
|
+
color: inherit;
|
|
319
|
+
-webkit-text-fill-color: currentColor;
|
|
320
|
+
}
|
|
321
|
+
&:focus-visible { outline: 2px solid var(--md-sys-color-primary); }
|
|
322
|
+
&:disabled { opacity: .38; }
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@media (forced-colors: active) {
|
|
328
|
+
.range-interval.range-outside::after { border-color: CanvasText; }
|
|
329
|
+
.range-control { border-color: CanvasText; }
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/// Fixed viewport for the centered film-roll readout. Focus reveals the real input.
|
|
333
|
+
.range-control-number {
|
|
334
|
+
position: relative;
|
|
335
|
+
display: block;
|
|
336
|
+
width: 100%;
|
|
337
|
+
height: 30px;
|
|
338
|
+
min-width: 0;
|
|
339
|
+
font-size: 16px;
|
|
340
|
+
font-variant-numeric: tabular-nums;
|
|
341
|
+
text-align: center;
|
|
342
|
+
|
|
343
|
+
&:focus-within > .range-control-readout { visibility: hidden; }
|
|
344
|
+
&:has(input:disabled) > .range-control-readout { opacity: .38; }
|
|
345
|
+
}
|
|
346
|
+
.range-control-readout {
|
|
347
|
+
position: absolute;
|
|
348
|
+
inset: 0 2px;
|
|
349
|
+
overflow: hidden;
|
|
350
|
+
pointer-events: none;
|
|
351
|
+
white-space: nowrap;
|
|
352
|
+
direction: ltr;
|
|
353
|
+
}
|
|
354
|
+
.range-control-reel {
|
|
355
|
+
display: block;
|
|
356
|
+
> span { display: block; height: 30px; line-height: 30px; }
|
|
357
|
+
}
|