mtrl 0.10.4 → 0.10.6

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.
Files changed (230) hide show
  1. package/README.md +15 -5
  2. package/dist/README.md +15 -5
  3. package/dist/components/bottom-app-bar/bottom-app-bar.js +2 -1
  4. package/dist/components/bottom-app-bar/index.d.ts +3 -1
  5. package/dist/components/button/api.js +3 -0
  6. package/dist/components/button/constants.d.ts +2 -0
  7. package/dist/components/button/index.d.ts +1 -0
  8. package/dist/components/button/types.d.ts +7 -1
  9. package/dist/components/button-group/button-group.js +25 -6
  10. package/dist/components/button-group/config.js +2 -2
  11. package/dist/components/button-group/constants.d.ts +8 -2
  12. package/dist/components/button-group/index.d.ts +1 -0
  13. package/dist/components/button-group/types.d.ts +5 -1
  14. package/dist/components/card/index.d.ts +21 -3
  15. package/dist/components/card/index.js +1 -1
  16. package/dist/components/card/types.d.ts +20 -4
  17. package/dist/components/carousel/api.d.ts +1 -0
  18. package/dist/components/carousel/api.js +4 -0
  19. package/dist/components/carousel/features/scroll.d.ts +1 -0
  20. package/dist/components/carousel/features/scroll.js +117 -2
  21. package/dist/components/carousel/types.d.ts +11 -0
  22. package/dist/components/checkbox/api.js +3 -0
  23. package/dist/components/checkbox/config.js +1 -0
  24. package/dist/components/checkbox/types.d.ts +8 -2
  25. package/dist/components/chips/types.d.ts +10 -2
  26. package/dist/components/datepicker/index.d.ts +3 -1
  27. package/dist/components/datepicker/index.js +1 -1
  28. package/dist/components/extended-fab/api.d.ts +2 -0
  29. package/dist/components/extended-fab/api.js +3 -0
  30. package/dist/components/extended-fab/config.d.ts +1 -0
  31. package/dist/components/extended-fab/config.js +1 -0
  32. package/dist/components/extended-fab/types.d.ts +2 -0
  33. package/dist/components/fab/api.d.ts +2 -0
  34. package/dist/components/fab/api.js +3 -0
  35. package/dist/components/fab/config.d.ts +1 -0
  36. package/dist/components/fab/config.js +1 -0
  37. package/dist/components/fab/constants.d.ts +2 -1
  38. package/dist/components/fab/types.d.ts +2 -0
  39. package/dist/components/fab-menu/fab-menu.js +2 -1
  40. package/dist/components/icon-button/api.js +3 -0
  41. package/dist/components/icon-button/constants.d.ts +2 -0
  42. package/dist/components/icon-button/types.d.ts +5 -0
  43. package/dist/components/index.d.ts +18 -5
  44. package/dist/components/index.js +2 -1
  45. package/dist/components/list/types.d.ts +5 -3
  46. package/dist/components/menu/features/keyboard.js +8 -5
  47. package/dist/components/menu/features/opener.js +1 -1
  48. package/dist/components/menu/index.d.ts +1 -0
  49. package/dist/components/navigation-rail/types.d.ts +2 -0
  50. package/dist/components/progress/features/canvas.js +1 -1
  51. package/dist/components/progress/features/colors.d.ts +2 -1
  52. package/dist/components/progress/features/colors.js +20 -10
  53. package/dist/components/radios/api.d.ts +1 -0
  54. package/dist/components/radios/api.js +3 -0
  55. package/dist/components/radios/constants.d.ts +9 -9
  56. package/dist/components/radios/radio.d.ts +1 -0
  57. package/dist/components/radios/radio.js +1 -0
  58. package/dist/components/radios/types.d.ts +10 -3
  59. package/dist/components/search/features/states.d.ts +2 -0
  60. package/dist/components/search/features/states.js +12 -1
  61. package/dist/components/search/features/structure.js +2 -0
  62. package/dist/components/search/types.d.ts +6 -6
  63. package/dist/components/segmented-button/config.js +2 -2
  64. package/dist/components/select/api.js +3 -0
  65. package/dist/components/select/constants.d.ts +1 -1
  66. package/dist/components/select/features.js +2 -0
  67. package/dist/components/select/index.d.ts +1 -0
  68. package/dist/components/select/types.d.ts +6 -0
  69. package/dist/components/split-button/types.d.ts +5 -1
  70. package/dist/components/switch/api.js +3 -0
  71. package/dist/components/switch/config.js +1 -0
  72. package/dist/components/switch/index.d.ts +6 -2
  73. package/dist/components/switch/index.js +1 -1
  74. package/dist/components/switch/types.d.ts +6 -2
  75. package/dist/components/tabs/config.js +2 -2
  76. package/dist/components/tabs/index.d.ts +36 -3
  77. package/dist/components/tabs/index.js +2 -1
  78. package/dist/components/tabs/tab.js +8 -1
  79. package/dist/components/tabs/types.d.ts +3 -1
  80. package/dist/components/tabs/utils.js +2 -1
  81. package/dist/components/textfield/api.d.ts +5 -0
  82. package/dist/components/textfield/api.js +209 -188
  83. package/dist/components/textfield/config.js +4 -3
  84. package/dist/components/textfield/constants.d.ts +24 -7
  85. package/dist/components/textfield/constants.js +9 -7
  86. package/dist/components/textfield/features/index.d.ts +3 -1
  87. package/dist/components/textfield/features/index.js +1 -0
  88. package/dist/components/textfield/features/leading-icon.js +1 -0
  89. package/dist/components/textfield/features/placement.js +15 -2
  90. package/dist/components/textfield/features/required.d.ts +33 -0
  91. package/dist/components/textfield/features/required.js +50 -0
  92. package/dist/components/textfield/features/supporting-text.js +43 -21
  93. package/dist/components/textfield/features/trailing-icon.d.ts +23 -1
  94. package/dist/components/textfield/features/trailing-icon.js +39 -5
  95. package/dist/components/textfield/index.d.ts +16 -1
  96. package/dist/components/textfield/index.js +1 -0
  97. package/dist/components/textfield/textfield.js +2 -2
  98. package/dist/components/textfield/types.d.ts +33 -3
  99. package/dist/components/timepicker/types.d.ts +2 -1
  100. package/dist/components/toolbar/toolbar.js +2 -1
  101. package/dist/components/tooltip/api.js +20 -18
  102. package/dist/components/tooltip/constants.d.ts +4 -1
  103. package/dist/components/top-app-bar/index.d.ts +3 -1
  104. package/dist/components/top-app-bar/top-app-bar.d.ts +2 -57
  105. package/dist/components/top-app-bar/top-app-bar.js +2 -1
  106. package/dist/core/compose/features/input.d.ts +9 -2
  107. package/dist/core/utils/theme.d.ts +6 -2
  108. package/dist/core/utils/theme.js +12 -19
  109. package/dist/elements/carousel.d.ts +5 -0
  110. package/dist/elements/carousel.js +1 -0
  111. package/dist/elements/css/textfield.js +1 -1
  112. package/dist/elements/define.d.ts +8 -0
  113. package/dist/elements/define.js +2 -1
  114. package/dist/elements/dialog.js +1 -1
  115. package/dist/elements/icon-button.d.ts +3 -2
  116. package/dist/elements/index.d.ts +15 -4
  117. package/dist/elements/index.js +2 -1
  118. package/dist/elements/preupgrade.css +1 -1
  119. package/dist/elements/textfield.d.ts +1 -1
  120. package/dist/elements/textfield.js +1 -1
  121. package/dist/index.cjs +0 -0
  122. package/dist/package.json +1 -1
  123. package/dist/react/dialog.d.ts +3 -0
  124. package/dist/react/dialog.js +3 -0
  125. package/dist/react/index.d.ts +4 -1
  126. package/dist/react/index.js +2 -1
  127. package/dist/solid/dialog.d.ts +3 -0
  128. package/dist/solid/dialog.js +3 -0
  129. package/dist/solid/index.d.ts +4 -1
  130. package/dist/solid/index.js +2 -1
  131. package/dist/styles/badge.css +1 -1
  132. package/dist/styles/base.css +2 -2
  133. package/dist/styles/bottom-app-bar.css +1 -1
  134. package/dist/styles/bottom-sheet.css +1 -1
  135. package/dist/styles/button-group.css +1 -1
  136. package/dist/styles/button.css +1 -1
  137. package/dist/styles/card.css +1 -1
  138. package/dist/styles/carousel.css +1 -1
  139. package/dist/styles/checkbox.css +1 -1
  140. package/dist/styles/chips.css +1 -1
  141. package/dist/styles/datepicker.css +1 -1
  142. package/dist/styles/dialog.css +1 -1
  143. package/dist/styles/divider.css +1 -1
  144. package/dist/styles/drawer.css +1 -1
  145. package/dist/styles/extended-fab.css +1 -1
  146. package/dist/styles/fab-menu.css +1 -1
  147. package/dist/styles/fab.css +1 -1
  148. package/dist/styles/icon-button.css +1 -1
  149. package/dist/styles/list.css +1 -1
  150. package/dist/styles/loading-indicator.css +1 -1
  151. package/dist/styles/menu.css +1 -1
  152. package/dist/styles/navigation-rail.css +1 -1
  153. package/dist/styles/progress.css +1 -1
  154. package/dist/styles/radios.css +1 -1
  155. package/dist/styles/search.css +1 -1
  156. package/dist/styles/segmented-button.css +1 -1
  157. package/dist/styles/select.css +1 -1
  158. package/dist/styles/side-sheet.css +1 -1
  159. package/dist/styles/slider.css +1 -1
  160. package/dist/styles/snackbar.css +1 -1
  161. package/dist/styles/split-button.css +1 -1
  162. package/dist/styles/switch.css +1 -1
  163. package/dist/styles/tabs.css +1 -1
  164. package/dist/styles/textfield.css +2 -2
  165. package/dist/styles/timepicker.css +1 -1
  166. package/dist/styles/toolbar.css +1 -1
  167. package/dist/styles/tooltip.css +1 -1
  168. package/dist/styles/top-app-bar.css +1 -1
  169. package/dist/styles/utilities.css +1 -1
  170. package/dist/styles.css +2 -2
  171. package/dist/svelte/Dialog.svelte +3 -0
  172. package/dist/svelte/index.d.ts +5 -1
  173. package/dist/svelte/index.js +5 -1
  174. package/dist/themes/autumn.css +2 -2
  175. package/dist/themes/baseline.css +2 -2
  176. package/dist/themes/brownbeige.css +2 -2
  177. package/dist/themes/browngreen.css +2 -2
  178. package/dist/themes/content.css +2 -2
  179. package/dist/themes/desert.css +2 -2
  180. package/dist/themes/expressive.css +2 -2
  181. package/dist/themes/fidelity.css +2 -2
  182. package/dist/themes/forest.css +2 -2
  183. package/dist/themes/fruit-salad.css +2 -2
  184. package/dist/themes/highcontrast.css +2 -2
  185. package/dist/themes/legacy.css +2 -2
  186. package/dist/themes/material.css +2 -2
  187. package/dist/themes/monochrome.css +2 -2
  188. package/dist/themes/neutral.css +2 -2
  189. package/dist/themes/ocean.css +2 -2
  190. package/dist/themes/rainbow.css +2 -2
  191. package/dist/themes/sageivory.css +2 -2
  192. package/dist/themes/spring.css +2 -2
  193. package/dist/themes/summer.css +2 -2
  194. package/dist/themes/sunset.css +2 -2
  195. package/dist/themes/tealcaramel.css +2 -2
  196. package/dist/themes/vibrant.css +2 -2
  197. package/dist/themes/winter.css +2 -2
  198. package/dist/vue/dialog.d.ts +3 -0
  199. package/dist/vue/dialog.js +3 -0
  200. package/dist/vue/index.d.ts +4 -1
  201. package/dist/vue/index.js +2 -1
  202. package/package.json +1 -1
  203. package/src/styles/abstract/_theme.scss +6 -6
  204. package/src/styles/abstract/_variables.scss +14 -4
  205. package/src/styles/components/_textfield.scss +61 -8
  206. package/src/styles/themes/_autumn.scss +173 -0
  207. package/src/styles/themes/_base-theme.scss +100 -12
  208. package/src/styles/themes/_baseline.scss +172 -0
  209. package/src/styles/themes/_brownbeige.scss +150 -0
  210. package/src/styles/themes/_browngreen.scss +183 -0
  211. package/src/styles/themes/_content.scss +148 -0
  212. package/src/styles/themes/_desert.scss +150 -0
  213. package/src/styles/themes/_expressive.scss +150 -0
  214. package/src/styles/themes/_fidelity.scss +148 -0
  215. package/src/styles/themes/_forest.scss +175 -0
  216. package/src/styles/themes/_fruit-salad.scss +150 -0
  217. package/src/styles/themes/_highcontrast.scss +74 -0
  218. package/src/styles/themes/_legacy.scss +181 -0
  219. package/src/styles/themes/_material.scss +181 -0
  220. package/src/styles/themes/_monochrome.scss +134 -0
  221. package/src/styles/themes/_neutral.scss +150 -0
  222. package/src/styles/themes/_ocean.scss +175 -0
  223. package/src/styles/themes/_rainbow.scss +150 -0
  224. package/src/styles/themes/_sageivory.scss +150 -0
  225. package/src/styles/themes/_spring.scss +171 -0
  226. package/src/styles/themes/_summer.scss +150 -0
  227. package/src/styles/themes/_sunset.scss +175 -0
  228. package/src/styles/themes/_tealcaramel.scss +150 -0
  229. package/src/styles/themes/_vibrant.scss +150 -0
  230. package/src/styles/themes/_winter.scss +181 -0
@@ -0,0 +1,33 @@
1
+ import type { BaseComponent, ElementComponent } from "../../../core/compose/component.js";
2
+ import type { LabelManager } from "../../../core/compose/features/textlabel.js";
3
+ /**
4
+ * A required field's asterisk (FLO-301). M3's text field guidelines mark a
5
+ * required field with an asterisk after its label, and Material's own web
6
+ * text field appends it to the label text in the label's colour. Here it is a
7
+ * span after the text, hidden from screen readers: the input's native
8
+ * `required` already announces the field as required, and "star" would not.
9
+ */
10
+ export interface RequiredConfig {
11
+ /** Whether the field is required; the input gets `required` from withTextInput */
12
+ required?: boolean;
13
+ /**
14
+ * No asterisk on a required field. M3 lets a form whose fields are mostly
15
+ * required mark its optional ones instead (Material Web's `noAsterisk`).
16
+ */
17
+ noAsterisk?: boolean;
18
+ prefix?: string;
19
+ componentName?: string;
20
+ }
21
+ /** What this feature installs */
22
+ export interface RequiredFeature {
23
+ /** Makes the field required or optional, its asterisk with it */
24
+ setRequired: (required: boolean) => RequiredFeature;
25
+ /** Whether the field is required */
26
+ isRequired: () => boolean;
27
+ }
28
+ interface LabelledInputComponent extends ElementComponent {
29
+ input?: HTMLInputElement | HTMLTextAreaElement;
30
+ label?: LabelManager;
31
+ }
32
+ export declare const withRequired: <T extends RequiredConfig & object>(config: T) => <C extends LabelledInputComponent>(component: C) => C & RequiredFeature & BaseComponent;
33
+ export {};
@@ -0,0 +1,50 @@
1
+ export const withRequired = (config) => (component) => {
2
+ const PREFIX = config.prefix || "mtrl";
3
+ const NAME = config.componentName || "textfield";
4
+ const label = component.label;
5
+ let required = Boolean(config.required);
6
+ const asterisk = () => label?.getElement().querySelector(`.${PREFIX}-${NAME}__required`) ?? null;
7
+ const render = () => {
8
+ if (!label)
9
+ return;
10
+ const shown = asterisk();
11
+ if (required && !config.noAsterisk) {
12
+ if (shown)
13
+ return;
14
+ const span = document.createElement("span");
15
+ span.className = `${PREFIX}-${NAME}__required`;
16
+ span.setAttribute("aria-hidden", "true");
17
+ span.textContent = "*";
18
+ label.getElement().append(span);
19
+ }
20
+ else {
21
+ shown?.remove();
22
+ }
23
+ };
24
+ if (label) {
25
+ const setText = label.setText.bind(label);
26
+ label.setText = (text) => {
27
+ setText(text);
28
+ render();
29
+ return label;
30
+ };
31
+ label.getText = () => {
32
+ const text = label.getElement().textContent ?? "";
33
+ return asterisk() ? text.slice(0, -1) : text;
34
+ };
35
+ }
36
+ render();
37
+ return {
38
+ ...component,
39
+ setRequired(next) {
40
+ required = next;
41
+ if (component.input)
42
+ component.input.required = next;
43
+ render();
44
+ return this;
45
+ },
46
+ isRequired() {
47
+ return required;
48
+ },
49
+ };
50
+ };
@@ -14,27 +14,42 @@ export const withSupportingText = (config) => (component) => {
14
14
  else
15
15
  removeIdRef(component.input, "aria-describedby", supportingId);
16
16
  };
17
- const createSupportingElement = (text, isError = false) => {
17
+ let pendingFrame = 0;
18
+ const cancelFill = () => {
19
+ if (pendingFrame)
20
+ cancelAnimationFrame(pendingFrame);
21
+ pendingFrame = 0;
22
+ };
23
+ const createSupportingElement = () => {
18
24
  const element = document.createElement("div");
19
25
  element.className = `${PREFIX}-${COMPONENT}__helper`;
20
26
  element.id = supportingId;
21
- element.textContent = text;
22
- if (isError)
23
- element.classList.add(`${PREFIX}-${COMPONENT}__helper--error`);
27
+ element.setAttribute("aria-live", "polite");
24
28
  return element;
25
29
  };
30
+ const markError = (element, isError) => {
31
+ element.classList.toggle(`${PREFIX}-${COMPONENT}__helper--error`, isError);
32
+ };
33
+ const detach = () => {
34
+ cancelFill();
35
+ if (!supportingElement)
36
+ return;
37
+ supportingElement.remove();
38
+ supportingElement = null;
39
+ row.release();
40
+ describe(null);
41
+ };
26
42
  if (config.supportingText) {
27
- supportingElement = createSupportingElement(config.supportingText, config.error);
43
+ supportingElement = createSupportingElement();
44
+ supportingElement.textContent = config.supportingText;
45
+ markError(supportingElement, Boolean(config.error));
28
46
  show(supportingElement);
29
47
  describe(supportingElement);
30
48
  }
31
49
  if ("lifecycle" in component && component.lifecycle?.destroy) {
32
50
  const originalDestroy = component.lifecycle.destroy;
33
51
  component.lifecycle.destroy = () => {
34
- if (supportingElement) {
35
- supportingElement.remove();
36
- row.release();
37
- }
52
+ detach();
38
53
  originalDestroy.call(component.lifecycle);
39
54
  };
40
55
  }
@@ -44,22 +59,29 @@ export const withSupportingText = (config) => (component) => {
44
59
  return supportingElement;
45
60
  },
46
61
  setSupportingText(text, isError = false) {
47
- supportingElement?.remove();
48
- supportingElement = text ? createSupportingElement(text, isError) : null;
49
- if (supportingElement)
50
- show(supportingElement);
51
- else
52
- row.release();
62
+ if (!text) {
63
+ detach();
64
+ return this;
65
+ }
66
+ if (supportingElement) {
67
+ supportingElement.textContent = text;
68
+ markError(supportingElement, isError);
69
+ return this;
70
+ }
71
+ supportingElement = createSupportingElement();
72
+ supportingElement.textContent = text;
73
+ markError(supportingElement, isError);
74
+ show(supportingElement);
53
75
  describe(supportingElement);
76
+ const element = supportingElement;
77
+ pendingFrame = requestAnimationFrame(() => {
78
+ pendingFrame = 0;
79
+ element.replaceChildren(document.createTextNode(element.textContent ?? ""));
80
+ });
54
81
  return this;
55
82
  },
56
83
  removeSupportingText() {
57
- if (supportingElement) {
58
- supportingElement.remove();
59
- supportingElement = null;
60
- row.release();
61
- describe(null);
62
- }
84
+ detach();
63
85
  return this;
64
86
  },
65
87
  };
@@ -4,10 +4,18 @@ import { BaseComponent, ElementComponent } from "../../../core/compose/component
4
4
  */
5
5
  interface InputElementComponent extends ElementComponent {
6
6
  input?: HTMLInputElement | HTMLTextAreaElement;
7
+ emit?: (event: string, data?: unknown) => void;
7
8
  lifecycle?: {
8
9
  destroy: () => void;
9
10
  };
10
11
  }
12
+ /** What an interactive trailing icon's `trailing` event carries (FLO-301) */
13
+ export interface TextfieldTrailingPayload {
14
+ /** The field's value when the icon was activated */
15
+ value: string;
16
+ /** The click (Enter and Space reach a button as one) */
17
+ event: MouseEvent;
18
+ }
11
19
  /**
12
20
  * Configuration for trailing icon feature
13
21
  */
@@ -16,6 +24,18 @@ export interface TrailingIconConfig {
16
24
  * Trailing icon HTML content
17
25
  */
18
26
  trailingIcon?: string;
27
+ /**
28
+ * Makes the trailing icon a button with this accessible name: clear, show
29
+ * password, open a menu (FLO-301). M3 draws an interactive trailing icon as an
30
+ * icon button, as Compose's trailing slot holds an `IconButton`: a 40dp state
31
+ * layer and a 48dp target. Activating it emits `trailing`. Without a label the
32
+ * icon is decorative.
33
+ */
34
+ trailingIconLabel?: string;
35
+ /** Called when the trailing icon button is activated, after `trailing` is emitted */
36
+ onTrailingClick?: (event: TextfieldTrailingPayload) => void;
37
+ /** Whether the field starts disabled; the trailing button is disabled with it */
38
+ disabled?: boolean;
19
39
  /**
20
40
  * CSS class prefix
21
41
  */
@@ -45,9 +65,11 @@ export interface TrailingIconFeature {
45
65
  /**
46
66
  * Sets trailing icon content
47
67
  * @param html - HTML content for the icon
68
+ * @param label - The button's accessible name, making the icon a button; an empty
69
+ * string makes it decorative again; left out, the icon keeps what it is
48
70
  * @returns Component instance for chaining
49
71
  */
50
- setTrailingIcon: (html: string) => TrailingIconComponent;
72
+ setTrailingIcon: (html: string, label?: string) => TrailingIconComponent;
51
73
  /**
52
74
  * Removes trailing icon
53
75
  * @returns Component instance for chaining
@@ -4,12 +4,44 @@ export const withTrailingIcon = (config) => (component) => {
4
4
  const PREFIX = config.prefix || 'mtrl';
5
5
  const NAME = config.componentName || 'textfield';
6
6
  let slot = null;
7
+ let label = config.trailingIconLabel ?? '';
8
+ const activate = (event) => {
9
+ if (component.input?.disabled)
10
+ return;
11
+ const detail = { value: component.input?.value ?? '', event };
12
+ component.emit?.('trailing', detail);
13
+ config.onTrailingClick?.(detail);
14
+ };
15
+ const create = () => {
16
+ const className = `${PREFIX}-${NAME}__trailing-icon`;
17
+ if (!label) {
18
+ const element = document.createElement('span');
19
+ element.className = className;
20
+ return element;
21
+ }
22
+ const button = document.createElement('button');
23
+ button.type = 'button';
24
+ button.className = `${className} ${className}--button`;
25
+ button.setAttribute('aria-label', label);
26
+ button.disabled = Boolean(component.input?.disabled ?? config.disabled);
27
+ button.addEventListener('click', activate);
28
+ return button;
29
+ };
7
30
  const ensureSlot = () => {
8
- if (slot && slot.parentNode)
31
+ const wanted = label ? 'BUTTON' : 'SPAN';
32
+ if (slot && slot.parentNode && slot.tagName === wanted) {
33
+ if (label)
34
+ slot.setAttribute('aria-label', label);
9
35
  return slot;
10
- const element = document.createElement('span');
11
- element.className = `${PREFIX}-${NAME}__trailing-icon`;
12
- fieldOf(component).appendChild(element);
36
+ }
37
+ const element = create();
38
+ if (slot?.parentNode) {
39
+ element.append(...Array.from(slot.childNodes));
40
+ slot.replaceWith(element);
41
+ }
42
+ else {
43
+ fieldOf(component).appendChild(element);
44
+ }
13
45
  component.element.classList.add(`${PREFIX}-${NAME}--with-trailing-icon`);
14
46
  if (component.input) {
15
47
  component.input.classList.add(`${PREFIX}-${NAME}__input--with-trailing-icon`);
@@ -42,7 +74,9 @@ export const withTrailingIcon = (config) => (component) => {
42
74
  return {
43
75
  ...component,
44
76
  trailingIcon: slot,
45
- setTrailingIcon(html) {
77
+ setTrailingIcon(html, nextLabel) {
78
+ if (nextLabel !== undefined)
79
+ label = nextLabel;
46
80
  this.trailingIcon = ensureSlot();
47
81
  write(this.trailingIcon, html);
48
82
  return this;
@@ -1,2 +1,17 @@
1
1
  export { default } from "./textfield.js";
2
- export type { TextfieldConfig, TextfieldComponent, TextfieldDensity, TextfieldEvents, TextfieldValuePayload, TextfieldFocusPayload, } from "./types.js";
2
+ export { default as createTextField } from "./textfield.js";
3
+ export type { TextfieldConfig as TextFieldConfig, TextfieldComponent as TextFieldComponent,
4
+ /** @deprecated Use TextFieldConfig: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
5
+ TextfieldConfig,
6
+ /** @deprecated Use TextFieldComponent: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
7
+ TextfieldComponent, TextfieldDensity as TextFieldDensity, TextfieldEvents as TextFieldEvents, TextfieldValuePayload as TextFieldValuePayload, TextfieldFocusPayload as TextFieldFocusPayload, TextfieldTrailingPayload as TextFieldTrailingPayload,
8
+ /** @deprecated Use TextFieldDensity: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
9
+ TextfieldDensity,
10
+ /** @deprecated Use TextFieldEvents: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
11
+ TextfieldEvents,
12
+ /** @deprecated Use TextFieldValuePayload: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
13
+ TextfieldValuePayload,
14
+ /** @deprecated Use TextFieldFocusPayload: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
15
+ TextfieldFocusPayload,
16
+ /** @deprecated Use TextFieldTrailingPayload: M3 writes "text field" as two words. Removed in 1.0 (FLO-383). */
17
+ TextfieldTrailingPayload, } from "./types.js";
@@ -1 +1,2 @@
1
1
  export { default } from "./textfield.js";
2
+ export { default as createTextField } from "./textfield.js";
@@ -2,12 +2,12 @@ import { pipe } from "../../core/compose/index.js";
2
2
  import { createBase, withElement } from "../../core/compose/component.js";
3
3
  import { withEvents, withDisabled, withLifecycle, withVariant, withTextInput, withTextLabel, } from "../../core/compose/features/index.js";
4
4
  import { withAPI } from "./api.js";
5
- import { withLeadingIcon, withTrailingIcon, withSupportingText, withPrefixText, withSuffixText, withPlacement, withDensity, withError, withField, withCounter, } from "./features/index.js";
5
+ import { withLeadingIcon, withTrailingIcon, withSupportingText, withPrefixText, withSuffixText, withPlacement, withDensity, withError, withField, withCounter, withRequired, } from "./features/index.js";
6
6
  import { createBaseConfig, getElementConfig, getApiConfig } from "./config.js";
7
7
  const createTextfield = (config = {}) => {
8
8
  const baseConfig = createBaseConfig(config);
9
9
  try {
10
- const textfield = pipe(createBase, withEvents(), withElement(getElementConfig(baseConfig)), withLifecycle(), withVariant(baseConfig), withField(baseConfig), withTextInput(baseConfig), withDensity(baseConfig), withTextLabel(baseConfig), withLeadingIcon(baseConfig), withTrailingIcon(baseConfig), withPrefixText(baseConfig), withSuffixText(baseConfig), withSupportingText(baseConfig), withError(baseConfig), withCounter(baseConfig), withDisabled(baseConfig), withPlacement(), (comp) => withAPI(getApiConfig(comp))(comp))(baseConfig);
10
+ const textfield = pipe(createBase, withEvents(), withElement(getElementConfig(baseConfig)), withLifecycle(), withVariant(baseConfig), withField(baseConfig), withTextInput(baseConfig), withDensity(baseConfig), withTextLabel(baseConfig), withRequired(baseConfig), withLeadingIcon(baseConfig), withTrailingIcon(baseConfig), withPrefixText(baseConfig), withSuffixText(baseConfig), withSupportingText(baseConfig), withError(baseConfig), withCounter(baseConfig), withDisabled(baseConfig), withPlacement(), (comp) => withAPI(getApiConfig(comp))(comp))(baseConfig);
11
11
  return textfield;
12
12
  }
13
13
  catch (error) {
@@ -1,4 +1,6 @@
1
1
  import type { EventCallback } from "../../core/state/emitter.js";
2
+ import type { TextfieldTrailingPayload } from "./features/trailing-icon.js";
3
+ export type { TextfieldTrailingPayload };
2
4
  /**
3
5
  * Available Textfield variants
4
6
  */
@@ -60,8 +62,13 @@ export interface TextfieldConfig {
60
62
  value?: string;
61
63
  /** Placeholder text */
62
64
  placeholder?: string;
63
- /** Whether input is required */
65
+ /** Whether input is required; the label gets an asterisk (FLO-301) */
64
66
  required?: boolean;
67
+ /**
68
+ * No asterisk on a required field: M3 lets a form whose fields are mostly
69
+ * required mark its optional ones instead (FLO-301)
70
+ */
71
+ noAsterisk?: boolean;
65
72
  /** Whether textfield is disabled */
66
73
  disabled?: boolean;
67
74
  /** Whether textfield is readonly */
@@ -76,6 +83,15 @@ export interface TextfieldConfig {
76
83
  leadingIcon?: string;
77
84
  /** Trailing icon HTML content */
78
85
  trailingIcon?: string;
86
+ /**
87
+ * Makes the trailing icon a button with this accessible name (clear, show
88
+ * password, …), emitting `trailing` when activated. Without it the icon is
89
+ * decorative. M3 draws an interactive trailing icon as an icon button, as
90
+ * Compose's trailing slot holds an `IconButton` (FLO-301).
91
+ */
92
+ trailingIconLabel?: string;
93
+ /** Called when the trailing icon button is activated, after `trailing` is emitted */
94
+ onTrailingClick?: (event: TextfieldTrailingPayload) => void;
79
95
  /** Supporting text content */
80
96
  supportingText?: string;
81
97
  /** Whether supporting text indicates an error */
@@ -103,6 +119,8 @@ export interface TextfieldFocusPayload {
103
119
  }
104
120
  /** Events emitted by the text input feature. setValue() is silent. */
105
121
  export interface TextfieldEvents {
122
+ /** The trailing icon button was activated (`trailingIconLabel`; FLO-301) */
123
+ trailing: (payload: TextfieldTrailingPayload) => void;
106
124
  input: (payload: TextfieldValuePayload) => void;
107
125
  change: (payload: TextfieldValuePayload) => void;
108
126
  focus: (payload: TextfieldFocusPayload) => void;
@@ -148,9 +166,17 @@ export interface TextfieldComponent {
148
166
  /** Trailing icon element (if present) */
149
167
  trailingIcon: HTMLElement | null;
150
168
  /** Sets the trailing icon HTML content */
151
- setTrailingIcon: (html: string) => TextfieldComponent;
169
+ /**
170
+ * Sets the trailing icon. `label` makes it a button with that accessible name,
171
+ * an empty one makes it decorative; left out, the icon keeps what it is.
172
+ */
173
+ setTrailingIcon: (html: string, label?: string) => TextfieldComponent;
152
174
  /** Removes the trailing icon */
153
175
  removeTrailingIcon: () => TextfieldComponent;
176
+ /** Makes the field required or optional, the label's asterisk with it (FLO-301) */
177
+ setRequired: (required: boolean) => TextfieldComponent;
178
+ /** Whether the field is required */
179
+ isRequired: () => boolean;
154
180
  /** Supporting text element (if present) */
155
181
  supportingTextElement: HTMLElement | null;
156
182
  /** Sets the supporting text content */
@@ -187,6 +213,8 @@ export interface TextfieldComponent {
187
213
  enable: () => TextfieldComponent;
188
214
  /** Disables the textfield */
189
215
  disable: () => TextfieldComponent;
216
+ /** Whether the text field is disabled (FLO-384) */
217
+ isDisabled: () => boolean;
190
218
  /** Destroys the textfield component and cleans up resources */
191
219
  destroy: () => void;
192
220
  }
@@ -197,6 +225,7 @@ export interface ApiOptions {
197
225
  disabled: {
198
226
  enable: () => void;
199
227
  disable: () => void;
228
+ isDisabled: () => boolean;
200
229
  };
201
230
  lifecycle: {
202
231
  destroy: () => void;
@@ -227,7 +256,7 @@ export interface BaseComponent {
227
256
  setLeadingIcon?: (html: string) => void;
228
257
  removeLeadingIcon?: () => void;
229
258
  trailingIcon?: HTMLElement | null;
230
- setTrailingIcon?: (html: string) => void;
259
+ setTrailingIcon?: (html: string, label?: string) => void;
231
260
  removeTrailingIcon?: () => void;
232
261
  supportingTextElement?: HTMLElement | null;
233
262
  setSupportingText?: (text: string, isError?: boolean) => void;
@@ -244,6 +273,7 @@ export interface BaseComponent {
244
273
  disabled?: {
245
274
  enable: () => void;
246
275
  disable: () => void;
276
+ isDisabled: () => boolean;
247
277
  };
248
278
  lifecycle?: {
249
279
  destroy: () => void;
@@ -222,7 +222,8 @@ export interface TimePickerConfig {
222
222
  */
223
223
  onClose?: () => void;
224
224
  /**
225
- * Callback when time is confirmed; receives the same 24-hour value as getValue()
225
+ * Callback when time is confirmed; receives the same 24-hour value as getValue().
226
+ * In 1.0 it receives `{ value }`, the object a `confirm` listener receives.
226
227
  */
227
228
  onConfirm?: (time: string) => void;
228
229
  /**
@@ -1,3 +1,4 @@
1
+ import { processClassNames } from "../../core/config/component.js";
1
2
  import { pipe } from "../../core/compose/pipe.js";
2
3
  import { createBase, withElement } from "../../core/compose/component.js";
3
4
  import { withEvents, withLifecycle } from "../../core/compose/features/index.js";
@@ -27,7 +28,7 @@ const createToolbar = (config = {}) => {
27
28
  placement !== TOOLBAR_PLACEMENTS.NONE ? `${block}--${placement}` : "",
28
29
  variant === TOOLBAR_VARIANTS.DOCKED ? `${block}--${settings.arrangement}` : "",
29
30
  settings.elevated ? `${block}--elevated` : "",
30
- settings.class ?? "",
31
+ processClassNames(settings.className || ""),
31
32
  ].filter(Boolean),
32
33
  }), withLifecycle())(settings);
33
34
  const resources = getCleanup(component);
@@ -39,7 +39,9 @@ export const withAPI = ({ lifecycle }) => (component) => {
39
39
  const calculatePosition = () => {
40
40
  if (!target)
41
41
  return { top: 0, left: 0 };
42
- const tooltipRect = component.element.getBoundingClientRect();
42
+ component.element.style.left = "0px";
43
+ const tooltipWidth = component.element.offsetWidth;
44
+ const tooltipHeight = component.element.offsetHeight;
43
45
  const targetRect = target.getBoundingClientRect();
44
46
  const scrollY = topLayer ? 0 : window.scrollY || window.pageYOffset;
45
47
  const scrollX = topLayer ? 0 : window.scrollX || window.pageXOffset;
@@ -49,23 +51,23 @@ export const withAPI = ({ lifecycle }) => (component) => {
49
51
  let arrowPosition;
50
52
  switch (position) {
51
53
  case TOOLTIP_POSITIONS.TOP:
52
- top = targetRect.top + scrollY - tooltipRect.height - offset;
54
+ top = targetRect.top + scrollY - tooltipHeight - offset;
53
55
  left =
54
56
  targetRect.left +
55
57
  scrollX +
56
58
  targetRect.width / 2 -
57
- tooltipRect.width / 2;
59
+ tooltipWidth / 2;
58
60
  arrowPosition = "bottom";
59
61
  break;
60
62
  case TOOLTIP_POSITIONS.TOP_START:
61
- top = targetRect.top + scrollY - tooltipRect.height - offset;
63
+ top = targetRect.top + scrollY - tooltipHeight - offset;
62
64
  left = targetRect.left + scrollX;
63
65
  arrowPosition = "bottom-start";
64
66
  break;
65
67
  case TOOLTIP_POSITIONS.TOP_END:
66
- top = targetRect.top + scrollY - tooltipRect.height - offset;
68
+ top = targetRect.top + scrollY - tooltipHeight - offset;
67
69
  left =
68
- targetRect.left + scrollX + targetRect.width - tooltipRect.width;
70
+ targetRect.left + scrollX + targetRect.width - tooltipWidth;
69
71
  arrowPosition = "bottom-end";
70
72
  break;
71
73
  case TOOLTIP_POSITIONS.RIGHT:
@@ -73,7 +75,7 @@ export const withAPI = ({ lifecycle }) => (component) => {
73
75
  targetRect.top +
74
76
  scrollY +
75
77
  targetRect.height / 2 -
76
- tooltipRect.height / 2;
78
+ tooltipHeight / 2;
77
79
  left = targetRect.left + scrollX + targetRect.width + offset;
78
80
  arrowPosition = "left";
79
81
  break;
@@ -84,7 +86,7 @@ export const withAPI = ({ lifecycle }) => (component) => {
84
86
  break;
85
87
  case TOOLTIP_POSITIONS.RIGHT_END:
86
88
  top =
87
- targetRect.top + scrollY + targetRect.height - tooltipRect.height;
89
+ targetRect.top + scrollY + targetRect.height - tooltipHeight;
88
90
  left = targetRect.left + scrollX + targetRect.width + offset;
89
91
  arrowPosition = "left-end";
90
92
  break;
@@ -94,7 +96,7 @@ export const withAPI = ({ lifecycle }) => (component) => {
94
96
  targetRect.left +
95
97
  scrollX +
96
98
  targetRect.width / 2 -
97
- tooltipRect.width / 2;
99
+ tooltipWidth / 2;
98
100
  arrowPosition = "top";
99
101
  break;
100
102
  case TOOLTIP_POSITIONS.BOTTOM_START:
@@ -105,7 +107,7 @@ export const withAPI = ({ lifecycle }) => (component) => {
105
107
  case TOOLTIP_POSITIONS.BOTTOM_END:
106
108
  top = targetRect.top + scrollY + targetRect.height + offset;
107
109
  left =
108
- targetRect.left + scrollX + targetRect.width - tooltipRect.width;
110
+ targetRect.left + scrollX + targetRect.width - tooltipWidth;
109
111
  arrowPosition = "top-end";
110
112
  break;
111
113
  case TOOLTIP_POSITIONS.LEFT:
@@ -113,19 +115,19 @@ export const withAPI = ({ lifecycle }) => (component) => {
113
115
  targetRect.top +
114
116
  scrollY +
115
117
  targetRect.height / 2 -
116
- tooltipRect.height / 2;
117
- left = targetRect.left + scrollX - tooltipRect.width - offset;
118
+ tooltipHeight / 2;
119
+ left = targetRect.left + scrollX - tooltipWidth - offset;
118
120
  arrowPosition = "right";
119
121
  break;
120
122
  case TOOLTIP_POSITIONS.LEFT_START:
121
123
  top = targetRect.top + scrollY;
122
- left = targetRect.left + scrollX - tooltipRect.width - offset;
124
+ left = targetRect.left + scrollX - tooltipWidth - offset;
123
125
  arrowPosition = "right-start";
124
126
  break;
125
127
  case TOOLTIP_POSITIONS.LEFT_END:
126
128
  top =
127
- targetRect.top + scrollY + targetRect.height - tooltipRect.height;
128
- left = targetRect.left + scrollX - tooltipRect.width - offset;
129
+ targetRect.top + scrollY + targetRect.height - tooltipHeight;
130
+ left = targetRect.left + scrollX - tooltipWidth - offset;
129
131
  arrowPosition = "right-end";
130
132
  break;
131
133
  default:
@@ -134,15 +136,15 @@ export const withAPI = ({ lifecycle }) => (component) => {
134
136
  targetRect.left +
135
137
  scrollX +
136
138
  targetRect.width / 2 -
137
- tooltipRect.width / 2;
139
+ tooltipWidth / 2;
138
140
  arrowPosition = "top";
139
141
  }
140
142
  const windowWidth = window.innerWidth;
141
143
  if (left < 0) {
142
144
  left = 0;
143
145
  }
144
- else if (left + tooltipRect.width > windowWidth) {
145
- left = windowWidth - tooltipRect.width;
146
+ else if (left + tooltipWidth > windowWidth) {
147
+ left = windowWidth - tooltipWidth;
146
148
  }
147
149
  return { top, left, arrowPosition };
148
150
  };
@@ -77,7 +77,10 @@ export declare const TOOLTIP_DEFAULTS: {
77
77
  readonly SHOW_ON_FOCUS: true;
78
78
  /** Whether to show tooltip on hover by default */
79
79
  readonly SHOW_ON_HOVER: true;
80
- /** Whether to allow rich HTML content by default */
80
+ /**
81
+ * Whether to allow rich HTML content by default
82
+ * @deprecated The `rich` option has no effect and is removed in 1.0, and this default with it.
83
+ */
81
84
  readonly RICH: false;
82
85
  };
83
86
  /**
@@ -6,4 +6,6 @@ import { createTopAppBar } from "./top-app-bar.js";
6
6
  export { TOP_APP_BAR_TYPES, TOP_APP_BAR_STATES, TOP_APP_BAR_DEFAULTS, TOP_APP_BAR_CLASSES } from "./constants.js";
7
7
  export default createTopAppBar;
8
8
  export { createTopAppBar };
9
- export type { TopAppBarConfig, TopAppBar } from "./types.js";
9
+ export type { TopAppBarConfig, TopAppBar as TopAppBarComponent,
10
+ /** @deprecated Use TopAppBarComponent, the name every component type has. Removed in 1.0 (FLO-383). */
11
+ TopAppBar, } from "./types.js";
@@ -2,63 +2,8 @@
2
2
  * @module components/top-app-bar
3
3
  * @description Top app bar implementation
4
4
  */
5
- import { ElementComponent } from "../../core/compose/index.js";
6
- import { TopAppBarConfig, TopAppBarType } from "./types.js";
7
- /**
8
- * Top app bar component interface
9
- */
10
- export interface TopAppBar extends ElementComponent {
11
- /**
12
- * Sets the title of the top app bar
13
- * @param {string} title - Title text
14
- * @returns {TopAppBar} TopAppBar instance for chaining
15
- */
16
- setTitle: (title: string) => TopAppBar;
17
- /**
18
- * Gets the current title
19
- * @returns {string} Current title text
20
- */
21
- getTitle: () => string;
22
- /**
23
- * Adds a leading navigation icon or element
24
- * @param {HTMLElement} element - Element to add to the leading section
25
- * @returns {TopAppBar} TopAppBar instance for chaining
26
- */
27
- addLeadingElement: (element: HTMLElement) => TopAppBar;
28
- /**
29
- * Adds a trailing action icon or element
30
- * @param {HTMLElement} element - Element to add to the trailing section
31
- * @returns {TopAppBar} TopAppBar instance for chaining
32
- */
33
- addTrailingElement: (element: HTMLElement) => TopAppBar;
34
- /**
35
- * Changes the top app bar type
36
- * @param {TopAppBarType} type - New app bar type
37
- * @returns {TopAppBar} TopAppBar instance for chaining
38
- */
39
- setType: (type: TopAppBarType) => TopAppBar;
40
- /**
41
- * Manually sets the scrolled state
42
- * @param {boolean} scrolled - Whether to show the scrolled state
43
- * @returns {TopAppBar} TopAppBar instance for chaining
44
- */
45
- setScrollState: (scrolled: boolean) => TopAppBar;
46
- /**
47
- * Gets the headline element
48
- * @returns {HTMLElement} Headline element
49
- */
50
- getHeadlineElement: () => HTMLElement;
51
- /**
52
- * Gets the leading container element
53
- * @returns {HTMLElement} Leading container element
54
- */
55
- getLeadingContainer: () => HTMLElement;
56
- /**
57
- * Gets the trailing container element
58
- * @returns {HTMLElement} Trailing container element
59
- */
60
- getTrailingContainer: () => HTMLElement;
61
- }
5
+ import type { TopAppBar, TopAppBarConfig } from "./types.js";
6
+ export type { TopAppBar };
62
7
  /**
63
8
  * Creates a top app bar component
64
9
  *