mtrl 0.2.6 → 0.2.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 (147) hide show
  1. package/index.ts +18 -0
  2. package/package.json +1 -1
  3. package/src/components/badge/_styles.scss +117 -109
  4. package/src/components/badge/api.ts +57 -59
  5. package/src/components/badge/badge.ts +16 -2
  6. package/src/components/badge/config.ts +65 -11
  7. package/src/components/badge/constants.ts +22 -12
  8. package/src/components/badge/features.ts +44 -40
  9. package/src/components/badge/types.ts +42 -30
  10. package/src/components/bottom-app-bar/_styles.scss +103 -0
  11. package/src/components/bottom-app-bar/bottom-app-bar.ts +196 -0
  12. package/src/components/bottom-app-bar/config.ts +73 -0
  13. package/src/components/bottom-app-bar/index.ts +11 -0
  14. package/src/components/bottom-app-bar/types.ts +108 -0
  15. package/src/components/button/_styles.scss +0 -10
  16. package/src/components/button/api.ts +5 -0
  17. package/src/components/button/config.ts +5 -0
  18. package/src/components/button/types.ts +6 -0
  19. package/src/components/card/card.ts +13 -25
  20. package/src/components/card/config.ts +67 -22
  21. package/src/components/card/features.ts +3 -0
  22. package/src/components/card/types.ts +28 -0
  23. package/src/components/checkbox/_styles.scss +0 -2
  24. package/src/components/datepicker/_styles.scss +358 -0
  25. package/src/components/datepicker/api.ts +272 -0
  26. package/src/components/datepicker/config.ts +144 -0
  27. package/src/components/datepicker/constants.ts +98 -0
  28. package/src/components/datepicker/datepicker.ts +346 -0
  29. package/src/components/datepicker/index.ts +9 -0
  30. package/src/components/datepicker/render.ts +452 -0
  31. package/src/components/datepicker/types.ts +268 -0
  32. package/src/components/datepicker/utils.ts +290 -0
  33. package/src/components/dialog/_styles.scss +174 -128
  34. package/src/components/dialog/api.ts +48 -13
  35. package/src/components/dialog/config.ts +9 -5
  36. package/src/components/dialog/dialog.ts +6 -3
  37. package/src/components/dialog/features.ts +290 -130
  38. package/src/components/dialog/types.ts +7 -4
  39. package/src/components/divider/_styles.scss +57 -0
  40. package/src/components/divider/config.ts +81 -0
  41. package/src/components/divider/divider.ts +37 -0
  42. package/src/components/divider/features.ts +207 -0
  43. package/src/components/divider/index.ts +5 -0
  44. package/src/components/divider/types.ts +55 -0
  45. package/src/components/extended-fab/_styles.scss +267 -0
  46. package/src/components/extended-fab/api.ts +141 -0
  47. package/src/components/extended-fab/config.ts +108 -0
  48. package/src/components/extended-fab/constants.ts +36 -0
  49. package/src/components/extended-fab/extended-fab.ts +125 -0
  50. package/src/components/extended-fab/index.ts +4 -0
  51. package/src/components/extended-fab/types.ts +287 -0
  52. package/src/components/fab/_styles.scss +225 -0
  53. package/src/components/fab/api.ts +97 -0
  54. package/src/components/fab/config.ts +94 -0
  55. package/src/components/fab/constants.ts +41 -0
  56. package/src/components/fab/fab.ts +67 -0
  57. package/src/components/fab/index.ts +4 -0
  58. package/src/components/fab/types.ts +234 -0
  59. package/src/components/navigation/_styles.scss +1 -0
  60. package/src/components/navigation/api.ts +78 -50
  61. package/src/components/navigation/features/items.ts +280 -0
  62. package/src/components/navigation/nav-item.ts +72 -23
  63. package/src/components/navigation/navigation.ts +54 -2
  64. package/src/components/navigation/types.ts +210 -188
  65. package/src/components/search/_styles.scss +306 -0
  66. package/src/components/search/api.ts +203 -0
  67. package/src/components/search/config.ts +87 -0
  68. package/src/components/search/constants.ts +21 -0
  69. package/src/components/search/features/index.ts +4 -0
  70. package/src/components/search/features/search.ts +718 -0
  71. package/src/components/search/features/states.ts +165 -0
  72. package/src/components/search/features/structure.ts +198 -0
  73. package/src/components/search/index.ts +10 -0
  74. package/src/components/search/search.ts +52 -0
  75. package/src/components/search/types.ts +163 -0
  76. package/src/components/segmented-button/_styles.scss +117 -0
  77. package/src/components/segmented-button/config.ts +67 -0
  78. package/src/components/segmented-button/constants.ts +42 -0
  79. package/src/components/segmented-button/index.ts +4 -0
  80. package/src/components/segmented-button/segment.ts +155 -0
  81. package/src/components/segmented-button/segmented-button.ts +250 -0
  82. package/src/components/segmented-button/types.ts +219 -0
  83. package/src/components/slider/_styles.scss +83 -24
  84. package/src/components/slider/accessibility.md +5 -5
  85. package/src/components/slider/api.ts +41 -120
  86. package/src/components/slider/config.ts +51 -47
  87. package/src/components/slider/features/handlers.ts +495 -0
  88. package/src/components/slider/features/index.ts +1 -2
  89. package/src/components/slider/features/slider.ts +66 -84
  90. package/src/components/slider/features/states.ts +195 -0
  91. package/src/components/slider/features/structure.ts +136 -206
  92. package/src/components/slider/features/ui.ts +145 -206
  93. package/src/components/slider/index.ts +2 -11
  94. package/src/components/slider/slider.ts +9 -12
  95. package/src/components/slider/types.ts +39 -24
  96. package/src/components/switch/_styles.scss +0 -2
  97. package/src/components/tabs/_styles.scss +94 -32
  98. package/src/components/tabs/features.ts +4 -2
  99. package/src/components/tabs/indicator.ts +73 -13
  100. package/src/components/tabs/types.ts +10 -2
  101. package/src/components/timepicker/README.md +277 -0
  102. package/src/components/timepicker/_styles.scss +451 -0
  103. package/src/components/timepicker/api.ts +632 -0
  104. package/src/components/timepicker/clockdial.ts +482 -0
  105. package/src/components/timepicker/config.ts +130 -0
  106. package/src/components/timepicker/constants.ts +138 -0
  107. package/src/components/timepicker/index.ts +8 -0
  108. package/src/components/timepicker/render.ts +613 -0
  109. package/src/components/timepicker/timepicker.ts +117 -0
  110. package/src/components/timepicker/types.ts +336 -0
  111. package/src/components/timepicker/utils.ts +241 -0
  112. package/src/components/top-app-bar/_styles.scss +225 -0
  113. package/src/components/top-app-bar/config.ts +83 -0
  114. package/src/components/top-app-bar/index.ts +11 -0
  115. package/src/components/top-app-bar/top-app-bar.ts +316 -0
  116. package/src/components/top-app-bar/types.ts +140 -0
  117. package/src/core/build/_ripple.scss +6 -6
  118. package/src/core/build/ripple.ts +72 -95
  119. package/src/core/compose/features/icon.ts +3 -1
  120. package/src/core/compose/features/ripple.ts +4 -1
  121. package/src/core/compose/features/textlabel.ts +26 -2
  122. package/src/core/dom/create.ts +5 -0
  123. package/src/index.ts +9 -0
  124. package/src/styles/abstract/_theme.scss +9 -1
  125. package/src/styles/themes/_autumn.scss +21 -0
  126. package/src/styles/themes/_base-theme.scss +61 -0
  127. package/src/styles/themes/_baseline.scss +58 -0
  128. package/src/styles/themes/_bluekhaki.scss +125 -0
  129. package/src/styles/themes/_brownbeige.scss +125 -0
  130. package/src/styles/themes/_browngreen.scss +125 -0
  131. package/src/styles/themes/_forest.scss +6 -0
  132. package/src/styles/themes/_greenbeige.scss +125 -0
  133. package/src/styles/themes/_material.scss +125 -0
  134. package/src/styles/themes/_ocean.scss +6 -0
  135. package/src/styles/themes/_sageivory.scss +125 -0
  136. package/src/styles/themes/_spring.scss +6 -0
  137. package/src/styles/themes/_summer.scss +5 -0
  138. package/src/styles/themes/_sunset.scss +5 -0
  139. package/src/styles/themes/_tealcaramel.scss +125 -0
  140. package/src/styles/themes/_winter.scss +6 -0
  141. package/src/components/navigation/features/items.js +0 -192
  142. package/src/components/slider/features/appearance.ts +0 -94
  143. package/src/components/slider/features/disabled.ts +0 -68
  144. package/src/components/slider/features/events.ts +0 -164
  145. package/src/components/slider/features/interactions.ts +0 -396
  146. package/src/components/slider/features/keyboard.ts +0 -233
  147. package/src/core/collection/adapters/mongodb.js +0 -232
@@ -0,0 +1,165 @@
1
+ // src/components/search/features/states.ts
2
+ import { SEARCH_VARIANTS } from '../constants';
3
+ import { SearchConfig } from '../types';
4
+
5
+ /**
6
+ * Add state management features to search component
7
+ *
8
+ * @param config Search configuration
9
+ * @returns Component enhancer with state management features
10
+ */
11
+ export const withStates = (config: SearchConfig) => component => {
12
+ // Track initial disabled state
13
+ const isDisabled = config.disabled === true;
14
+
15
+ // Apply initial disabled state if needed
16
+ if (isDisabled) {
17
+ setTimeout(() => {
18
+ disableComponent();
19
+ }, 0);
20
+ }
21
+
22
+ /**
23
+ * Disables the component
24
+ */
25
+ function disableComponent() {
26
+ component.element.classList.add(`${component.getClass('search')}--disabled`);
27
+ component.element.setAttribute('aria-disabled', 'true');
28
+
29
+ // Disable input - important to use the disabled property not the attribute
30
+ if (component.structure?.input) {
31
+ component.structure.input.disabled = true;
32
+ }
33
+
34
+ // Ensure buttons cannot receive focus when disabled
35
+ const buttons = [
36
+ component.structure?.leadingIcon,
37
+ component.structure?.clearButton,
38
+ component.structure?.trailingIcon,
39
+ component.structure?.trailingIcon2
40
+ ].filter(Boolean);
41
+
42
+ buttons.forEach(button => {
43
+ button.tabIndex = -1;
44
+ button.setAttribute('aria-disabled', 'true');
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Enables the component
50
+ */
51
+ function enableComponent() {
52
+ component.element.classList.remove(`${component.getClass('search')}--disabled`);
53
+ component.element.setAttribute('aria-disabled', 'false');
54
+
55
+ // Enable input - important to use the disabled property not the attribute
56
+ if (component.structure?.input) {
57
+ component.structure.input.disabled = false;
58
+ }
59
+
60
+ // Re-enable buttons
61
+ const buttons = [
62
+ component.structure?.leadingIcon,
63
+ component.structure?.clearButton,
64
+ component.structure?.trailingIcon,
65
+ component.structure?.trailingIcon2
66
+ ].filter(Boolean);
67
+
68
+ buttons.forEach(button => {
69
+ button.tabIndex = 0;
70
+ button.setAttribute('aria-disabled', 'false');
71
+ });
72
+
73
+ // Clear button special case - only enable if there's text
74
+ if (component.structure?.clearButton &&
75
+ component.structure.input &&
76
+ !component.structure.input.value) {
77
+ component.structure.clearButton.tabIndex = -1;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Gets the active variant
83
+ */
84
+ function getActiveVariant() {
85
+ return Object.values(SEARCH_VARIANTS).find(variantName =>
86
+ component.element.classList.contains(`${component.getClass('search')}--${variantName}`)
87
+ ) || SEARCH_VARIANTS.BAR;
88
+ }
89
+
90
+ // Return enhanced component
91
+ return {
92
+ ...component,
93
+
94
+ // Disabled state management
95
+ disabled: {
96
+ /**
97
+ * Enables the component
98
+ * @returns Disabled manager for chaining
99
+ */
100
+ enable() {
101
+ enableComponent();
102
+ return this;
103
+ },
104
+
105
+ /**
106
+ * Disables the component
107
+ * @returns Disabled manager for chaining
108
+ */
109
+ disable() {
110
+ disableComponent();
111
+ return this;
112
+ },
113
+
114
+ /**
115
+ * Checks if component is disabled
116
+ * @returns True if disabled
117
+ */
118
+ isDisabled() {
119
+ return component.element.classList.contains(`${component.getClass('search')}--disabled`);
120
+ }
121
+ },
122
+
123
+ // Appearance management
124
+ appearance: {
125
+ /**
126
+ * Sets search variant
127
+ * @param variant Variant name
128
+ * @returns Appearance manager for chaining
129
+ */
130
+ setVariant(variant) {
131
+ const currentVariant = getActiveVariant();
132
+
133
+ // If already this variant, do nothing
134
+ if (currentVariant === variant) {
135
+ return this;
136
+ }
137
+
138
+ // Remove existing variant classes
139
+ Object.values(SEARCH_VARIANTS).forEach(variantName => {
140
+ component.element.classList.remove(`${component.getClass('search')}--${variantName}`);
141
+ });
142
+
143
+ // Add new variant class
144
+ component.element.classList.add(`${component.getClass('search')}--${variant}`);
145
+
146
+ // Toggle expanded state class
147
+ if (variant === SEARCH_VARIANTS.VIEW) {
148
+ component.element.classList.add(`${component.getClass('search')}--expanded`);
149
+ } else {
150
+ component.element.classList.remove(`${component.getClass('search')}--expanded`);
151
+ }
152
+
153
+ return this;
154
+ },
155
+
156
+ /**
157
+ * Gets search variant
158
+ * @returns Current variant name
159
+ */
160
+ getVariant() {
161
+ return getActiveVariant();
162
+ }
163
+ }
164
+ };
165
+ };
@@ -0,0 +1,198 @@
1
+ // src/components/search/features/structure.ts
2
+ import { SEARCH_VARIANTS } from '../constants';
3
+ import { SearchConfig } from '../types';
4
+ import { createElement } from '../../../core/dom/create';
5
+
6
+ /**
7
+ * Creates the search component DOM structure
8
+ * @param config Search configuration
9
+ * @returns Component enhancer with DOM structure
10
+ */
11
+ export const withStructure = (config: SearchConfig) => component => {
12
+ // Get initial config values
13
+ const isDisabled = config.disabled === true;
14
+ const variant = config.variant || SEARCH_VARIANTS.BAR;
15
+ const isViewMode = variant === SEARCH_VARIANTS.VIEW;
16
+ const placeholder = config.placeholder || 'Search';
17
+ const value = config.value || '';
18
+
19
+ // Get prefixed class names
20
+ const getClass = (className) => component.getClass(className);
21
+
22
+ // Build attributes object for the input
23
+ const inputAttrs = {
24
+ 'type': 'text',
25
+ 'placeholder': placeholder,
26
+ 'value': value,
27
+ 'aria-label': placeholder
28
+ };
29
+
30
+ // Only add disabled attribute if the input should be disabled
31
+ if (isDisabled) {
32
+ inputAttrs['disabled'] = 'disabled';
33
+ }
34
+
35
+ // Create container element
36
+ const container = createElement({
37
+ tag: 'div',
38
+ className: getClass('search-container'),
39
+ container: component.element,
40
+ attrs: {
41
+ style: `
42
+ ${config.minWidth ? `min-width: ${config.minWidth}px;` : ''}
43
+ ${config.maxWidth ? `max-width: ${config.maxWidth}px;` : ''}
44
+ ${config.fullWidth ? 'width: 100%;' : ''}
45
+ `
46
+ }
47
+ });
48
+
49
+ // Create leading icon
50
+ const leadingIcon = createElement({
51
+ tag: 'div',
52
+ className: getClass('search-leading-icon'),
53
+ container: container,
54
+ html: config.leadingIcon || '',
55
+ attrs: {
56
+ 'role': 'button',
57
+ 'tabindex': isDisabled ? '-1' : '0',
58
+ 'aria-label': 'Search'
59
+ }
60
+ });
61
+
62
+ // Create input wrapper
63
+ const inputWrapper = createElement({
64
+ tag: 'div',
65
+ className: getClass('search-input-wrapper'),
66
+ container: container
67
+ });
68
+
69
+ // Create input element with properly handled disabled state
70
+ const input = createElement({
71
+ tag: 'input',
72
+ className: getClass('search-input'),
73
+ container: inputWrapper,
74
+ attrs: inputAttrs
75
+ });
76
+
77
+ // Create clear button
78
+ const clearButton = createElement({
79
+ tag: 'div',
80
+ className: [
81
+ getClass('search-clear-button'),
82
+ value ? '' : getClass('search-clear-button--hidden')
83
+ ],
84
+ container: container,
85
+ html: '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>',
86
+ attrs: {
87
+ 'role': 'button',
88
+ 'tabindex': isDisabled || !value ? '-1' : '0',
89
+ 'aria-label': 'Clear search'
90
+ }
91
+ });
92
+
93
+ // Create optional trailing elements
94
+ let trailingIcon, trailingIcon2, avatar;
95
+
96
+ if (config.trailingIcon) {
97
+ trailingIcon = createElement({
98
+ tag: 'div',
99
+ className: getClass('search-trailing-icon'),
100
+ container: container,
101
+ html: config.trailingIcon,
102
+ attrs: {
103
+ 'role': 'button',
104
+ 'tabindex': isDisabled ? '-1' : '0',
105
+ 'aria-label': 'Search option'
106
+ }
107
+ });
108
+ }
109
+
110
+ if (config.trailingIcon2) {
111
+ trailingIcon2 = createElement({
112
+ tag: 'div',
113
+ className: getClass('search-trailing-icon'),
114
+ container: container,
115
+ html: config.trailingIcon2,
116
+ attrs: {
117
+ 'role': 'button',
118
+ 'tabindex': isDisabled ? '-1' : '0',
119
+ 'aria-label': 'Search option'
120
+ }
121
+ });
122
+ }
123
+
124
+ if (config.avatar) {
125
+ avatar = createElement({
126
+ tag: 'div',
127
+ className: getClass('search-avatar'),
128
+ container: container,
129
+ html: config.avatar
130
+ });
131
+ }
132
+
133
+ // Create divider and suggestions container for view variant
134
+ let divider, suggestionsContainer;
135
+
136
+ if (isViewMode || config.suggestions) {
137
+ divider = createElement({
138
+ tag: 'div',
139
+ className: getClass('search-divider')
140
+ });
141
+
142
+ suggestionsContainer = createElement({
143
+ tag: 'div',
144
+ className: getClass('search-suggestions-container'),
145
+ container: component.element
146
+ });
147
+ }
148
+
149
+ // Add component base class and accessibility attributes
150
+ component.element.classList.add(component.getClass('search'));
151
+ component.element.setAttribute('role', 'search');
152
+ component.element.setAttribute('aria-disabled', isDisabled ? 'true' : 'false');
153
+
154
+ // Apply style classes
155
+ applyStyleClasses(component, config, isViewMode, isDisabled);
156
+
157
+ // Return enhanced component with structure
158
+ return {
159
+ ...component,
160
+ structure: {
161
+ container,
162
+ input,
163
+ inputWrapper,
164
+ leadingIcon,
165
+ clearButton,
166
+ trailingIcon,
167
+ trailingIcon2,
168
+ avatar,
169
+ divider,
170
+ suggestionsContainer
171
+ }
172
+ };
173
+ };
174
+
175
+ /**
176
+ * Applies style classes based on configuration
177
+ */
178
+ function applyStyleClasses(component, config, isViewMode, isDisabled) {
179
+ const baseClass = component.getClass('search');
180
+
181
+ // Apply variant class
182
+ component.element.classList.add(`${baseClass}--${config.variant || SEARCH_VARIANTS.BAR}`);
183
+
184
+ // Apply disabled class if needed
185
+ if (isDisabled) {
186
+ component.element.classList.add(`${baseClass}--disabled`);
187
+ }
188
+
189
+ // Apply expanded class for view mode
190
+ if (isViewMode) {
191
+ component.element.classList.add(`${baseClass}--expanded`);
192
+ }
193
+
194
+ // Apply fullwidth class if needed
195
+ if (config.fullWidth) {
196
+ component.element.classList.add(`${baseClass}--fullwidth`);
197
+ }
198
+ }
@@ -0,0 +1,10 @@
1
+ // src/components/search/index.ts
2
+
3
+ // Export main component creator
4
+ export { default } from './search';
5
+
6
+ // Export constants
7
+ export { SEARCH_VARIANTS, SEARCH_EVENTS } from './constants';
8
+
9
+ // Export types for TypeScript users
10
+ export type { SearchConfig, SearchComponent, SearchEvent } from './types';
@@ -0,0 +1,52 @@
1
+ // src/components/search/search.ts
2
+ import { pipe } from '../../core/compose/pipe';
3
+ import { createBase, withElement } from '../../core/compose/component';
4
+ import { withEvents, withLifecycle } from '../../core/compose/features';
5
+ import { withStructure, withSearch, withStates } from './features';
6
+ import { withAPI } from './api';
7
+ import { SearchConfig, SearchComponent } from './types';
8
+ import { createBaseConfig, getElementConfig, getApiConfig } from './config';
9
+
10
+ /**
11
+ * Creates a new Search component
12
+ * @param {SearchConfig} config - Search configuration object
13
+ * @returns {SearchComponent} Search component instance
14
+ */
15
+ const createSearch = (config: SearchConfig = {}): SearchComponent => {
16
+ const baseConfig = createBaseConfig(config);
17
+
18
+ try {
19
+ // Create the component with all required features
20
+ const component = pipe(
21
+ createBase,
22
+ withEvents(),
23
+ withElement(getElementConfig(baseConfig)),
24
+ withStructure(baseConfig),
25
+ withStates(baseConfig),
26
+ withSearch(baseConfig),
27
+ withLifecycle()
28
+ )(baseConfig);
29
+
30
+ // Generate the API configuration
31
+ const apiOptions = getApiConfig(component);
32
+
33
+ // Apply the API layer
34
+ const search = withAPI(apiOptions)(component);
35
+
36
+ // Register event handlers from config
37
+ if (baseConfig.on && typeof search.on === 'function') {
38
+ Object.entries(baseConfig.on).forEach(([event, handler]) => {
39
+ if (typeof handler === 'function') {
40
+ search.on(event, handler);
41
+ }
42
+ });
43
+ }
44
+
45
+ return search;
46
+ } catch (error) {
47
+ console.error('Search creation error:', error);
48
+ throw new Error(`Failed to create search: ${(error as Error).message}`);
49
+ }
50
+ };
51
+
52
+ export default createSearch;
@@ -0,0 +1,163 @@
1
+ // src/components/search/types.ts
2
+ import { SEARCH_VARIANTS, SEARCH_EVENTS } from './constants';
3
+
4
+ /**
5
+ * Configuration options for the search component
6
+ */
7
+ export interface SearchConfig {
8
+ /** The variant of the search component (bar or view) */
9
+ variant?: keyof typeof SEARCH_VARIANTS | typeof SEARCH_VARIANTS[keyof typeof SEARCH_VARIANTS];
10
+
11
+ /** Whether the search component is disabled */
12
+ disabled?: boolean;
13
+
14
+ /** Placeholder text for the search input */
15
+ placeholder?: string;
16
+
17
+ /** Initial value for the search input */
18
+ value?: string;
19
+
20
+ /** Leading icon HTML or 'none' to remove */
21
+ leadingIcon?: string;
22
+
23
+ /** Trailing icon HTML (optional) */
24
+ trailingIcon?: string;
25
+
26
+ /** Second trailing icon HTML (optional) */
27
+ trailingIcon2?: string;
28
+
29
+ /** Avatar HTML for trailing avatar (optional) */
30
+ avatar?: string;
31
+
32
+ /** If true, will show the clear button when the input has text */
33
+ showClearButton?: boolean;
34
+
35
+ /** Callback function when a search is submitted */
36
+ onSubmit?: (value: string) => void;
37
+
38
+ /** Callback function when the input value changes */
39
+ onInput?: (value: string) => void;
40
+
41
+ /** Callback function when the clear button is clicked */
42
+ onClear?: () => void;
43
+
44
+ /** Suggestions to show when the search is focused (array of strings or objects) */
45
+ suggestions?: string[] | Array<{text: string, value?: string, icon?: string}>;
46
+
47
+ /** Whether to show dividers between suggestion groups */
48
+ showDividers?: boolean;
49
+
50
+ /** Additional CSS classes */
51
+ class?: string;
52
+
53
+ /** Maximum width of the search component in pixels */
54
+ maxWidth?: number;
55
+
56
+ /** Minimum width of the search component in pixels */
57
+ minWidth?: number;
58
+
59
+ /** Whether the search component is full-width */
60
+ fullWidth?: boolean;
61
+
62
+ /** Event handlers for search events */
63
+ on?: {
64
+ [key in keyof typeof SEARCH_EVENTS | typeof SEARCH_EVENTS[keyof typeof SEARCH_EVENTS]]?: (event: SearchEvent) => void;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Search event data
70
+ */
71
+ export interface SearchEvent {
72
+ /** The search component that triggered the event */
73
+ search: any;
74
+
75
+ /** Current search value */
76
+ value: string;
77
+
78
+ /** Original DOM event if available */
79
+ originalEvent: Event | null;
80
+
81
+ /** Function to prevent default behavior */
82
+ preventDefault: () => void;
83
+
84
+ /** Whether default behavior was prevented */
85
+ defaultPrevented: boolean;
86
+ }
87
+
88
+ /**
89
+ * Search component public API interface
90
+ */
91
+ export interface SearchComponent {
92
+ /** The root element of the search component */
93
+ element: HTMLElement;
94
+
95
+ /** Sets the search value */
96
+ setValue: (value: string, triggerEvent?: boolean) => SearchComponent;
97
+
98
+ /** Gets the search value */
99
+ getValue: () => string;
100
+
101
+ /** Sets placeholder text */
102
+ setPlaceholder: (text: string) => SearchComponent;
103
+
104
+ /** Gets placeholder text */
105
+ getPlaceholder: () => string;
106
+
107
+ /** Sets leading icon */
108
+ setLeadingIcon: (iconHtml: string) => SearchComponent;
109
+
110
+ /** Sets trailing icon */
111
+ setTrailingIcon: (iconHtml: string) => SearchComponent;
112
+
113
+ /** Sets second trailing icon */
114
+ setTrailingIcon2: (iconHtml: string) => SearchComponent;
115
+
116
+ /** Sets avatar */
117
+ setAvatar: (avatarHtml: string) => SearchComponent;
118
+
119
+ /** Shows or hides clear button */
120
+ showClearButton: (show: boolean) => SearchComponent;
121
+
122
+ /** Updates suggestions */
123
+ setSuggestions: (suggestions: string[] | Array<{text: string, value?: string, icon?: string}>) => SearchComponent;
124
+
125
+ /** Shows or hides suggestions */
126
+ showSuggestions: (show: boolean) => SearchComponent;
127
+
128
+ /** Focuses the search input */
129
+ focus: () => SearchComponent;
130
+
131
+ /** Blurs the search input */
132
+ blur: () => SearchComponent;
133
+
134
+ /** Expands the search bar into view mode (if in bar mode) */
135
+ expand: () => SearchComponent;
136
+
137
+ /** Collapses the search view back to bar mode */
138
+ collapse: () => SearchComponent;
139
+
140
+ /** Clears the search input */
141
+ clear: () => SearchComponent;
142
+
143
+ /** Submits the search */
144
+ submit: () => SearchComponent;
145
+
146
+ /** Enables the search component */
147
+ enable: () => SearchComponent;
148
+
149
+ /** Disables the search component */
150
+ disable: () => SearchComponent;
151
+
152
+ /** Checks if search is disabled */
153
+ isDisabled: () => boolean;
154
+
155
+ /** Adds event listener */
156
+ on: (event: keyof typeof SEARCH_EVENTS | typeof SEARCH_EVENTS[keyof typeof SEARCH_EVENTS], handler: (event: SearchEvent) => void) => SearchComponent;
157
+
158
+ /** Removes event listener */
159
+ off: (event: keyof typeof SEARCH_EVENTS | typeof SEARCH_EVENTS[keyof typeof SEARCH_EVENTS], handler: (event: SearchEvent) => void) => SearchComponent;
160
+
161
+ /** Destroys the search component and cleans up resources */
162
+ destroy: () => void;
163
+ }
@@ -0,0 +1,117 @@
1
+ // src/components/segmented-button/_styles.scss
2
+ @use '../../styles/abstract/base' as base;
3
+ @use '../../styles/abstract/variables' as v;
4
+ @use '../../styles/abstract/functions' as f;
5
+ @use '../../styles/abstract/mixins' as m;
6
+ @use '../../styles/abstract/theme' as t;
7
+
8
+ $component: '#{base.$prefix}-segmented-button';
9
+
10
+ .#{$component} {
11
+ // Base styles
12
+ position: relative;
13
+ display: inline-flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ height: 40px;
17
+ border-radius: v.shape('full');
18
+ border: 1px solid t.color('outline');
19
+ background-color: transparent;
20
+ overflow: hidden;
21
+
22
+ // Disabled state
23
+ &--disabled {
24
+ opacity: 0.38;
25
+ pointer-events: none;
26
+ }
27
+
28
+ // Segment container
29
+ &-segment {
30
+ // Base styles
31
+ position: relative;
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ height: 100%;
36
+ min-width: 48px;
37
+ padding: 0 12px;
38
+ border: none;
39
+ background-color: transparent;
40
+ color: t.color('on-surface');
41
+ cursor: pointer;
42
+ user-select: none;
43
+
44
+ // Fix segmented borders
45
+ &:not(:first-child) {
46
+ border-left: 1px solid t.color('outline');
47
+ }
48
+
49
+ // Typography
50
+ @include m.typography('label-large');
51
+
52
+ // Transition
53
+ @include m.motion-transition(
54
+ background-color,
55
+ color
56
+ );
57
+
58
+ // States
59
+ &:focus {
60
+ outline: none;
61
+ }
62
+
63
+ &:focus-visible {
64
+ outline: 2px solid t.color('outline');
65
+ outline-offset: -2px;
66
+ }
67
+
68
+ &:hover:not(.#{$component}-segment--disabled) {
69
+ background-color: t.alpha('on-surface', 0.08);
70
+ }
71
+
72
+ // Selected state
73
+ &--selected {
74
+ background-color: t.color('secondary-container');
75
+ color: t.color('on-secondary-container');
76
+
77
+ &:hover:not(.#{$component}-segment--disabled) {
78
+ background-color: t.alpha('secondary-container', 0.8);
79
+ }
80
+ }
81
+
82
+ // Disabled state
83
+ &--disabled {
84
+ opacity: 0.38;
85
+ cursor: not-allowed;
86
+ }
87
+
88
+ // Text element
89
+ &-text {
90
+ // For when both icon and text are present
91
+ margin: 0 auto;
92
+ }
93
+
94
+ // Icon styles
95
+ &-icon, &-checkmark {
96
+ display: inline-flex;
97
+ align-items: center;
98
+ justify-content: center;
99
+ width: 18px;
100
+ height: 18px;
101
+
102
+ svg {
103
+ width: 18px;
104
+ height: 18px;
105
+ }
106
+
107
+ + .#{$component}-segment-text {
108
+ margin-left: 8px;
109
+ }
110
+ }
111
+
112
+ // Space the checkmark icon
113
+ &-checkmark + .#{$component}-segment-text {
114
+ margin-left: 8px;
115
+ }
116
+ }
117
+ }