mtrl 0.9.5 → 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 +26 -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/config.js +0 -1
- package/dist/components/dialog/features.js +10 -8
- package/dist/components/dialog/types.d.ts +3 -2
- 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/config.js +2 -2
- package/dist/components/menu/features/controller.js +41 -26
- package/dist/components/menu/features/keyboard.js +7 -5
- package/dist/components/menu/features/opener.js +19 -12
- 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 +13 -2
- 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 +267 -44
- 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/switch.js +1 -1
- 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/tabs.js +10 -1
- package/dist/components/tabs/utils.d.ts +8 -0
- package/dist/components/tabs/utils.js +63 -18
- package/dist/components/textfield/api.js +12 -4
- package/dist/components/textfield/features/error.d.ts +1 -0
- package/dist/components/textfield/features/error.js +10 -0
- 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/supporting-text.d.ts +1 -0
- package/dist/components/textfield/features/supporting-text.js +25 -0
- 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 +27 -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/input.js +1 -1
- package/dist/core/compose/features/lifecycle.js +10 -11
- package/dist/core/compose/features/textlabel.js +3 -0
- 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 +2 -2
- package/dist/styles/button-group.css +1 -1
- package/dist/styles/button.css +2 -2
- package/dist/styles/card.css +2 -2
- package/dist/styles/carousel.css +1 -1
- package/dist/styles/checkbox.css +2 -2
- package/dist/styles/chips.css +2 -2
- package/dist/styles/dialog.css +2 -2
- package/dist/styles/divider.css +1 -1
- package/dist/styles/drawer.css +2 -2
- package/dist/styles/extended-fab.css +2 -2
- package/dist/styles/fab.css +2 -2
- package/dist/styles/icon-button.css +2 -2
- package/dist/styles/list.css +2 -2
- package/dist/styles/loading-indicator.css +1 -1
- package/dist/styles/menu.css +2 -2
- package/dist/styles/navigation-rail.css +1 -1
- package/dist/styles/navigation.css +2 -2
- package/dist/styles/progress.css +2 -2
- package/dist/styles/radios.css +1 -1
- package/dist/styles/search.css +1 -1
- package/dist/styles/segmented-button.css +2 -2
- package/dist/styles/select.css +2 -2
- package/dist/styles/side-sheet.css +2 -2
- package/dist/styles/slider.css +1 -1
- package/dist/styles/snackbar.css +1 -1
- package/dist/styles/split-button.css +2 -2
- package/dist/styles/switch.css +2 -2
- package/dist/styles/tabs.css +1 -1
- package/dist/styles/textfield.css +2 -2
- package/dist/styles/timepicker.css +1 -1
- package/dist/styles/tooltip.css +2 -2
- 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/abstract/_mixins.scss +26 -0
- package/src/styles/abstract/_variables.scss +25 -11
- package/src/styles/base/_reset.scss +9 -2
- package/src/styles/components/_bottom-sheet.scss +23 -7
- package/src/styles/components/_button.scss +1 -12
- package/src/styles/components/_dialog.scss +59 -26
- package/src/styles/components/_drawer.scss +60 -7
- package/src/styles/components/_menu.scss +12 -7
- package/src/styles/components/_side-sheet.scss +33 -4
- package/src/styles/components/_tooltip.scss +3 -0
package/README.md
CHANGED
|
@@ -167,6 +167,26 @@ const createNote = (config: NoteConfig): NoteComponent =>
|
|
|
167
167
|
|
|
168
168
|
The element gets the `mtrl-note` class, `withEvents` adds `on`, `off` and `emit`, and `destroy()` removes the element. Add `withLifecycle()` from `mtrl/core/compose/features` when features need to register cleanup.
|
|
169
169
|
|
|
170
|
+
## Markup and sanitizing
|
|
171
|
+
|
|
172
|
+
Icons and `content` options are markup strings, written with `innerHTML`. Every such write goes through one sink, so you can decide once how markup is treated. Set a policy when the strings can come from users or a CMS, or when your page enforces Trusted Types:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { configureHTML } from 'mtrl';
|
|
176
|
+
|
|
177
|
+
// A sanitizer
|
|
178
|
+
configureHTML({ sanitize: (html) => DOMPurify.sanitize(html) });
|
|
179
|
+
|
|
180
|
+
// Trusted Types: under `require-trusted-types-for 'script'` a plain string
|
|
181
|
+
// assignment throws, so return a TrustedHTML from a policy your CSP allows
|
|
182
|
+
const policy = window.trustedTypes.createPolicy('mtrl', {
|
|
183
|
+
createHTML: (html) => DOMPurify.sanitize(html),
|
|
184
|
+
});
|
|
185
|
+
configureHTML({ sanitize: (html) => policy.createHTML(html) });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The policy sees every string, the library's own icons included; a `TrustedHTML` value passed as an icon or content skips it. With no policy set, markup is written as it is. Text options (`text`, a card's `text`) never go through `innerHTML`.
|
|
189
|
+
|
|
170
190
|
## Upgrading from 0.7
|
|
171
191
|
|
|
172
192
|
0.8.0 aligns the components with Material 3 expressive, and some of that changes the API or the styles:
|
package/dist/README.md
CHANGED
|
@@ -167,6 +167,26 @@ const createNote = (config: NoteConfig): NoteComponent =>
|
|
|
167
167
|
|
|
168
168
|
The element gets the `mtrl-note` class, `withEvents` adds `on`, `off` and `emit`, and `destroy()` removes the element. Add `withLifecycle()` from `mtrl/core/compose/features` when features need to register cleanup.
|
|
169
169
|
|
|
170
|
+
## Markup and sanitizing
|
|
171
|
+
|
|
172
|
+
Icons and `content` options are markup strings, written with `innerHTML`. Every such write goes through one sink, so you can decide once how markup is treated. Set a policy when the strings can come from users or a CMS, or when your page enforces Trusted Types:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { configureHTML } from 'mtrl';
|
|
176
|
+
|
|
177
|
+
// A sanitizer
|
|
178
|
+
configureHTML({ sanitize: (html) => DOMPurify.sanitize(html) });
|
|
179
|
+
|
|
180
|
+
// Trusted Types: under `require-trusted-types-for 'script'` a plain string
|
|
181
|
+
// assignment throws, so return a TrustedHTML from a policy your CSP allows
|
|
182
|
+
const policy = window.trustedTypes.createPolicy('mtrl', {
|
|
183
|
+
createHTML: (html) => DOMPurify.sanitize(html),
|
|
184
|
+
});
|
|
185
|
+
configureHTML({ sanitize: (html) => policy.createHTML(html) });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The policy sees every string, the library's own icons included; a `TrustedHTML` value passed as an icon or content skips it. With no policy set, markup is written as it is. Text options (`text`, a card's `text`) never go through `innerHTML`.
|
|
189
|
+
|
|
170
190
|
## Upgrading from 0.7
|
|
171
191
|
|
|
172
192
|
0.8.0 aligns the components with Material 3 expressive, and some of that changes the API or the styles:
|
|
@@ -18,7 +18,7 @@ export const createBottomAppBar = (config = {}) => {
|
|
|
18
18
|
? `${component.getClass("bottom-app-bar")}--fab-center`
|
|
19
19
|
: "",
|
|
20
20
|
componentConfig.class,
|
|
21
|
-
],
|
|
21
|
+
].filter((name) => Boolean(name)),
|
|
22
22
|
attributes: {
|
|
23
23
|
role: "toolbar",
|
|
24
24
|
"aria-label": "Bottom app bar",
|
|
@@ -63,7 +63,7 @@ export const createBottomAppBar = (config = {}) => {
|
|
|
63
63
|
return this;
|
|
64
64
|
},
|
|
65
65
|
addFab(fab) {
|
|
66
|
-
fabContainer.
|
|
66
|
+
fabContainer.replaceChildren();
|
|
67
67
|
fabContainer.appendChild(fab);
|
|
68
68
|
this.element.classList.add(`${component.getClass("bottom-app-bar")}--with-fab`);
|
|
69
69
|
return this;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BOTTOM_SHEET_CLASSES, BOTTOM_SHEET_VARIANTS } from "../constants.js";
|
|
2
|
+
import { setHTML } from "../../../core/dom/html.js";
|
|
2
3
|
export const withStructure = (config) => (component) => {
|
|
3
4
|
const { element, getClass } = component;
|
|
4
5
|
const isModal = config.variant === BOTTOM_SHEET_VARIANTS.MODAL;
|
|
@@ -46,7 +47,7 @@ export const withStructure = (config) => (component) => {
|
|
|
46
47
|
content.appendChild(config.content);
|
|
47
48
|
}
|
|
48
49
|
else if (typeof config.content === "string") {
|
|
49
|
-
content
|
|
50
|
+
setHTML(content, config.content);
|
|
50
51
|
}
|
|
51
52
|
container.appendChild(content);
|
|
52
53
|
element.appendChild(container);
|
|
@@ -60,11 +61,11 @@ export const withStructure = (config) => (component) => {
|
|
|
60
61
|
title,
|
|
61
62
|
content,
|
|
62
63
|
setContent(next) {
|
|
63
|
-
content.
|
|
64
|
+
content.replaceChildren();
|
|
64
65
|
if (next instanceof HTMLElement)
|
|
65
66
|
content.appendChild(next);
|
|
66
67
|
else
|
|
67
|
-
content
|
|
68
|
+
setHTML(content, next);
|
|
68
69
|
},
|
|
69
70
|
setTitle(next) {
|
|
70
71
|
if (title) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { addClass } from "../../core/index.js";
|
|
2
|
+
import { setHTML } from "../../core/dom/html.js";
|
|
2
3
|
export const withAPI = ({ disabled, lifecycle }) => (component) => {
|
|
3
4
|
const textAPI = {
|
|
4
5
|
setText: (content) => {
|
|
@@ -85,7 +86,7 @@ export const withAPI = ({ disabled, lifecycle }) => (component) => {
|
|
|
85
86
|
else if (!buttonComponent.hasIcon()) {
|
|
86
87
|
const newIconElement = document.createElement("span");
|
|
87
88
|
newIconElement.className = `${component.getClass("button-icon")}`;
|
|
88
|
-
newIconElement
|
|
89
|
+
setHTML(newIconElement, icon);
|
|
89
90
|
if (component.element.firstChild) {
|
|
90
91
|
component.element.insertBefore(newIconElement, component.element.firstChild);
|
|
91
92
|
}
|
|
@@ -99,7 +100,7 @@ export const withAPI = ({ disabled, lifecycle }) => (component) => {
|
|
|
99
100
|
else {
|
|
100
101
|
const iconElement = component.icon.getElement();
|
|
101
102
|
if (iconElement) {
|
|
102
|
-
iconElement
|
|
103
|
+
setHTML(iconElement, icon);
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
return buttonComponent;
|
|
@@ -36,7 +36,7 @@ export const withProgress = (config) => (component) => {
|
|
|
36
36
|
addClass(progress.element, component.getClass("button-progress"));
|
|
37
37
|
progress.element.style.display = "none";
|
|
38
38
|
Object.entries(progressEventHandlers).forEach(([event, handler]) => {
|
|
39
|
-
|
|
39
|
+
p.on(event, handler);
|
|
40
40
|
});
|
|
41
41
|
component.progress = progress;
|
|
42
42
|
return progress;
|
|
@@ -51,7 +51,7 @@ export const withProgress = (config) => (component) => {
|
|
|
51
51
|
}
|
|
52
52
|
return component.element.firstChild;
|
|
53
53
|
};
|
|
54
|
-
|
|
54
|
+
const showProgress = async () => {
|
|
55
55
|
const p = await ensureProgress();
|
|
56
56
|
if (p.element) {
|
|
57
57
|
if (!component.element.contains(p.element)) {
|
|
@@ -72,11 +72,12 @@ export const withProgress = (config) => (component) => {
|
|
|
72
72
|
}
|
|
73
73
|
return component;
|
|
74
74
|
};
|
|
75
|
-
component.
|
|
76
|
-
|
|
75
|
+
component.showProgress = showProgress;
|
|
76
|
+
component.showProgressSync = () => {
|
|
77
|
+
void showProgress();
|
|
77
78
|
return component;
|
|
78
79
|
};
|
|
79
|
-
|
|
80
|
+
const hideProgress = async () => {
|
|
80
81
|
if (!progress)
|
|
81
82
|
return component;
|
|
82
83
|
progress.element.style.display = "none";
|
|
@@ -87,26 +88,29 @@ export const withProgress = (config) => (component) => {
|
|
|
87
88
|
}
|
|
88
89
|
return component;
|
|
89
90
|
};
|
|
90
|
-
component.
|
|
91
|
-
|
|
91
|
+
component.hideProgress = hideProgress;
|
|
92
|
+
component.hideProgressSync = () => {
|
|
93
|
+
void hideProgress();
|
|
92
94
|
return component;
|
|
93
95
|
};
|
|
94
|
-
|
|
96
|
+
const setProgress = async (value) => {
|
|
95
97
|
const p = await ensureProgress();
|
|
96
98
|
p.setValue(value);
|
|
97
99
|
return component;
|
|
98
100
|
};
|
|
99
|
-
component.
|
|
100
|
-
|
|
101
|
+
component.setProgress = setProgress;
|
|
102
|
+
component.setProgressSync = (value) => {
|
|
103
|
+
void setProgress(value);
|
|
101
104
|
return component;
|
|
102
105
|
};
|
|
103
|
-
|
|
106
|
+
const setIndeterminate = async (indeterminate) => {
|
|
104
107
|
const p = await ensureProgress();
|
|
105
108
|
p.setIndeterminate(indeterminate);
|
|
106
109
|
return component;
|
|
107
110
|
};
|
|
108
|
-
component.
|
|
109
|
-
|
|
111
|
+
component.setIndeterminate = setIndeterminate;
|
|
112
|
+
component.setIndeterminateSync = (indeterminate) => {
|
|
113
|
+
void setIndeterminate(indeterminate);
|
|
110
114
|
return component;
|
|
111
115
|
};
|
|
112
116
|
const readLabel = () => component.text ? component.text.getText() : component.getText?.();
|
|
@@ -116,11 +120,12 @@ export const withProgress = (config) => (component) => {
|
|
|
116
120
|
else
|
|
117
121
|
component.setText?.(value);
|
|
118
122
|
};
|
|
119
|
-
|
|
123
|
+
const setLoading = async (loading, text) => {
|
|
120
124
|
if (loading && !isLoading) {
|
|
121
125
|
originalText = readLabel() ?? "";
|
|
122
126
|
isLoading = true;
|
|
123
|
-
|
|
127
|
+
component.element.setAttribute("aria-busy", "true");
|
|
128
|
+
await showProgress();
|
|
124
129
|
if (component.disabled?.disable) {
|
|
125
130
|
component.disabled.disable();
|
|
126
131
|
}
|
|
@@ -129,7 +134,8 @@ export const withProgress = (config) => (component) => {
|
|
|
129
134
|
}
|
|
130
135
|
else if (!loading && isLoading) {
|
|
131
136
|
isLoading = false;
|
|
132
|
-
|
|
137
|
+
component.element.removeAttribute("aria-busy");
|
|
138
|
+
await hideProgress();
|
|
133
139
|
if (component.disabled?.enable) {
|
|
134
140
|
component.disabled.enable();
|
|
135
141
|
}
|
|
@@ -140,8 +146,9 @@ export const withProgress = (config) => (component) => {
|
|
|
140
146
|
}
|
|
141
147
|
return component;
|
|
142
148
|
};
|
|
143
|
-
component.
|
|
144
|
-
|
|
149
|
+
component.setLoading = setLoading;
|
|
150
|
+
component.setLoadingSync = (loading, text) => {
|
|
151
|
+
void setLoading(loading, text);
|
|
145
152
|
return component;
|
|
146
153
|
};
|
|
147
154
|
if (component.lifecycle) {
|
|
@@ -157,9 +164,7 @@ export const withProgress = (config) => (component) => {
|
|
|
157
164
|
};
|
|
158
165
|
}
|
|
159
166
|
if (config.showProgress) {
|
|
160
|
-
ensureProgress().then(() =>
|
|
161
|
-
component.showProgress();
|
|
162
|
-
});
|
|
167
|
+
void ensureProgress().then(() => showProgress());
|
|
163
168
|
}
|
|
164
169
|
return component;
|
|
165
170
|
};
|
|
@@ -6,11 +6,19 @@ import { ButtonGroupConfig, ButtonGroupVariant, ButtonGroupDensity } from "./typ
|
|
|
6
6
|
export declare const DEFAULT_CONFIG: Partial<ButtonGroupConfig>;
|
|
7
7
|
/**
|
|
8
8
|
* Creates the base configuration for Button Group component
|
|
9
|
+
*
|
|
10
|
+
* The return type keeps what createComponentConfig guarantees: componentName
|
|
11
|
+
* and prefix are always filled in, so callers building class names from them
|
|
12
|
+
* do not have to treat them as missing.
|
|
13
|
+
*
|
|
9
14
|
* @param {ButtonGroupConfig} config - User provided configuration
|
|
10
|
-
* @returns
|
|
15
|
+
* @returns Complete configuration with defaults applied
|
|
11
16
|
* @internal
|
|
12
17
|
*/
|
|
13
|
-
export declare const createBaseConfig: (config?: ButtonGroupConfig) => ButtonGroupConfig
|
|
18
|
+
export declare const createBaseConfig: (config?: ButtonGroupConfig) => Partial<ButtonGroupConfig> & {
|
|
19
|
+
componentName: string;
|
|
20
|
+
prefix: string;
|
|
21
|
+
};
|
|
14
22
|
/**
|
|
15
23
|
* Generates element configuration for the Button Group container
|
|
16
24
|
* @param {ButtonGroupConfig} config - Button Group configuration
|
|
@@ -50,7 +58,7 @@ export declare const getSizeStyles: (prefix: string, size: ButtonGroupConfig["si
|
|
|
50
58
|
* @returns {Object} Button configuration with group-specific settings
|
|
51
59
|
* @internal
|
|
52
60
|
*/
|
|
53
|
-
export declare const getButtonConfig: (buttonConfig: ButtonGroupConfig["buttons"][number], index: number, total: number, groupConfig: ButtonGroupConfig) => {
|
|
61
|
+
export declare const getButtonConfig: (buttonConfig: NonNullable<ButtonGroupConfig["buttons"]>[number], index: number, total: number, groupConfig: ButtonGroupConfig) => {
|
|
54
62
|
variant: ButtonGroupVariant;
|
|
55
63
|
size: import("./types.js").ButtonGroupSize;
|
|
56
64
|
shape: import("./types.js").ButtonGroupShape;
|
|
@@ -53,7 +53,7 @@ export const getContainerConfig = (config) => {
|
|
|
53
53
|
? `${config.prefix}-button-group--density-${density}`
|
|
54
54
|
: null,
|
|
55
55
|
config.equalWidth ? `${config.prefix}-button-group--equal-width` : null
|
|
56
|
-
],
|
|
56
|
+
].filter((name) => Boolean(name)),
|
|
57
57
|
interactive: true
|
|
58
58
|
};
|
|
59
59
|
};
|
|
@@ -3,6 +3,7 @@ import { pipe } from "../../core/compose/index.js";
|
|
|
3
3
|
import { createBase, withElement } from "../../core/compose/component.js";
|
|
4
4
|
import { createElement } from "../../core/dom/create.js";
|
|
5
5
|
import { safeUrl } from "../../core/utils/url.js";
|
|
6
|
+
import { setHTML } from "../../core/dom/html.js";
|
|
6
7
|
export const CARD_CONTENT_PADDING = true;
|
|
7
8
|
let headerCount = 0;
|
|
8
9
|
export const createCardContent = (config = {}) => {
|
|
@@ -18,14 +19,14 @@ export const createCardContent = (config = {}) => {
|
|
|
18
19
|
className: [
|
|
19
20
|
config.class,
|
|
20
21
|
config.padding === false ? `${PREFIX}-card-content--no-padding` : null
|
|
21
|
-
],
|
|
22
|
+
].filter((name) => Boolean(name)),
|
|
22
23
|
attributes: {
|
|
23
24
|
'role': 'region',
|
|
24
25
|
'style': 'display: block; color: inherit;'
|
|
25
26
|
}
|
|
26
27
|
}))(baseConfig);
|
|
27
28
|
if (config.html) {
|
|
28
|
-
content.element
|
|
29
|
+
setHTML(content.element, config.html);
|
|
29
30
|
}
|
|
30
31
|
else if (config.text) {
|
|
31
32
|
const paragraph = document.createElement("p");
|
|
@@ -133,7 +134,7 @@ export const createCardActions = (config = {}) => {
|
|
|
133
134
|
config.fullBleed ? `${PREFIX}-card-actions--full-bleed` : null,
|
|
134
135
|
config.vertical ? `${PREFIX}-card-actions--vertical` : null,
|
|
135
136
|
config.align ? `${PREFIX}-card-actions--${config.align}` : null
|
|
136
|
-
],
|
|
137
|
+
].filter((name) => Boolean(name)),
|
|
137
138
|
attributes: {
|
|
138
139
|
'role': 'group'
|
|
139
140
|
}
|
|
@@ -171,7 +172,7 @@ export const createCardMedia = (config = {}) => {
|
|
|
171
172
|
config.class,
|
|
172
173
|
config.aspectRatio ? `${PREFIX}-card-media--${config.aspectRatio.replace(':', '-')}` : null,
|
|
173
174
|
config.contain ? `${PREFIX}-card-media--contain` : null
|
|
174
|
-
]
|
|
175
|
+
].filter((name) => Boolean(name))
|
|
175
176
|
}))(baseConfig);
|
|
176
177
|
if (config.element instanceof HTMLElement) {
|
|
177
178
|
media.element.appendChild(config.element);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { safeUrl } from "../../../core/utils/url.js";
|
|
2
|
+
import { setHTML } from "../../../core/dom/html.js";
|
|
2
3
|
export const withSlides = (config) => (component) => {
|
|
3
4
|
const prefix = component.getClass("carousel");
|
|
4
5
|
const scroller = document.createElement("div");
|
|
@@ -22,7 +23,7 @@ export const withSlides = (config) => (component) => {
|
|
|
22
23
|
el.replaceChildren();
|
|
23
24
|
if (slide.content !== undefined) {
|
|
24
25
|
if (typeof slide.content === "string")
|
|
25
|
-
el
|
|
26
|
+
setHTML(el, slide.content);
|
|
26
27
|
else
|
|
27
28
|
el.appendChild(slide.content);
|
|
28
29
|
return;
|
|
@@ -18,6 +18,28 @@ const enhanceWithCheckable = (component, config) => {
|
|
|
18
18
|
syncIndeterminate();
|
|
19
19
|
return enhanced;
|
|
20
20
|
};
|
|
21
|
+
const clearIndeterminate = () => {
|
|
22
|
+
if (enhanced.input.indeterminate) {
|
|
23
|
+
enhanced.input.indeterminate = false;
|
|
24
|
+
syncIndeterminate();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
const { checkable } = enhanced;
|
|
28
|
+
if (checkable) {
|
|
29
|
+
const { check, uncheck, toggle } = checkable;
|
|
30
|
+
checkable.check = () => {
|
|
31
|
+
clearIndeterminate();
|
|
32
|
+
return check.call(checkable);
|
|
33
|
+
};
|
|
34
|
+
checkable.uncheck = () => {
|
|
35
|
+
clearIndeterminate();
|
|
36
|
+
return uncheck.call(checkable);
|
|
37
|
+
};
|
|
38
|
+
checkable.toggle = () => {
|
|
39
|
+
clearIndeterminate();
|
|
40
|
+
return toggle.call(checkable);
|
|
41
|
+
};
|
|
42
|
+
}
|
|
21
43
|
return enhanced;
|
|
22
44
|
};
|
|
23
45
|
const createCheckbox = (config = {}) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CheckboxConfig, BaseComponent, ApiOptions } from "./types.js";
|
|
1
|
+
import { CheckboxConfig, BaseComponent, ApiComponent, ApiOptions } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Default configuration for the Checkbox component
|
|
4
4
|
*/
|
|
@@ -48,8 +48,8 @@ export declare const withCheckIcon: (config: CheckboxConfig) => (component: Base
|
|
|
48
48
|
export declare const withLabelPosition: (config: CheckboxConfig) => (component: BaseComponent) => BaseComponent;
|
|
49
49
|
/**
|
|
50
50
|
* Creates API configuration for the Checkbox component
|
|
51
|
-
* @param {
|
|
51
|
+
* @param {ApiComponent} comp - Component with disabled, lifecycle, and checkable features
|
|
52
52
|
* @returns {ApiOptions} API configuration object
|
|
53
53
|
*/
|
|
54
|
-
export declare const getApiConfig: (comp:
|
|
54
|
+
export declare const getApiConfig: (comp: ApiComponent) => ApiOptions;
|
|
55
55
|
export default defaultConfig;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createComponentConfig, createElementConfig, } from "../../core/config/component.js";
|
|
2
|
+
import { setHTML } from "../../core/dom/html.js";
|
|
2
3
|
export const defaultConfig = {
|
|
3
4
|
variant: "filled",
|
|
4
5
|
labelPosition: "end",
|
|
@@ -12,11 +13,11 @@ export const getElementConfig = (config) => createElementConfig(config, {
|
|
|
12
13
|
export const withCheckIcon = (config) => (component) => {
|
|
13
14
|
const icon = document.createElement("span");
|
|
14
15
|
icon.className = `${config.prefix}-checkbox-icon`;
|
|
15
|
-
icon
|
|
16
|
+
setHTML(icon, `
|
|
16
17
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="currentColor">
|
|
17
18
|
<path d="M9.55 14.6L6.35 11.4l-1.9 1.9L9.55 18.4l10.9-10.9-1.9-1.9z"/>
|
|
18
19
|
</svg>
|
|
19
|
-
|
|
20
|
+
`);
|
|
20
21
|
component.element.appendChild(icon);
|
|
21
22
|
return component;
|
|
22
23
|
};
|
|
@@ -28,17 +29,17 @@ export const withLabelPosition = (config) => (component) => {
|
|
|
28
29
|
};
|
|
29
30
|
export const getApiConfig = (comp) => ({
|
|
30
31
|
disabled: {
|
|
31
|
-
enable: comp.disabled
|
|
32
|
-
disable: comp.disabled
|
|
32
|
+
enable: comp.disabled.enable,
|
|
33
|
+
disable: comp.disabled.disable,
|
|
33
34
|
},
|
|
34
35
|
lifecycle: {
|
|
35
|
-
destroy: comp.lifecycle
|
|
36
|
+
destroy: comp.lifecycle.destroy,
|
|
36
37
|
},
|
|
37
38
|
checkable: {
|
|
38
|
-
check: comp.checkable
|
|
39
|
-
uncheck: comp.checkable
|
|
40
|
-
toggle: comp.checkable
|
|
41
|
-
isChecked: comp.checkable
|
|
39
|
+
check: comp.checkable.check,
|
|
40
|
+
uncheck: comp.checkable.uncheck,
|
|
41
|
+
toggle: comp.checkable.toggle,
|
|
42
|
+
isChecked: comp.checkable.isChecked,
|
|
42
43
|
},
|
|
43
44
|
});
|
|
44
45
|
export default defaultConfig;
|
|
@@ -111,7 +111,8 @@ export interface CheckboxComponent {
|
|
|
111
111
|
*/
|
|
112
112
|
getValue: () => boolean;
|
|
113
113
|
/**
|
|
114
|
-
* Sets the checkbox's checked state
|
|
114
|
+
* Sets the checkbox's checked state.
|
|
115
|
+
* Clears indeterminate, as a user click does.
|
|
115
116
|
* @param value - Boolean or string ("true"/"false"/"1"/"0") value
|
|
116
117
|
* @returns Checkbox component for method chaining
|
|
117
118
|
*/
|
|
@@ -128,17 +129,20 @@ export interface CheckboxComponent {
|
|
|
128
129
|
*/
|
|
129
130
|
setValueAttribute: (value: string) => CheckboxComponent;
|
|
130
131
|
/**
|
|
131
|
-
* Checks the checkbox (sets checked=true)
|
|
132
|
+
* Checks the checkbox (sets checked=true).
|
|
133
|
+
* Clears indeterminate, as a user click does.
|
|
132
134
|
* @returns Checkbox component for method chaining
|
|
133
135
|
*/
|
|
134
136
|
check: () => CheckboxComponent;
|
|
135
137
|
/**
|
|
136
|
-
* Unchecks the checkbox (sets checked=false)
|
|
138
|
+
* Unchecks the checkbox (sets checked=false).
|
|
139
|
+
* Clears indeterminate, as a user click does.
|
|
137
140
|
* @returns Checkbox component for method chaining
|
|
138
141
|
*/
|
|
139
142
|
uncheck: () => CheckboxComponent;
|
|
140
143
|
/**
|
|
141
|
-
* Toggles the checkbox's checked state
|
|
144
|
+
* Toggles the checkbox's checked state.
|
|
145
|
+
* Clears indeterminate, as a user click does.
|
|
142
146
|
* @returns Checkbox component for method chaining
|
|
143
147
|
*/
|
|
144
148
|
toggle: () => CheckboxComponent;
|
|
@@ -250,3 +254,14 @@ export interface BaseComponent {
|
|
|
250
254
|
isChecked: () => boolean;
|
|
251
255
|
};
|
|
252
256
|
}
|
|
257
|
+
/**
|
|
258
|
+
* Component as it reaches the API step of the pipeline.
|
|
259
|
+
*
|
|
260
|
+
* `BaseComponent` marks the feature controllers optional because a component
|
|
261
|
+
* part-way through the pipeline has not got them yet. By the time
|
|
262
|
+
* `getApiConfig` runs, the checkable, disabled and lifecycle features have all
|
|
263
|
+
* been applied, so they are there.
|
|
264
|
+
* @category Components
|
|
265
|
+
* @internal
|
|
266
|
+
*/
|
|
267
|
+
export type ApiComponent = BaseComponent & Required<Pick<BaseComponent, "checkable" | "disabled" | "lifecycle">>;
|
|
@@ -37,7 +37,9 @@ export const withAPI = (options) => (component) => {
|
|
|
37
37
|
getValue() {
|
|
38
38
|
if (options.chips &&
|
|
39
39
|
typeof options.chips.getSelectedValues === "function") {
|
|
40
|
-
const values = options.chips
|
|
40
|
+
const values = options.chips
|
|
41
|
+
.getSelectedValues()
|
|
42
|
+
.filter((value) => value !== null);
|
|
41
43
|
if (!options.config?.multiSelect) {
|
|
42
44
|
return values.length > 0 ? values[0] : null;
|
|
43
45
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setHTML } from "../../../core/dom/html.js";
|
|
1
2
|
export const withAPI = ({ disabled, lifecycle }) => (component) => {
|
|
2
3
|
let isSelected = component.element.classList.contains(`${component.getClass("chip")}--selected`);
|
|
3
4
|
let chipValue = component.element.getAttribute("data-value") || null;
|
|
@@ -118,7 +119,7 @@ export const withAPI = ({ disabled, lifecycle }) => (component) => {
|
|
|
118
119
|
contentContainer.insertBefore(leadingIconElement, contentContainer.firstChild);
|
|
119
120
|
}
|
|
120
121
|
if (leadingIconElement) {
|
|
121
|
-
leadingIconElement
|
|
122
|
+
setHTML(leadingIconElement, icon || "");
|
|
122
123
|
if (!icon && leadingIconElement.parentNode) {
|
|
123
124
|
leadingIconElement.parentNode.removeChild(leadingIconElement);
|
|
124
125
|
}
|
|
@@ -141,7 +142,7 @@ export const withAPI = ({ disabled, lifecycle }) => (component) => {
|
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
if (trailingIconElement) {
|
|
144
|
-
trailingIconElement
|
|
145
|
+
setHTML(trailingIconElement, icon || "");
|
|
145
146
|
if (!icon && trailingIconElement.parentNode) {
|
|
146
147
|
trailingIconElement.parentNode.removeChild(trailingIconElement);
|
|
147
148
|
}
|
|
@@ -3,6 +3,7 @@ import { createBase, withElement } from "../../../core/compose/component.js";
|
|
|
3
3
|
import { withEvents, withVariant, withRipple, withDisabled, withLifecycle, } from "../../../core/compose/features/index.js";
|
|
4
4
|
import { withAPI } from "./api.js";
|
|
5
5
|
import { createBaseConfig, getElementConfig, getApiConfig } from "./config.js";
|
|
6
|
+
import { setHTML } from "../../../core/dom/html.js";
|
|
6
7
|
const createChip = (config = {}) => {
|
|
7
8
|
const baseConfig = createBaseConfig(config);
|
|
8
9
|
try {
|
|
@@ -17,7 +18,7 @@ const createChip = (config = {}) => {
|
|
|
17
18
|
if (config.leadingIcon || config.icon) {
|
|
18
19
|
const leadingIconElement = document.createElement("span");
|
|
19
20
|
leadingIconElement.className = `${chip.getClass("chip")}-leading-icon`;
|
|
20
|
-
leadingIconElement
|
|
21
|
+
setHTML(leadingIconElement, config.leadingIcon || config.icon || "");
|
|
21
22
|
contentContainer.appendChild(leadingIconElement);
|
|
22
23
|
}
|
|
23
24
|
if (config.text) {
|
|
@@ -29,11 +30,12 @@ const createChip = (config = {}) => {
|
|
|
29
30
|
if (config.trailingIcon) {
|
|
30
31
|
const trailingIconElement = document.createElement("span");
|
|
31
32
|
trailingIconElement.className = `${chip.getClass("chip")}-trailing-icon`;
|
|
32
|
-
trailingIconElement
|
|
33
|
-
|
|
33
|
+
setHTML(trailingIconElement, config.trailingIcon);
|
|
34
|
+
const onTrailingIconClick = config.onTrailingIconClick;
|
|
35
|
+
if (onTrailingIconClick) {
|
|
34
36
|
trailingIconElement.addEventListener("click", (e) => {
|
|
35
37
|
e.stopPropagation();
|
|
36
|
-
|
|
38
|
+
onTrailingIconClick(chip);
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
contentContainer.appendChild(trailingIconElement);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ElementComponent } from "../../core/compose/component.js";
|
|
2
2
|
import type { DisabledComponent } from "../../core/compose/features/disabled.js";
|
|
3
3
|
import type { LifecycleComponent } from "../../core/compose/features/lifecycle.js";
|
|
4
|
-
import { DatePickerComponent, DatePickerState, ApiOptions } from "./types.js";
|
|
4
|
+
import { DatePickerComponent, DatePickerState, DatePickerApiState, ApiOptions } from "./types.js";
|
|
5
5
|
/**
|
|
6
6
|
* Enhances a datepicker component with API methods
|
|
7
7
|
* @param {object} state - Current state of the datepicker
|
|
@@ -9,4 +9,10 @@ import { DatePickerComponent, DatePickerState, ApiOptions } from "./types.js";
|
|
|
9
9
|
* @returns {Function} Higher-order function that adds API methods to component
|
|
10
10
|
* @internal This is an internal utility for the DatePicker component
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The API's precondition, checked rather than asserted: the factory creates the
|
|
14
|
+
* input and installs the outside-click handler before it builds the API, and
|
|
15
|
+
* this verifies that at runtime instead of casting past it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const isReadyForApi: (state: DatePickerState) => state is DatePickerApiState;
|
|
18
|
+
export declare const withAPI: (state: DatePickerApiState, { disabled, lifecycle, events }: ApiOptions) => (component: ElementComponent & DisabledComponent & LifecycleComponent) => DatePickerComponent;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatDate, parseDate, addMonths } from "./utils.js";
|
|
2
|
+
export const isReadyForApi = (state) => state.input !== null && state.outsideClickHandler !== undefined;
|
|
2
3
|
export const withAPI = (state, { disabled, lifecycle, events }) => (component) => {
|
|
3
4
|
const calendar = {
|
|
4
5
|
goToDate(date) {
|
|
@@ -6,12 +6,22 @@ import { DatePickerConfig } from "./types.js";
|
|
|
6
6
|
* Default configuration for the DatePicker component
|
|
7
7
|
*/
|
|
8
8
|
export declare const defaultConfig: DatePickerConfig;
|
|
9
|
+
/**
|
|
10
|
+
* Configuration as it leaves `createBaseConfig`.
|
|
11
|
+
*
|
|
12
|
+
* `DatePickerConfig` marks these optional because a caller may omit them.
|
|
13
|
+
* Once the defaults have been merged and `closeOnSelect` derived from the
|
|
14
|
+
* variant, they are all present — and the state the component builds from
|
|
15
|
+
* this declares them non-optional. Saying so here is what lets that hold
|
|
16
|
+
* without a cast.
|
|
17
|
+
*/
|
|
18
|
+
export type ResolvedDatePickerConfig = DatePickerConfig & Required<Pick<DatePickerConfig, "variant" | "initialView" | "selectionMode" | "dateFormat" | "closeOnSelect">>;
|
|
9
19
|
/**
|
|
10
20
|
* Creates the base configuration for DatePicker component
|
|
11
21
|
* @param {DatePickerConfig} config - User provided configuration
|
|
12
|
-
* @returns {
|
|
22
|
+
* @returns {ResolvedDatePickerConfig} Complete configuration with defaults applied
|
|
13
23
|
*/
|
|
14
|
-
export declare const createBaseConfig: (config?: DatePickerConfig) =>
|
|
24
|
+
export declare const createBaseConfig: (config?: DatePickerConfig) => ResolvedDatePickerConfig;
|
|
15
25
|
/**
|
|
16
26
|
* Generates element configuration for the DatePicker container
|
|
17
27
|
* @param {DatePickerConfig} config - DatePicker configuration
|
|
@@ -44,7 +54,7 @@ export declare const getContainerConfig: (config: DatePickerConfig) => {
|
|
|
44
54
|
* @param {DatePickerConfig} config - DatePicker configuration
|
|
45
55
|
* @returns {Object} Element configuration object for input field
|
|
46
56
|
*/
|
|
47
|
-
export declare const getInputConfig: (config:
|
|
57
|
+
export declare const getInputConfig: (config: ResolvedDatePickerConfig) => {
|
|
48
58
|
tag: string;
|
|
49
59
|
componentName: string;
|
|
50
60
|
attributes: Record<string, unknown>;
|
|
@@ -71,7 +81,7 @@ export declare const getInputConfig: (config: DatePickerConfig) => {
|
|
|
71
81
|
* @param {DatePickerConfig} config - DatePicker configuration
|
|
72
82
|
* @returns {Object} Element configuration object for calendar container
|
|
73
83
|
*/
|
|
74
|
-
export declare const getCalendarConfig: (config:
|
|
84
|
+
export declare const getCalendarConfig: (config: ResolvedDatePickerConfig) => {
|
|
75
85
|
tag: string;
|
|
76
86
|
componentName: string;
|
|
77
87
|
attributes: Record<string, unknown>;
|