mtrl 0.9.6 → 0.9.8
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/README.md +20 -0
- package/dist/README.md +20 -0
- package/dist/components/bottom-app-bar/bottom-app-bar.js +2 -2
- package/dist/components/bottom-sheet/features/structure.js +4 -3
- package/dist/components/button/api.js +3 -2
- package/dist/components/button/features/progress.js +24 -21
- package/dist/components/button-group/config.d.ts +11 -3
- package/dist/components/button-group/config.js +1 -1
- package/dist/components/card/content.js +5 -4
- package/dist/components/carousel/features/slides.js +2 -1
- package/dist/components/checkbox/checkbox.js +22 -0
- package/dist/components/checkbox/config.d.ts +3 -3
- package/dist/components/checkbox/config.js +10 -9
- package/dist/components/checkbox/types.d.ts +19 -4
- package/dist/components/chips/api.js +3 -1
- package/dist/components/chips/chip/api.js +3 -2
- package/dist/components/chips/chip/chip.js +6 -4
- package/dist/components/chips/config.js +1 -1
- package/dist/components/datepicker/api.d.ts +8 -2
- package/dist/components/datepicker/api.js +1 -0
- package/dist/components/datepicker/config.d.ts +14 -4
- package/dist/components/datepicker/datepicker.js +5 -2
- package/dist/components/datepicker/types.d.ts +14 -0
- package/dist/components/datepicker/utils.js +4 -6
- package/dist/components/dialog/features.js +5 -4
- package/dist/components/drawer/features/items.js +3 -2
- package/dist/components/list/features/renderer.js +4 -3
- package/dist/components/menu/api.d.ts +1 -1
- package/dist/components/menu/features/controller.js +4 -3
- package/dist/components/menu/features/keyboard.js +7 -5
- package/dist/components/menu/features/opener.js +15 -10
- package/dist/components/menu/features/position.js +1 -1
- package/dist/components/menu/features/submenu.js +7 -1
- package/dist/components/menu/types.d.ts +1 -1
- package/dist/components/navigation/nav-item.js +4 -3
- package/dist/components/navigation/system/mobile.js +3 -2
- package/dist/components/navigation-rail/navigation-rail.js +3 -2
- package/dist/components/progress/features/canvas.js +0 -2
- package/dist/components/search/features/states.js +3 -3
- package/dist/components/search/features/structure.js +3 -0
- package/dist/components/search/features/suggestions.js +2 -2
- package/dist/components/search/types.d.ts +6 -0
- package/dist/components/segmented-button/config.d.ts +4 -1
- package/dist/components/segmented-button/config.js +1 -1
- package/dist/components/segmented-button/segment.d.ts +2 -5
- package/dist/components/segmented-button/segment.js +7 -3
- package/dist/components/select/features.js +29 -27
- package/dist/components/side-sheet/features/structure.js +5 -4
- package/dist/components/slider/features/controller.js +7 -0
- package/dist/components/slider/features/dom.d.ts +1 -0
- package/dist/components/slider/features/dom.js +11 -0
- package/dist/components/slider/features/handlers.js +8 -3
- package/dist/components/slider/features/states.js +8 -0
- package/dist/components/slider/types.d.ts +7 -0
- package/dist/components/switch/config.d.ts +3 -3
- package/dist/components/switch/config.js +7 -7
- package/dist/components/switch/features.js +5 -5
- package/dist/components/switch/types.d.ts +10 -0
- package/dist/components/tabs/features.d.ts +1 -0
- package/dist/components/tabs/scroll-indicators.js +3 -2
- package/dist/components/tabs/tab.d.ts +0 -5
- package/dist/components/tabs/tab.js +12 -3
- package/dist/components/tabs/utils.js +21 -1
- package/dist/components/textfield/api.js +12 -4
- package/dist/components/textfield/features/leading-icon.js +32 -33
- package/dist/components/textfield/features/prefix-text.js +30 -18
- package/dist/components/textfield/features/suffix-text.js +30 -14
- package/dist/components/textfield/features/trailing-icon.js +36 -19
- package/dist/components/timepicker/api.d.ts +1 -1
- package/dist/components/timepicker/api.js +16 -11
- package/dist/components/timepicker/render.js +27 -24
- package/dist/components/timepicker/timepicker.js +4 -2
- package/dist/components/timepicker/types.d.ts +7 -0
- package/dist/components/timepicker/utils.d.ts +14 -0
- package/dist/components/timepicker/utils.js +4 -0
- package/dist/components/tooltip/api.js +8 -2
- package/dist/components/top-app-bar/top-app-bar.js +3 -2
- package/dist/core/compose/component.d.ts +1 -1
- package/dist/core/compose/component.js +1 -1
- package/dist/core/compose/features/icon.js +3 -2
- package/dist/core/compose/features/lifecycle.js +10 -11
- package/dist/core/compose/features/track.js +2 -1
- package/dist/core/config/component.d.ts +1 -1
- package/dist/core/config/component.js +1 -1
- package/dist/core/dom/create.js +5 -4
- package/dist/core/dom/form-value.d.ts +24 -0
- package/dist/core/dom/form-value.js +14 -0
- package/dist/core/dom/html.d.ts +34 -0
- package/dist/core/dom/html.js +13 -0
- package/dist/core/dom/index.d.ts +2 -0
- package/dist/core/dom/index.js +1 -0
- package/dist/core/dom/utils.js +1 -1
- package/dist/core/state/emitter.js +8 -1
- package/dist/core/state/index.d.ts +1 -1
- package/dist/core/state/store.d.ts +10 -2
- package/dist/core/state/store.js +2 -1
- package/dist/index.cjs +0 -0
- package/dist/package.json +5 -2
- package/dist/styles/badge.css +1 -1
- package/dist/styles/base.css +2 -2
- package/dist/styles/bottom-app-bar.css +1 -1
- package/dist/styles/bottom-sheet.css +1 -1
- package/dist/styles/button-group.css +1 -1
- package/dist/styles/button.css +2 -2
- package/dist/styles/card.css +1 -1
- package/dist/styles/carousel.css +1 -1
- package/dist/styles/checkbox.css +1 -1
- package/dist/styles/chips.css +1 -1
- package/dist/styles/dialog.css +1 -1
- package/dist/styles/divider.css +1 -1
- package/dist/styles/drawer.css +1 -1
- package/dist/styles/extended-fab.css +1 -1
- package/dist/styles/fab.css +1 -1
- package/dist/styles/icon-button.css +1 -1
- package/dist/styles/list.css +1 -1
- package/dist/styles/loading-indicator.css +1 -1
- package/dist/styles/menu.css +1 -1
- package/dist/styles/navigation-rail.css +1 -1
- package/dist/styles/navigation.css +1 -1
- package/dist/styles/progress.css +1 -1
- package/dist/styles/radios.css +1 -1
- package/dist/styles/search.css +1 -1
- package/dist/styles/segmented-button.css +1 -1
- package/dist/styles/select.css +1 -1
- package/dist/styles/side-sheet.css +1 -1
- package/dist/styles/slider.css +1 -1
- package/dist/styles/snackbar.css +1 -1
- package/dist/styles/split-button.css +1 -1
- package/dist/styles/switch.css +1 -1
- package/dist/styles/tabs.css +1 -1
- package/dist/styles/textfield.css +1 -1
- package/dist/styles/timepicker.css +1 -1
- package/dist/styles/tooltip.css +1 -1
- package/dist/styles/top-app-bar.css +1 -1
- package/dist/styles/utilities.css +1 -1
- package/dist/styles.css +2 -2
- package/dist/styles.d.ts +1 -0
- package/dist/themes/autumn.css +1 -1
- package/dist/themes/baseline.css +1 -1
- package/dist/themes/brownbeige.css +1 -1
- package/dist/themes/browngreen.css +1 -1
- package/dist/themes/desert.css +1 -1
- package/dist/themes/forest.css +1 -1
- package/dist/themes/highcontrast.css +1 -1
- package/dist/themes/legacy.css +1 -1
- package/dist/themes/material.css +1 -1
- package/dist/themes/ocean.css +1 -1
- package/dist/themes/sageivory.css +1 -1
- package/dist/themes/spring.css +1 -1
- package/dist/themes/summer.css +1 -1
- package/dist/themes/sunset.css +1 -1
- package/dist/themes/tealcaramel.css +1 -1
- package/dist/themes/winter.css +1 -1
- package/package.json +7 -3
- package/src/styles/base/_reset.scss +9 -2
- package/src/styles/components/_button.scss +1 -12
|
@@ -1,35 +1,47 @@
|
|
|
1
1
|
export const withPrefixText = (config) => (component) => {
|
|
2
|
-
if (!config.prefixText) {
|
|
3
|
-
return component;
|
|
4
|
-
}
|
|
5
2
|
const PREFIX = config.prefix || 'mtrl';
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const NAME = config.componentName || 'textfield';
|
|
4
|
+
let slot = null;
|
|
5
|
+
const ensureSlot = () => {
|
|
6
|
+
if (slot && slot.parentNode)
|
|
7
|
+
return slot;
|
|
8
|
+
const element = document.createElement('span');
|
|
9
|
+
element.className = `${PREFIX}-${NAME}-prefix`;
|
|
10
|
+
component.element.appendChild(element);
|
|
11
|
+
component.element.classList.add(`${PREFIX}-${NAME}--with-prefix`);
|
|
12
|
+
slot = element;
|
|
13
|
+
return element;
|
|
14
|
+
};
|
|
15
|
+
const detachSlot = () => {
|
|
16
|
+
slot?.remove();
|
|
17
|
+
slot = null;
|
|
18
|
+
component.element.classList.remove(`${PREFIX}-${NAME}--with-prefix`);
|
|
19
|
+
};
|
|
20
|
+
const write = (element, value) => {
|
|
21
|
+
element.textContent = value;
|
|
22
|
+
};
|
|
23
|
+
if (config.prefixText) {
|
|
24
|
+
write(ensureSlot(), config.prefixText);
|
|
25
|
+
}
|
|
11
26
|
if ('lifecycle' in component && component.lifecycle?.destroy) {
|
|
12
27
|
const originalDestroy = component.lifecycle.destroy;
|
|
13
28
|
component.lifecycle.destroy = () => {
|
|
14
|
-
|
|
29
|
+
slot?.remove();
|
|
30
|
+
slot = null;
|
|
15
31
|
originalDestroy.call(component.lifecycle);
|
|
16
32
|
};
|
|
17
33
|
}
|
|
18
34
|
return {
|
|
19
35
|
...component,
|
|
20
|
-
prefixTextElement:
|
|
36
|
+
prefixTextElement: slot,
|
|
21
37
|
setPrefixText(text) {
|
|
22
|
-
|
|
38
|
+
this.prefixTextElement = ensureSlot();
|
|
39
|
+
write(this.prefixTextElement, text);
|
|
23
40
|
return this;
|
|
24
41
|
},
|
|
25
42
|
removePrefixText() {
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-prefix`);
|
|
29
|
-
const labelEl = component.element.querySelector(`.${PREFIX}-${config.componentName || 'textfield'}-label`);
|
|
30
|
-
if (labelEl) {
|
|
31
|
-
labelEl.style.left = '';
|
|
32
|
-
}
|
|
43
|
+
if (slot) {
|
|
44
|
+
detachSlot();
|
|
33
45
|
this.prefixTextElement = null;
|
|
34
46
|
}
|
|
35
47
|
return this;
|
|
@@ -1,31 +1,47 @@
|
|
|
1
1
|
export const withSuffixText = (config) => (component) => {
|
|
2
|
-
if (!config.suffixText) {
|
|
3
|
-
return component;
|
|
4
|
-
}
|
|
5
2
|
const PREFIX = config.prefix || 'mtrl';
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
const NAME = config.componentName || 'textfield';
|
|
4
|
+
let slot = null;
|
|
5
|
+
const ensureSlot = () => {
|
|
6
|
+
if (slot && slot.parentNode)
|
|
7
|
+
return slot;
|
|
8
|
+
const element = document.createElement('span');
|
|
9
|
+
element.className = `${PREFIX}-${NAME}-suffix`;
|
|
10
|
+
component.element.appendChild(element);
|
|
11
|
+
component.element.classList.add(`${PREFIX}-${NAME}--with-suffix`);
|
|
12
|
+
slot = element;
|
|
13
|
+
return element;
|
|
14
|
+
};
|
|
15
|
+
const detachSlot = () => {
|
|
16
|
+
slot?.remove();
|
|
17
|
+
slot = null;
|
|
18
|
+
component.element.classList.remove(`${PREFIX}-${NAME}--with-suffix`);
|
|
19
|
+
};
|
|
20
|
+
const write = (element, value) => {
|
|
21
|
+
element.textContent = value;
|
|
22
|
+
};
|
|
23
|
+
if (config.suffixText) {
|
|
24
|
+
write(ensureSlot(), config.suffixText);
|
|
25
|
+
}
|
|
11
26
|
if ('lifecycle' in component && component.lifecycle?.destroy) {
|
|
12
27
|
const originalDestroy = component.lifecycle.destroy;
|
|
13
28
|
component.lifecycle.destroy = () => {
|
|
14
|
-
|
|
29
|
+
slot?.remove();
|
|
30
|
+
slot = null;
|
|
15
31
|
originalDestroy.call(component.lifecycle);
|
|
16
32
|
};
|
|
17
33
|
}
|
|
18
34
|
return {
|
|
19
35
|
...component,
|
|
20
|
-
suffixTextElement:
|
|
36
|
+
suffixTextElement: slot,
|
|
21
37
|
setSuffixText(text) {
|
|
22
|
-
|
|
38
|
+
this.suffixTextElement = ensureSlot();
|
|
39
|
+
write(this.suffixTextElement, text);
|
|
23
40
|
return this;
|
|
24
41
|
},
|
|
25
42
|
removeSuffixText() {
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-suffix`);
|
|
43
|
+
if (slot) {
|
|
44
|
+
detachSlot();
|
|
29
45
|
this.suffixTextElement = null;
|
|
30
46
|
}
|
|
31
47
|
return this;
|
|
@@ -1,37 +1,54 @@
|
|
|
1
|
+
import { setHTML } from "../../../core/dom/html.js";
|
|
1
2
|
export const withTrailingIcon = (config) => (component) => {
|
|
2
|
-
if (!config.trailingIcon) {
|
|
3
|
-
return component;
|
|
4
|
-
}
|
|
5
3
|
const PREFIX = config.prefix || 'mtrl';
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
4
|
+
const NAME = config.componentName || 'textfield';
|
|
5
|
+
let slot = null;
|
|
6
|
+
const ensureSlot = () => {
|
|
7
|
+
if (slot && slot.parentNode)
|
|
8
|
+
return slot;
|
|
9
|
+
const element = document.createElement('span');
|
|
10
|
+
element.className = `${PREFIX}-${NAME}-trailing-icon`;
|
|
11
|
+
component.element.appendChild(element);
|
|
12
|
+
component.element.classList.add(`${PREFIX}-${NAME}--with-trailing-icon`);
|
|
13
|
+
if (component.input) {
|
|
14
|
+
component.input.classList.add(`${PREFIX}-${NAME}-input--with-trailing-icon`);
|
|
15
|
+
}
|
|
16
|
+
slot = element;
|
|
17
|
+
return element;
|
|
18
|
+
};
|
|
19
|
+
const detachSlot = () => {
|
|
20
|
+
slot?.remove();
|
|
21
|
+
slot = null;
|
|
22
|
+
component.element.classList.remove(`${PREFIX}-${NAME}--with-trailing-icon`);
|
|
23
|
+
if (component.input) {
|
|
24
|
+
component.input.classList.remove(`${PREFIX}-${NAME}-input--with-trailing-icon`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const write = (element, value) => {
|
|
28
|
+
setHTML(element, value);
|
|
29
|
+
};
|
|
30
|
+
if (config.trailingIcon) {
|
|
31
|
+
write(ensureSlot(), config.trailingIcon);
|
|
13
32
|
}
|
|
14
33
|
if ('lifecycle' in component && component.lifecycle?.destroy) {
|
|
15
34
|
const originalDestroy = component.lifecycle.destroy;
|
|
16
35
|
component.lifecycle.destroy = () => {
|
|
17
|
-
|
|
36
|
+
slot?.remove();
|
|
37
|
+
slot = null;
|
|
18
38
|
originalDestroy.call(component.lifecycle);
|
|
19
39
|
};
|
|
20
40
|
}
|
|
21
41
|
return {
|
|
22
42
|
...component,
|
|
23
|
-
trailingIcon:
|
|
43
|
+
trailingIcon: slot,
|
|
24
44
|
setTrailingIcon(html) {
|
|
25
|
-
|
|
45
|
+
this.trailingIcon = ensureSlot();
|
|
46
|
+
write(this.trailingIcon, html);
|
|
26
47
|
return this;
|
|
27
48
|
},
|
|
28
49
|
removeTrailingIcon() {
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-trailing-icon`);
|
|
32
|
-
if (component.input) {
|
|
33
|
-
component.input.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}-input--with-trailing-icon`);
|
|
34
|
-
}
|
|
50
|
+
if (slot) {
|
|
51
|
+
detachSlot();
|
|
35
52
|
this.trailingIcon = null;
|
|
36
53
|
}
|
|
37
54
|
return this;
|
|
@@ -21,5 +21,5 @@ interface ApiOptions {
|
|
|
21
21
|
* @param options - API options with events and lifecycle methods
|
|
22
22
|
* @returns TimePicker component API
|
|
23
23
|
*/
|
|
24
|
-
export declare const createTimePickerAPI: (baseComponent: ElementComponent, modalElement: HTMLElement, dialogElement: HTMLElement, timeValue: TimeValue, config: TimePickerConfig, options: ApiOptions) => TimePickerComponent;
|
|
24
|
+
export declare const createTimePickerAPI: (baseComponent: ElementComponent, modalElement: HTMLElement, dialogElement: HTMLElement, timeValue: TimeValue, config: TimePickerConfig, options: ApiOptions, formValue?: HTMLInputElement | null) => TimePickerComponent;
|
|
25
25
|
export {};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { TIME_PICKER_TYPE, TIME_PICKER_ORIENTATION, TIME_FORMAT, TIME_PERIOD } from "./types.js";
|
|
2
2
|
import { TIMEPICKER_EVENTS as EVENTS, TIMEPICKER_SELECTORS as SELECTORS } from "./constants.js";
|
|
3
|
-
import { formatTime, padZero } from "./utils.js";
|
|
3
|
+
import { formatTime, padZero, formatFormValue } from "./utils.js";
|
|
4
4
|
import { renderTimePicker } from "./render.js";
|
|
5
5
|
import { renderClockDial, getTimeValueFromClick } from "./clockdial.js";
|
|
6
|
-
|
|
6
|
+
import { setFormValue } from "../../core/dom/form-value.js";
|
|
7
|
+
export const createTimePickerAPI = (baseComponent, modalElement, dialogElement, timeValue, config, options, formValue = null) => {
|
|
8
|
+
const render = () => {
|
|
9
|
+
renderTimePicker(dialogElement, timeValue, config);
|
|
10
|
+
setFormValue(formValue, formatFormValue(timeValue, config.showSeconds === true));
|
|
11
|
+
};
|
|
7
12
|
let isOpen = !!config.isOpen;
|
|
8
13
|
const handleKeydown = (event) => {
|
|
9
14
|
if (event.key === 'Escape') {
|
|
@@ -33,7 +38,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
33
38
|
isOpen = true;
|
|
34
39
|
baseComponent.element.classList.add(`${config.prefix}-time-picker--open`);
|
|
35
40
|
setTimeout(() => {
|
|
36
|
-
|
|
41
|
+
render();
|
|
37
42
|
}, 50);
|
|
38
43
|
options.events.emit(EVENTS.OPEN);
|
|
39
44
|
if (config.onOpen) {
|
|
@@ -83,7 +88,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
83
88
|
timeValue.minutes = minutes;
|
|
84
89
|
timeValue.seconds = seconds;
|
|
85
90
|
timeValue.period = hours >= 12 ? TIME_PERIOD.PM : TIME_PERIOD.AM;
|
|
86
|
-
|
|
91
|
+
render();
|
|
87
92
|
options.events.emit(EVENTS.CHANGE, this.getValue());
|
|
88
93
|
if (config.onChange) {
|
|
89
94
|
config.onChange(this.getValue());
|
|
@@ -100,7 +105,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
100
105
|
config.type = type;
|
|
101
106
|
dialogElement.classList.remove(`${config.prefix}-time-picker-dialog--${TIME_PICKER_TYPE.DIAL}`, `${config.prefix}-time-picker-dialog--${TIME_PICKER_TYPE.INPUT}`);
|
|
102
107
|
dialogElement.classList.add(`${config.prefix}-time-picker-dialog--${type}`);
|
|
103
|
-
|
|
108
|
+
render();
|
|
104
109
|
return this;
|
|
105
110
|
},
|
|
106
111
|
getType() {
|
|
@@ -118,7 +123,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
118
123
|
else {
|
|
119
124
|
timeValue.period = timeValue.hours >= 12 ? TIME_PERIOD.PM : TIME_PERIOD.AM;
|
|
120
125
|
}
|
|
121
|
-
|
|
126
|
+
render();
|
|
122
127
|
options.events.emit(EVENTS.CHANGE, this.getValue());
|
|
123
128
|
return this;
|
|
124
129
|
},
|
|
@@ -131,7 +136,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
131
136
|
config.orientation = orientation;
|
|
132
137
|
dialogElement.classList.remove(`${config.prefix}-time-picker-dialog--${TIME_PICKER_ORIENTATION.VERTICAL}`, `${config.prefix}-time-picker-dialog--${TIME_PICKER_ORIENTATION.HORIZONTAL}`);
|
|
133
138
|
dialogElement.classList.add(`${config.prefix}-time-picker-dialog--${orientation}`);
|
|
134
|
-
|
|
139
|
+
render();
|
|
135
140
|
return this;
|
|
136
141
|
},
|
|
137
142
|
getOrientation() {
|
|
@@ -144,7 +149,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
144
149
|
titleElement.textContent = title;
|
|
145
150
|
}
|
|
146
151
|
else {
|
|
147
|
-
|
|
152
|
+
render();
|
|
148
153
|
}
|
|
149
154
|
return this;
|
|
150
155
|
},
|
|
@@ -197,7 +202,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
197
202
|
if (timeValue.hours >= 12) {
|
|
198
203
|
timeValue.hours -= 12;
|
|
199
204
|
}
|
|
200
|
-
|
|
205
|
+
render();
|
|
201
206
|
options.events.emit(EVENTS.CHANGE, timePickerAPI.getValue());
|
|
202
207
|
if (config.onChange) {
|
|
203
208
|
config.onChange(timePickerAPI.getValue());
|
|
@@ -210,7 +215,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
210
215
|
if (timeValue.hours < 12) {
|
|
211
216
|
timeValue.hours += 12;
|
|
212
217
|
}
|
|
213
|
-
|
|
218
|
+
render();
|
|
214
219
|
options.events.emit(EVENTS.CHANGE, timePickerAPI.getValue());
|
|
215
220
|
if (config.onChange) {
|
|
216
221
|
config.onChange(timePickerAPI.getValue());
|
|
@@ -372,7 +377,7 @@ export const createTimePickerAPI = (baseComponent, modalElement, dialogElement,
|
|
|
372
377
|
newSeconds = 59;
|
|
373
378
|
timeValue.seconds = newSeconds;
|
|
374
379
|
}
|
|
375
|
-
|
|
380
|
+
render();
|
|
376
381
|
options.events.emit(EVENTS.CHANGE, timePickerAPI.getValue());
|
|
377
382
|
if (config.onChange) {
|
|
378
383
|
config.onChange(timePickerAPI.getValue());
|
|
@@ -2,8 +2,9 @@ import { TIME_PICKER_TYPE, TIME_FORMAT, TIME_PERIOD, } from "./types.js";
|
|
|
2
2
|
import { TIMEPICKER_DIAL, TIMEPICKER_ICONS } from "./constants.js";
|
|
3
3
|
import { padZero, convertTo12Hour } from "./utils.js";
|
|
4
4
|
import { renderClockDial, getTimeValueFromClick } from "./clockdial.js";
|
|
5
|
+
import { setHTML } from "../../core/dom/html.js";
|
|
5
6
|
export const renderTimePicker = (container, timeValue, config, onTimeChange) => {
|
|
6
|
-
container.
|
|
7
|
+
container.replaceChildren();
|
|
7
8
|
if (config.title) {
|
|
8
9
|
const title = document.createElement("div");
|
|
9
10
|
title.className = `${config.prefix}-time-picker-title`;
|
|
@@ -117,10 +118,9 @@ export const renderTimePicker = (container, timeValue, config, onTimeChange) =>
|
|
|
117
118
|
toggleTypeButton.setAttribute("aria-label", config.type === TIME_PICKER_TYPE.DIAL
|
|
118
119
|
? "Switch to keyboard input"
|
|
119
120
|
: "Switch to dial selector");
|
|
120
|
-
toggleTypeButton.
|
|
121
|
-
config.
|
|
122
|
-
|
|
123
|
-
: config.clockIcon || TIMEPICKER_ICONS.CLOCK;
|
|
121
|
+
setHTML(toggleTypeButton, config.type === TIME_PICKER_TYPE.DIAL
|
|
122
|
+
? config.keyboardIcon || TIMEPICKER_ICONS.KEYBOARD
|
|
123
|
+
: config.clockIcon || TIMEPICKER_ICONS.CLOCK);
|
|
124
124
|
actions.appendChild(toggleTypeButton);
|
|
125
125
|
const actionButtons = document.createElement("div");
|
|
126
126
|
actionButtons.className = `${config.prefix}-time-picker-action-buttons`;
|
|
@@ -150,16 +150,15 @@ export const renderTimePicker = (container, timeValue, config, onTimeChange) =>
|
|
|
150
150
|
toggleTypeButton.addEventListener("click", () => {
|
|
151
151
|
if (dialContainer.style.display === "none") {
|
|
152
152
|
dialContainer.style.display = "block";
|
|
153
|
-
toggleTypeButton.
|
|
154
|
-
config.keyboardIcon || TIMEPICKER_ICONS.KEYBOARD;
|
|
153
|
+
setHTML(toggleTypeButton, config.keyboardIcon || TIMEPICKER_ICONS.KEYBOARD);
|
|
155
154
|
toggleTypeButton.setAttribute("aria-label", "Switch to keyboard input");
|
|
156
155
|
setTimeout(() => {
|
|
157
156
|
canvas.focus();
|
|
158
|
-
const hourElements =
|
|
157
|
+
const hourElements = container.querySelectorAll(`.${config.prefix}-time-picker-hours`);
|
|
159
158
|
hourElements.forEach((el) => el.setAttribute("data-active", "true"));
|
|
160
|
-
const minuteElements =
|
|
159
|
+
const minuteElements = container.querySelectorAll(`.${config.prefix}-time-picker-minutes`);
|
|
161
160
|
minuteElements.forEach((el) => el.setAttribute("data-active", "false"));
|
|
162
|
-
const secondElements =
|
|
161
|
+
const secondElements = container.querySelectorAll(`.${config.prefix}-time-picker-seconds`);
|
|
163
162
|
secondElements.forEach((el) => el.setAttribute("data-active", "false"));
|
|
164
163
|
renderClockDial(canvas, timeValue, {
|
|
165
164
|
type: TIME_PICKER_TYPE.DIAL,
|
|
@@ -172,7 +171,7 @@ export const renderTimePicker = (container, timeValue, config, onTimeChange) =>
|
|
|
172
171
|
}
|
|
173
172
|
else {
|
|
174
173
|
dialContainer.style.display = "none";
|
|
175
|
-
toggleTypeButton
|
|
174
|
+
setHTML(toggleTypeButton, config.clockIcon || TIMEPICKER_ICONS.CLOCK);
|
|
176
175
|
toggleTypeButton.setAttribute("aria-label", "Switch to dial selector");
|
|
177
176
|
setTimeout(() => {
|
|
178
177
|
hoursInput.focus();
|
|
@@ -324,13 +323,13 @@ export const renderTimePicker = (container, timeValue, config, onTimeChange) =>
|
|
|
324
323
|
: timeValue.hours;
|
|
325
324
|
hoursInput.value = padZero(displayHours);
|
|
326
325
|
}
|
|
327
|
-
|
|
326
|
+
container
|
|
328
327
|
.querySelectorAll(`.${config.prefix}-time-picker-period-am, .${config.prefix}-time-picker-period-pm`)
|
|
329
328
|
.forEach((el) => {
|
|
330
329
|
el.classList.remove(`${config.prefix}-time-picker-period--selected`);
|
|
331
330
|
el.setAttribute("aria-pressed", "false");
|
|
332
331
|
});
|
|
333
|
-
const selectedPeriod =
|
|
332
|
+
const selectedPeriod = container.querySelector(`.${config.prefix}-time-picker-period-${period.toLowerCase()}`);
|
|
334
333
|
if (selectedPeriod) {
|
|
335
334
|
selectedPeriod.classList.add(`${config.prefix}-time-picker-period--selected`);
|
|
336
335
|
selectedPeriod.setAttribute("aria-pressed", "true");
|
|
@@ -350,18 +349,22 @@ export const renderTimePicker = (container, timeValue, config, onTimeChange) =>
|
|
|
350
349
|
}
|
|
351
350
|
};
|
|
352
351
|
if (config.format === TIME_FORMAT.AMPM) {
|
|
353
|
-
const amPeriodElement =
|
|
354
|
-
const pmPeriodElement =
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
352
|
+
const amPeriodElement = container.querySelector(`.${config.prefix}-time-picker-period-am`);
|
|
353
|
+
const pmPeriodElement = container.querySelector(`.${config.prefix}-time-picker-period-pm`);
|
|
354
|
+
const activateOn = (element, period) => {
|
|
355
|
+
if (!element)
|
|
356
|
+
return;
|
|
357
|
+
element.addEventListener("click", () => handlePeriodChange(period));
|
|
358
|
+
element.addEventListener("keydown", (event) => {
|
|
359
|
+
const key = event.key;
|
|
360
|
+
if (key !== "Enter" && key !== " " && key !== "Spacebar")
|
|
361
|
+
return;
|
|
362
|
+
event.preventDefault();
|
|
363
|
+
handlePeriodChange(period);
|
|
358
364
|
});
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
handlePeriodChange(TIME_PERIOD.PM);
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
+
};
|
|
366
|
+
activateOn(amPeriodElement, TIME_PERIOD.AM);
|
|
367
|
+
activateOn(pmPeriodElement, TIME_PERIOD.PM);
|
|
365
368
|
}
|
|
366
369
|
if (config.type === TIME_PICKER_TYPE.DIAL ||
|
|
367
370
|
dialContainer.style.display === "block") {
|
|
@@ -7,7 +7,8 @@ import { createBaseConfig, getContainerConfig, getApiConfig } from "./config.js"
|
|
|
7
7
|
import { TIMEPICKER_Z_INDEX as Z_INDEX } from "./constants.js";
|
|
8
8
|
import { createTimePickerAPI } from "./api.js";
|
|
9
9
|
import { renderTimePicker } from "./render.js";
|
|
10
|
-
import { parseTime } from "./utils.js";
|
|
10
|
+
import { parseTime, formatFormValue } from "./utils.js";
|
|
11
|
+
import { createFormValue } from "../../core/dom/form-value.js";
|
|
11
12
|
const createTimePicker = (config = {}) => {
|
|
12
13
|
const baseConfig = createBaseConfig(config);
|
|
13
14
|
try {
|
|
@@ -54,7 +55,8 @@ const createTimePicker = (config = {}) => {
|
|
|
54
55
|
};
|
|
55
56
|
}
|
|
56
57
|
renderTimePicker(dialogElement, timeValue, baseConfig);
|
|
57
|
-
const
|
|
58
|
+
const formValue = createFormValue(baseComponent.element, baseConfig.name, formatFormValue(timeValue, baseConfig.showSeconds === true));
|
|
59
|
+
const timePicker = createTimePickerAPI(baseComponent, modalElement, dialogElement, timeValue, baseConfig, getApiConfig(baseComponent), formValue);
|
|
58
60
|
if (baseConfig.isOpen) {
|
|
59
61
|
setTimeout(() => timePicker.open(), 0);
|
|
60
62
|
}
|
|
@@ -48,6 +48,13 @@ export interface TimePickerConfig {
|
|
|
48
48
|
* @example '14:30'
|
|
49
49
|
*/
|
|
50
50
|
value?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Form field name. A time picker renders no form control of its own, so it
|
|
53
|
+
* submits nothing unless this is set; with it, a hidden input on the
|
|
54
|
+
* picker's element carries the current time as 24-hour `HH:mm` — or
|
|
55
|
+
* `HH:mm:ss` when `showSeconds` is on — whatever format the picker displays.
|
|
56
|
+
*/
|
|
57
|
+
name?: string;
|
|
51
58
|
/**
|
|
52
59
|
* Type of time picker to display
|
|
53
60
|
* @default TIME_PICKER_TYPE.DIAL
|
|
@@ -72,3 +72,17 @@ export declare const getTimeFromPosition: (centerX: number, centerY: number, cli
|
|
|
72
72
|
* @returns {boolean} Whether the time is within constraints
|
|
73
73
|
*/
|
|
74
74
|
export declare const isTimeWithinConstraints: (time: TimeValue, minTime?: string, maxTime?: string) => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Formats a time for submission in a form: 24-hour `HH:mm`, or `HH:mm:ss`
|
|
77
|
+
* when the picker shows seconds.
|
|
78
|
+
*
|
|
79
|
+
* Separate from `formatTime` on purpose. What a form submits should not change
|
|
80
|
+
* because the picker happens to display 12-hour time, and `formatTime` appends
|
|
81
|
+
* seconds whenever the value carries them — which it always does, since the
|
|
82
|
+
* picker stores 0 when seconds are hidden.
|
|
83
|
+
*
|
|
84
|
+
* @param timeValue - Current time value, whose hours are 24-hour
|
|
85
|
+
* @param showSeconds - Whether the picker shows seconds
|
|
86
|
+
* @returns The value to submit
|
|
87
|
+
*/
|
|
88
|
+
export declare const formatFormValue: (timeValue: TimeValue, showSeconds: boolean) => string;
|
|
@@ -129,3 +129,7 @@ export const isTimeWithinConstraints = (time, minTime, maxTime) => {
|
|
|
129
129
|
}
|
|
130
130
|
return true;
|
|
131
131
|
};
|
|
132
|
+
export const formatFormValue = (timeValue, showSeconds) => {
|
|
133
|
+
const base = `${padZero(timeValue.hours)}:${padZero(timeValue.minutes)}`;
|
|
134
|
+
return showSeconds ? `${base}:${padZero(timeValue.seconds ?? 0)}` : base;
|
|
135
|
+
};
|
|
@@ -182,10 +182,16 @@ export const withAPI = ({ lifecycle }) => (component) => {
|
|
|
182
182
|
getClass: component.getClass,
|
|
183
183
|
setText(text) {
|
|
184
184
|
const contentNode = document.createTextNode(text);
|
|
185
|
-
while (component.element.firstChild
|
|
185
|
+
while (component.element.firstChild &&
|
|
186
|
+
component.element.firstChild !== arrowElement) {
|
|
186
187
|
component.element.removeChild(component.element.firstChild);
|
|
187
188
|
}
|
|
188
|
-
component.element
|
|
189
|
+
if (arrowElement.parentNode === component.element) {
|
|
190
|
+
component.element.insertBefore(contentNode, arrowElement);
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
component.element.appendChild(contentNode);
|
|
194
|
+
}
|
|
189
195
|
if (isVisible) {
|
|
190
196
|
this.updatePosition();
|
|
191
197
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createBase, withElement, withEvents, withLifecycle, } from "../../core/compose/index.js";
|
|
2
2
|
import { createConfig } from "./config.js";
|
|
3
|
+
import { TOP_APP_BAR_DEFAULTS } from "./constants.js";
|
|
3
4
|
export const createTopAppBar = (config = {}) => {
|
|
4
5
|
const componentConfig = createConfig(config);
|
|
5
6
|
const component = createBase(componentConfig);
|
|
@@ -25,7 +26,7 @@ export const createTopAppBar = (config = {}) => {
|
|
|
25
26
|
(componentConfig.type === "medium" || componentConfig.type === "large")) {
|
|
26
27
|
classes.push(`${component.getClass("top-app-bar")}--compressible`);
|
|
27
28
|
}
|
|
28
|
-
return classes.filter(Boolean);
|
|
29
|
+
return classes.filter((name) => Boolean(name));
|
|
29
30
|
};
|
|
30
31
|
const enhancedComponent = withElement({
|
|
31
32
|
tag: componentConfig.tag,
|
|
@@ -61,7 +62,7 @@ export const createTopAppBar = (config = {}) => {
|
|
|
61
62
|
let isScrolled = false;
|
|
62
63
|
if (componentConfig.scrollable) {
|
|
63
64
|
const handleScroll = () => {
|
|
64
|
-
const shouldBeScrolled = window.scrollY > componentConfig.scrollThreshold;
|
|
65
|
+
const shouldBeScrolled = window.scrollY > (componentConfig.scrollThreshold ?? TOP_APP_BAR_DEFAULTS.SCROLL_THRESHOLD);
|
|
65
66
|
if (isScrolled !== shouldBeScrolled) {
|
|
66
67
|
isScrolled = shouldBeScrolled;
|
|
67
68
|
if (isScrolled) {
|
|
@@ -102,7 +102,7 @@ export const withElement = (options = {}) => (component) => {
|
|
|
102
102
|
...(Array.isArray(options.className)
|
|
103
103
|
? options.className
|
|
104
104
|
: [options.className]),
|
|
105
|
-
].filter(Boolean),
|
|
105
|
+
].filter((name) => Boolean(name)),
|
|
106
106
|
attributes: options.attributes || {},
|
|
107
107
|
id: options.id,
|
|
108
108
|
name: options.name,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setHTML } from "../../dom/html.js";
|
|
1
2
|
const createIcon = (element, config = {}) => {
|
|
2
3
|
let iconElement = null;
|
|
3
4
|
const PREFIX = config.prefix || "mtrl";
|
|
@@ -12,7 +13,7 @@ const createIcon = (element, config = {}) => {
|
|
|
12
13
|
if (config.iconSize) {
|
|
13
14
|
iconEl.classList.add(`${PREFIX}-icon--${config.iconSize}`);
|
|
14
15
|
}
|
|
15
|
-
iconEl
|
|
16
|
+
setHTML(iconEl, html);
|
|
16
17
|
return iconEl;
|
|
17
18
|
};
|
|
18
19
|
return {
|
|
@@ -38,7 +39,7 @@ const createIcon = (element, config = {}) => {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
else if (iconElement && html) {
|
|
41
|
-
iconElement
|
|
42
|
+
setHTML(iconElement, html);
|
|
42
43
|
}
|
|
43
44
|
return this;
|
|
44
45
|
},
|
|
@@ -28,6 +28,13 @@ export const withLifecycle = () => (component) => {
|
|
|
28
28
|
const destroyElement = component.destroy?.bind(component);
|
|
29
29
|
const emitter = createEmitter();
|
|
30
30
|
resources.add(() => emitter.clear());
|
|
31
|
+
const unmount = () => {
|
|
32
|
+
if (mounted) {
|
|
33
|
+
mounted = false;
|
|
34
|
+
emitter.emit('unmount');
|
|
35
|
+
emitter.clear();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
31
38
|
const lifecycle = {
|
|
32
39
|
onMount: (handler) => emitter.on('mount', handler),
|
|
33
40
|
onUnmount: (handler) => emitter.on('unmount', handler),
|
|
@@ -37,21 +44,13 @@ export const withLifecycle = () => (component) => {
|
|
|
37
44
|
emitter.emit('mount');
|
|
38
45
|
}
|
|
39
46
|
},
|
|
40
|
-
unmount
|
|
41
|
-
if (mounted) {
|
|
42
|
-
mounted = false;
|
|
43
|
-
emitter.emit('unmount');
|
|
44
|
-
emitter.clear();
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
+
unmount,
|
|
47
48
|
isMounted: () => mounted,
|
|
48
|
-
destroy() {
|
|
49
|
+
destroy: () => {
|
|
49
50
|
if (destroyed)
|
|
50
51
|
return;
|
|
51
52
|
destroyed = true;
|
|
52
|
-
|
|
53
|
-
this.unmount();
|
|
54
|
-
}
|
|
53
|
+
unmount();
|
|
55
54
|
emitter.clear();
|
|
56
55
|
resources.destroy();
|
|
57
56
|
if (hasEvents(component)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setHTML } from "../../dom/html.js";
|
|
1
2
|
const DEFAULT_ICON = `
|
|
2
3
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
3
4
|
<polyline points="20 6 9 17 4 12"></polyline>
|
|
@@ -11,7 +12,7 @@ export const withTrack = (config) => (component) => {
|
|
|
11
12
|
if (config.icon !== 'none') {
|
|
12
13
|
const icon = document.createElement('span');
|
|
13
14
|
icon.className = `${config.prefix}-${config.componentName}-thumb-icon`;
|
|
14
|
-
icon
|
|
15
|
+
setHTML(icon, config.icon || DEFAULT_ICON);
|
|
15
16
|
thumb.appendChild(icon);
|
|
16
17
|
}
|
|
17
18
|
component.element.appendChild(track);
|
|
@@ -86,7 +86,7 @@ export declare const processClassNames: (classNames: string | string[] | null) =
|
|
|
86
86
|
export declare const createElementConfig: (config: BaseComponentConfig, options: {
|
|
87
87
|
tag: string;
|
|
88
88
|
attributes?: Record<string, unknown>;
|
|
89
|
-
className?: string | string[] | null;
|
|
89
|
+
className?: string | (string | null | undefined)[] | null;
|
|
90
90
|
html?: string;
|
|
91
91
|
text?: string;
|
|
92
92
|
forwardEvents?: Record<string, EventCondition>;
|
|
@@ -44,7 +44,7 @@ export const createElementConfig = (config, options) => {
|
|
|
44
44
|
const combinedClassNames = [
|
|
45
45
|
...(Array.isArray(configClasses) ? configClasses : [configClasses]),
|
|
46
46
|
...(Array.isArray(optionsClasses) ? optionsClasses : [optionsClasses]),
|
|
47
|
-
].filter(Boolean);
|
|
47
|
+
].filter((name) => Boolean(name));
|
|
48
48
|
const elementAttributes = options.attributes || {};
|
|
49
49
|
return {
|
|
50
50
|
tag: options.tag,
|