mtrl 0.3.6 → 0.3.7

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 (45) hide show
  1. package/package.json +1 -1
  2. package/src/components/button/api.ts +16 -0
  3. package/src/components/button/types.ts +9 -0
  4. package/src/components/menu/api.ts +15 -13
  5. package/src/components/menu/config.ts +5 -5
  6. package/src/components/menu/features/anchor.ts +99 -15
  7. package/src/components/menu/features/controller.ts +418 -221
  8. package/src/components/menu/features/index.ts +2 -1
  9. package/src/components/menu/features/position.ts +353 -0
  10. package/src/components/menu/index.ts +5 -5
  11. package/src/components/menu/menu.ts +18 -60
  12. package/src/components/menu/types.ts +17 -16
  13. package/src/components/select/api.ts +78 -0
  14. package/src/components/select/config.ts +76 -0
  15. package/src/components/select/features.ts +317 -0
  16. package/src/components/select/index.ts +38 -0
  17. package/src/components/select/select.ts +73 -0
  18. package/src/components/select/types.ts +355 -0
  19. package/src/components/textfield/api.ts +78 -6
  20. package/src/components/textfield/features/index.ts +17 -0
  21. package/src/components/textfield/features/leading-icon.ts +127 -0
  22. package/src/components/textfield/features/placement.ts +149 -0
  23. package/src/components/textfield/features/prefix-text.ts +107 -0
  24. package/src/components/textfield/features/suffix-text.ts +100 -0
  25. package/src/components/textfield/features/supporting-text.ts +113 -0
  26. package/src/components/textfield/features/trailing-icon.ts +108 -0
  27. package/src/components/textfield/textfield.ts +51 -15
  28. package/src/components/textfield/types.ts +70 -0
  29. package/src/core/collection/adapters/base.ts +62 -0
  30. package/src/core/collection/collection.ts +300 -0
  31. package/src/core/collection/index.ts +57 -0
  32. package/src/core/collection/list-manager.ts +333 -0
  33. package/src/index.ts +4 -1
  34. package/src/styles/abstract/_variables.scss +18 -0
  35. package/src/styles/components/_button.scss +21 -5
  36. package/src/styles/components/{_chip.scss → _chips.scss} +118 -4
  37. package/src/styles/components/_menu.scss +103 -24
  38. package/src/styles/components/_select.scss +265 -0
  39. package/src/styles/components/_textfield.scss +233 -42
  40. package/src/styles/main.scss +2 -1
  41. package/src/components/textfield/features.ts +0 -322
  42. package/src/core/collection/adapters/base.js +0 -26
  43. package/src/core/collection/collection.js +0 -259
  44. package/src/core/collection/list-manager.js +0 -157
  45. /package/src/core/collection/adapters/{route.js → route.ts} +0 -0
@@ -0,0 +1,149 @@
1
+ // src/components/textfield/features/placement.ts
2
+
3
+ import { BaseComponent, ElementComponent } from '../../../core/compose/component';
4
+
5
+ /**
6
+ * Component with placement management capabilities
7
+ */
8
+ export interface PlacementComponent extends BaseComponent {
9
+ /**
10
+ * Updates positions of all elements in the textfield
11
+ * @returns The component instance for chaining
12
+ */
13
+ updateElementPositions: () => PlacementComponent;
14
+ }
15
+
16
+ /**
17
+ * Handles dynamic positioning of textfield elements (label, prefix, suffix)
18
+ * This feature should be added last in the pipe to ensure all elements exist
19
+ *
20
+ * @returns Function that enhances a component with dynamic positioning
21
+ */
22
+ export const withPlacement = () =>
23
+ <C extends ElementComponent>(component: C): C & PlacementComponent => {
24
+ const PREFIX = component.config.prefix || 'mtrl';
25
+ const COMPONENT = component.config.componentName || 'textfield';
26
+
27
+ /**
28
+ * Updates positions of labels and adjusts input padding
29
+ * to accommodate prefix/suffix elements
30
+ */
31
+ const updateElementPositions = () => {
32
+ if (!component.element || !component.element.isConnected) return component;
33
+
34
+ // Get necessary elements
35
+ const labelEl = component.element.querySelector(`.${PREFIX}-${COMPONENT}-label`) as HTMLElement;
36
+ const prefixEl = component.element.querySelector(`.${PREFIX}-${COMPONENT}-prefix`);
37
+ const suffixEl = component.element.querySelector(`.${PREFIX}-${COMPONENT}-suffix`);
38
+
39
+ // Get component states
40
+ const isOutlined = component.element.classList.contains(`${PREFIX}-${COMPONENT}--outlined`);
41
+ const isFilled = component.element.classList.contains(`${PREFIX}-${COMPONENT}--filled`);
42
+ const isFocused = component.element.classList.contains(`${PREFIX}-${COMPONENT}--focused`);
43
+ const isEmpty = component.element.classList.contains(`${PREFIX}-${COMPONENT}--empty`);
44
+ const hasLeadingIcon = component.element.classList.contains(`${PREFIX}-${COMPONENT}--with-leading-icon`);
45
+
46
+ // Handle prefix positioning and input padding
47
+ if (prefixEl && component.input) {
48
+ const prefixWidth = prefixEl.getBoundingClientRect().width + 4; // 4px spacing
49
+ const inputPadding = prefixWidth + 12; // 12px additional padding
50
+
51
+ // Update input left padding
52
+ component.input.style.paddingLeft = `${inputPadding}px`;
53
+
54
+ // Update label position if present
55
+ if (labelEl) {
56
+ let labelPosition = inputPadding;
57
+
58
+ // Account for leading icon if present
59
+ if (hasLeadingIcon) {
60
+ labelPosition = Math.max(labelPosition, 44);
61
+ }
62
+
63
+ // Different positioning strategy based on variant and state
64
+ if (!isFocused && isEmpty) {
65
+ // When unfocused and empty, align with prefix/input
66
+ labelEl.style.left = `${labelPosition}px`;
67
+ } else {
68
+ // When focused or filled, move to default position
69
+ labelEl.style.left = '12px';
70
+ }
71
+
72
+ }
73
+ } else if (hasLeadingIcon && labelEl) {
74
+ // Handle case with leading icon but no prefix
75
+ if (isOutlined) {
76
+ if (!isFocused && isEmpty) {
77
+ // When unfocused and empty, align with icon
78
+ labelEl.style.left = '44px';
79
+ } else {
80
+ // When focused or filled, move to default position
81
+ labelEl.style.left = '12px';
82
+ }
83
+ }
84
+ // For filled variant, the CSS handles this
85
+ }
86
+
87
+ // Handle suffix positioning and input padding
88
+ if (suffixEl && component.input) {
89
+ const suffixWidth = suffixEl.getBoundingClientRect().width + 4; // 4px spacing
90
+ const inputPadding = suffixWidth + 12; // 12px additional padding
91
+
92
+ // Update input right padding
93
+ component.input.style.paddingRight = `${inputPadding}px`;
94
+ }
95
+
96
+ return component;
97
+ };
98
+
99
+ // Set up event listeners for dynamic positioning
100
+ const setupEventListeners = () => {
101
+ if (component.input) {
102
+ // Update positions when focus state changes
103
+ component.input.addEventListener('focus', () => {
104
+ component.element.classList.add(`${PREFIX}-${COMPONENT}--focused`);
105
+ setTimeout(updateElementPositions, 10);
106
+ });
107
+
108
+ component.input.addEventListener('blur', () => {
109
+ component.element.classList.remove(`${PREFIX}-${COMPONENT}--focused`);
110
+ setTimeout(updateElementPositions, 10);
111
+ });
112
+
113
+ // Update positions when empty state changes
114
+ component.input.addEventListener('input', () => {
115
+ const isEmpty = !component.input.value;
116
+ component.element.classList.toggle(`${PREFIX}-${COMPONENT}--empty`, isEmpty);
117
+ setTimeout(updateElementPositions, 10);
118
+ });
119
+
120
+ // Set initial empty state
121
+ if (!component.input.value) {
122
+ component.element.classList.add(`${PREFIX}-${COMPONENT}--empty`);
123
+ }
124
+ }
125
+
126
+ // Update positions on window resize
127
+ window.addEventListener('resize', updateElementPositions);
128
+ };
129
+
130
+ // Perform initial setup
131
+ setTimeout(() => {
132
+ setupEventListeners();
133
+ updateElementPositions();
134
+ }, 0);
135
+
136
+ // Add lifecycle integration
137
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
138
+ const originalDestroy = component.lifecycle.destroy;
139
+ component.lifecycle.destroy = () => {
140
+ window.removeEventListener('resize', updateElementPositions);
141
+ originalDestroy.call(component.lifecycle);
142
+ };
143
+ }
144
+
145
+ return {
146
+ ...component,
147
+ updateElementPositions
148
+ };
149
+ };
@@ -0,0 +1,107 @@
1
+ // src/components/textfield/features/prefix-text.ts
2
+
3
+ import { BaseComponent, ElementComponent } from '../../../core/compose/component';
4
+
5
+ /**
6
+ * Configuration for prefix text feature
7
+ */
8
+ export interface PrefixTextConfig {
9
+ /**
10
+ * Prefix text content to display before the input
11
+ */
12
+ prefixText?: string;
13
+
14
+ /**
15
+ * CSS class prefix
16
+ */
17
+ prefix?: string;
18
+
19
+ /**
20
+ * Component name
21
+ */
22
+ componentName?: string;
23
+
24
+ [key: string]: any;
25
+ }
26
+
27
+ /**
28
+ * Component with prefix text capabilities
29
+ */
30
+ export interface PrefixTextComponent extends BaseComponent {
31
+ /**
32
+ * Prefix text element
33
+ */
34
+ prefixTextElement: HTMLElement | null;
35
+
36
+ /**
37
+ * Sets prefix text content
38
+ * @param text - Text content to display before the input
39
+ * @returns Component instance for chaining
40
+ */
41
+ setPrefixText: (text: string) => PrefixTextComponent;
42
+
43
+ /**
44
+ * Removes prefix text
45
+ * @returns Component instance for chaining
46
+ */
47
+ removePrefixText: () => PrefixTextComponent;
48
+ }
49
+
50
+ /**
51
+ * Adds prefix text to a textfield component
52
+ * @param config - Configuration with prefix text settings
53
+ * @returns Function that enhances a component with prefix text
54
+ */
55
+ export const withPrefixText = <T extends PrefixTextConfig>(config: T) =>
56
+ <C extends ElementComponent>(component: C): C & PrefixTextComponent => {
57
+ if (!config.prefixText) {
58
+ return component as any;
59
+ }
60
+
61
+ // Create prefix text element
62
+ const PREFIX = config.prefix || 'mtrl';
63
+ const prefixElement = document.createElement('span');
64
+ prefixElement.className = `${PREFIX}-${config.componentName || 'textfield'}-prefix`;
65
+ prefixElement.textContent = config.prefixText;
66
+
67
+ // Add prefix text to the component
68
+ component.element.appendChild(prefixElement);
69
+
70
+ // Add prefix class to the component
71
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--with-prefix`);
72
+
73
+ // Add lifecycle integration
74
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
75
+ const originalDestroy = component.lifecycle.destroy;
76
+ component.lifecycle.destroy = () => {
77
+ prefixElement.remove();
78
+ originalDestroy.call(component.lifecycle);
79
+ };
80
+ }
81
+
82
+ return {
83
+ ...component,
84
+ prefixTextElement: prefixElement,
85
+
86
+ setPrefixText(text: string) {
87
+ prefixElement.textContent = text;
88
+ return this;
89
+ },
90
+
91
+ removePrefixText() {
92
+ if (prefixElement.parentNode) {
93
+ prefixElement.remove();
94
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-prefix`);
95
+
96
+ // Reset label position if no prefix
97
+ const labelEl = component.element.querySelector(`.${PREFIX}-${config.componentName || 'textfield'}-label`);
98
+ if (labelEl) {
99
+ (labelEl as HTMLElement).style.left = '';
100
+ }
101
+
102
+ this.prefixTextElement = null;
103
+ }
104
+ return this;
105
+ }
106
+ };
107
+ };
@@ -0,0 +1,100 @@
1
+ // src/components/textfield/features/suffix-text.ts
2
+
3
+ import { BaseComponent, ElementComponent } from '../../../core/compose/component';
4
+
5
+ /**
6
+ * Configuration for suffix text feature
7
+ */
8
+ export interface SuffixTextConfig {
9
+ /**
10
+ * Suffix text content to display after the input
11
+ */
12
+ suffixText?: string;
13
+
14
+ /**
15
+ * CSS class prefix
16
+ */
17
+ prefix?: string;
18
+
19
+ /**
20
+ * Component name
21
+ */
22
+ componentName?: string;
23
+
24
+ [key: string]: any;
25
+ }
26
+
27
+ /**
28
+ * Component with suffix text capabilities
29
+ */
30
+ export interface SuffixTextComponent extends BaseComponent {
31
+ /**
32
+ * Suffix text element
33
+ */
34
+ suffixTextElement: HTMLElement | null;
35
+
36
+ /**
37
+ * Sets suffix text content
38
+ * @param text - Text content to display after the input
39
+ * @returns Component instance for chaining
40
+ */
41
+ setSuffixText: (text: string) => SuffixTextComponent;
42
+
43
+ /**
44
+ * Removes suffix text
45
+ * @returns Component instance for chaining
46
+ */
47
+ removeSuffixText: () => SuffixTextComponent;
48
+ }
49
+
50
+ /**
51
+ * Adds suffix text to a textfield component
52
+ * @param config - Configuration with suffix text settings
53
+ * @returns Function that enhances a component with suffix text
54
+ */
55
+ export const withSuffixText = <T extends SuffixTextConfig>(config: T) =>
56
+ <C extends ElementComponent>(component: C): C & SuffixTextComponent => {
57
+ if (!config.suffixText) {
58
+ return component as any;
59
+ }
60
+
61
+ // Create suffix text element
62
+ const PREFIX = config.prefix || 'mtrl';
63
+ const suffixElement = document.createElement('span');
64
+ suffixElement.className = `${PREFIX}-${config.componentName || 'textfield'}-suffix`;
65
+ suffixElement.textContent = config.suffixText;
66
+
67
+ // Add suffix text to the component
68
+ component.element.appendChild(suffixElement);
69
+
70
+ // Add suffix class to the component
71
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--with-suffix`);
72
+
73
+ // Add lifecycle integration
74
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
75
+ const originalDestroy = component.lifecycle.destroy;
76
+ component.lifecycle.destroy = () => {
77
+ suffixElement.remove();
78
+ originalDestroy.call(component.lifecycle);
79
+ };
80
+ }
81
+
82
+ return {
83
+ ...component,
84
+ suffixTextElement: suffixElement,
85
+
86
+ setSuffixText(text: string) {
87
+ suffixElement.textContent = text;
88
+ return this;
89
+ },
90
+
91
+ removeSuffixText() {
92
+ if (suffixElement.parentNode) {
93
+ suffixElement.remove();
94
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-suffix`);
95
+ this.suffixTextElement = null;
96
+ }
97
+ return this;
98
+ }
99
+ };
100
+ };
@@ -0,0 +1,113 @@
1
+ // src/components/textfield/features/supporting-text.ts
2
+
3
+ import { BaseComponent, ElementComponent } from '../../../core/compose/component';
4
+
5
+ /**
6
+ * Configuration for supporting text feature
7
+ */
8
+ export interface SupportingTextConfig {
9
+ /**
10
+ * Supporting text content
11
+ */
12
+ supportingText?: string;
13
+
14
+ /**
15
+ * Whether supporting text indicates an error
16
+ */
17
+ error?: boolean;
18
+
19
+ /**
20
+ * CSS class prefix
21
+ */
22
+ prefix?: string;
23
+
24
+ /**
25
+ * Component name
26
+ */
27
+ componentName?: string;
28
+
29
+ [key: string]: any;
30
+ }
31
+
32
+ /**
33
+ * Component with supporting text capabilities
34
+ */
35
+ export interface SupportingTextComponent extends BaseComponent {
36
+ /**
37
+ * Supporting text element
38
+ */
39
+ supportingTextElement: HTMLElement | null;
40
+
41
+ /**
42
+ * Sets supporting text content
43
+ * @param text - Text content
44
+ * @param isError - Whether text represents an error
45
+ * @returns Component instance for chaining
46
+ */
47
+ setSupportingText: (text: string, isError?: boolean) => SupportingTextComponent;
48
+
49
+ /**
50
+ * Removes supporting text
51
+ * @returns Component instance for chaining
52
+ */
53
+ removeSupportingText: () => SupportingTextComponent;
54
+ }
55
+
56
+ /**
57
+ * Adds supporting text to a textfield component
58
+ * @param config - Configuration with supporting text settings
59
+ * @returns Function that enhances a component with supporting text
60
+ */
61
+ export const withSupportingText = <T extends SupportingTextConfig>(config: T) =>
62
+ <C extends ElementComponent>(component: C): C & SupportingTextComponent => {
63
+ if (!config.supportingText) {
64
+ return component as any;
65
+ }
66
+
67
+ // Create supporting text element
68
+ const PREFIX = config.prefix || 'mtrl';
69
+ const supportingElement = document.createElement('div');
70
+ supportingElement.className = `${PREFIX}-${config.componentName || 'textfield'}-helper`;
71
+ supportingElement.textContent = config.supportingText;
72
+
73
+ if (config.error) {
74
+ supportingElement.classList.add(`${PREFIX}-${config.componentName || 'textfield'}-helper--error`);
75
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--error`);
76
+ }
77
+
78
+ // Add supporting text to the component
79
+ component.element.appendChild(supportingElement);
80
+
81
+ // Add lifecycle integration if available
82
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
83
+ const originalDestroy = component.lifecycle.destroy;
84
+ component.lifecycle.destroy = () => {
85
+ supportingElement.remove();
86
+ originalDestroy.call(component.lifecycle);
87
+ };
88
+ }
89
+
90
+ return {
91
+ ...component,
92
+ supportingTextElement: supportingElement,
93
+
94
+ setSupportingText(text: string, isError = false) {
95
+ supportingElement.textContent = text;
96
+
97
+ // Handle error state
98
+ supportingElement.classList.toggle(`${PREFIX}-${config.componentName || 'textfield'}-helper--error`, isError);
99
+ component.element.classList.toggle(`${PREFIX}-${config.componentName || 'textfield'}--error`, isError);
100
+
101
+ return this;
102
+ },
103
+
104
+ removeSupportingText() {
105
+ if (supportingElement.parentNode) {
106
+ supportingElement.remove();
107
+ this.supportingTextElement = null;
108
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--error`);
109
+ }
110
+ return this;
111
+ }
112
+ };
113
+ };
@@ -0,0 +1,108 @@
1
+ // src/components/textfield/features/trailing-icon.ts
2
+
3
+ import { BaseComponent, ElementComponent } from '../../../core/compose/component';
4
+
5
+ /**
6
+ * Configuration for trailing icon feature
7
+ */
8
+ export interface TrailingIconConfig {
9
+ /**
10
+ * Trailing icon HTML content
11
+ */
12
+ trailingIcon?: string;
13
+
14
+ /**
15
+ * CSS class prefix
16
+ */
17
+ prefix?: string;
18
+
19
+ /**
20
+ * Component name
21
+ */
22
+ componentName?: string;
23
+
24
+ [key: string]: any;
25
+ }
26
+
27
+ /**
28
+ * Component with trailing icon capabilities
29
+ */
30
+ export interface TrailingIconComponent extends BaseComponent {
31
+ /**
32
+ * Trailing icon element
33
+ */
34
+ trailingIcon: HTMLElement | null;
35
+
36
+ /**
37
+ * Sets trailing icon content
38
+ * @param html - HTML content for the icon
39
+ * @returns Component instance for chaining
40
+ */
41
+ setTrailingIcon: (html: string) => TrailingIconComponent;
42
+
43
+ /**
44
+ * Removes trailing icon
45
+ * @returns Component instance for chaining
46
+ */
47
+ removeTrailingIcon: () => TrailingIconComponent;
48
+ }
49
+
50
+ /**
51
+ * Adds trailing icon to a textfield component
52
+ * @param config - Configuration with trailing icon settings
53
+ * @returns Function that enhances a component with trailing icon
54
+ */
55
+ export const withTrailingIcon = <T extends TrailingIconConfig>(config: T) =>
56
+ <C extends ElementComponent>(component: C): C & TrailingIconComponent => {
57
+ if (!config.trailingIcon) {
58
+ return component as any;
59
+ }
60
+
61
+ // Create icon element
62
+ const PREFIX = config.prefix || 'mtrl';
63
+ const iconElement = document.createElement('span');
64
+ iconElement.className = `${PREFIX}-${config.componentName || 'textfield'}-trailing-icon`;
65
+ iconElement.innerHTML = config.trailingIcon;
66
+
67
+ // Add trailing icon to the component
68
+ component.element.appendChild(iconElement);
69
+
70
+ // Add trailing-icon class to the component
71
+ component.element.classList.add(`${PREFIX}-${config.componentName || 'textfield'}--with-trailing-icon`);
72
+
73
+ // When there's a trailing icon, adjust input padding
74
+ if (component.input) {
75
+ component.input.classList.add(`${PREFIX}-${config.componentName || 'textfield'}-input--with-trailing-icon`);
76
+ }
77
+
78
+ // Add lifecycle integration if available
79
+ if ('lifecycle' in component && component.lifecycle?.destroy) {
80
+ const originalDestroy = component.lifecycle.destroy;
81
+ component.lifecycle.destroy = () => {
82
+ iconElement.remove();
83
+ originalDestroy.call(component.lifecycle);
84
+ };
85
+ }
86
+
87
+ return {
88
+ ...component,
89
+ trailingIcon: iconElement,
90
+
91
+ setTrailingIcon(html: string) {
92
+ iconElement.innerHTML = html;
93
+ return this;
94
+ },
95
+
96
+ removeTrailingIcon() {
97
+ if (iconElement.parentNode) {
98
+ iconElement.remove();
99
+ component.element.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}--with-trailing-icon`);
100
+ if (component.input) {
101
+ component.input.classList.remove(`${PREFIX}-${config.componentName || 'textfield'}-input--with-trailing-icon`);
102
+ }
103
+ this.trailingIcon = null;
104
+ }
105
+ return this;
106
+ }
107
+ };
108
+ };
@@ -13,7 +13,10 @@ import { withAPI } from './api';
13
13
  import {
14
14
  withLeadingIcon,
15
15
  withTrailingIcon,
16
- withSupportingText
16
+ withSupportingText,
17
+ withPrefixText,
18
+ withSuffixText,
19
+ withPlacement
17
20
  } from './features';
18
21
  import { TextfieldConfig, TextfieldComponent } from './types';
19
22
  import {
@@ -24,26 +27,59 @@ import {
24
27
 
25
28
  /**
26
29
  * Creates a new Textfield component
27
- * @param {TextfieldConfig} config - Textfield configuration
28
- * @returns {TextfieldComponent} Textfield component instance
30
+ *
31
+ * Textfields allow users to enter text into a UI. They typically appear in forms and dialogs.
32
+ * This implementation follows Material Design 3 guidelines for accessible, customizable textfields.
33
+ *
34
+ * @param {TextfieldConfig} config - Textfield configuration options
35
+ * @returns {TextfieldComponent} A fully configured textfield component instance
36
+ * @throws {Error} Throws an error if textfield creation fails
37
+ *
38
+ * @example
39
+ * // Create a basic text field
40
+ * const textfield = createTextfield({
41
+ * label: 'Username',
42
+ * name: 'username'
43
+ * });
44
+ *
45
+ * document.querySelector('.form').appendChild(textfield.element);
46
+ *
47
+ * @example
48
+ * // Create a text field with prefix and suffix
49
+ * const currencyField = createTextfield({
50
+ * label: 'Amount',
51
+ * type: 'number',
52
+ * prefixText: '$',
53
+ * suffixText: 'USD'
54
+ * });
55
+ *
56
+ * // Add event listener
57
+ * currencyField.on('input', (e) => {
58
+ * console.log('Amount entered:', e.target.value);
59
+ * });
29
60
  */
30
61
  const createTextfield = (config: TextfieldConfig = {}): TextfieldComponent => {
31
62
  const baseConfig = createBaseConfig(config);
32
63
 
33
64
  try {
65
+ // Build textfield through functional composition
66
+ // Each function in the pipe adds specific capabilities
34
67
  const textfield = pipe(
35
- createBase,
36
- withEvents(),
37
- withElement(getElementConfig(baseConfig)),
38
- withVariant(baseConfig),
39
- withTextInput(baseConfig),
40
- withTextLabel(baseConfig),
41
- withLeadingIcon(baseConfig),
42
- withTrailingIcon(baseConfig),
43
- withSupportingText(baseConfig),
44
- withDisabled(baseConfig),
45
- withLifecycle(),
46
- comp => withAPI(getApiConfig(comp))(comp)
68
+ createBase, // Base component structure
69
+ withEvents(), // Event handling system
70
+ withElement(getElementConfig(baseConfig)), // Create DOM element
71
+ withVariant(baseConfig), // Apply variant styling (filled/outlined)
72
+ withTextInput(baseConfig), // Add input element
73
+ withTextLabel(baseConfig), // Add text label
74
+ withLeadingIcon(baseConfig), // Add leading icon (if specified)
75
+ withTrailingIcon(baseConfig), // Add trailing icon (if specified)
76
+ withPrefixText(baseConfig), // Add prefix text (if specified)
77
+ withSuffixText(baseConfig), // Add suffix text (if specified)
78
+ withSupportingText(baseConfig), // Add supporting/helper text (if specified)
79
+ withDisabled(baseConfig), // Add disabled state management
80
+ withLifecycle(), // Add lifecycle management
81
+ withPlacement(), // Add dynamic positioning for elements
82
+ comp => withAPI(getApiConfig(comp))(comp) // Add public API
47
83
  )(baseConfig);
48
84
 
49
85
  return textfield as TextfieldComponent;