mtrl 0.3.3 → 0.3.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.
- package/package.json +1 -1
- package/src/components/menu/api.ts +143 -268
- package/src/components/menu/config.ts +84 -40
- package/src/components/menu/features/anchor.ts +159 -0
- package/src/components/menu/features/controller.ts +970 -0
- package/src/components/menu/features/index.ts +4 -0
- package/src/components/menu/index.ts +31 -63
- package/src/components/menu/menu.ts +107 -97
- package/src/components/menu/types.ts +263 -447
- package/src/components/segmented-button/config.ts +59 -20
- package/src/components/segmented-button/index.ts +1 -1
- package/src/components/segmented-button/segment.ts +51 -97
- package/src/components/segmented-button/segmented-button.ts +114 -2
- package/src/components/segmented-button/types.ts +52 -0
- package/src/core/compose/features/icon.ts +15 -13
- package/src/core/dom/classes.ts +81 -9
- package/src/core/dom/create.ts +30 -19
- package/src/core/layout/README.md +531 -166
- package/src/core/layout/array.ts +3 -4
- package/src/core/layout/config.ts +193 -0
- package/src/core/layout/create.ts +1 -2
- package/src/core/layout/index.ts +12 -2
- package/src/core/layout/object.ts +2 -3
- package/src/core/layout/processor.ts +60 -12
- package/src/core/layout/result.ts +1 -2
- package/src/core/layout/types.ts +105 -50
- package/src/core/layout/utils.ts +69 -61
- package/src/index.ts +2 -1
- package/src/styles/components/_button.scss +6 -0
- package/src/styles/components/_chip.scss +4 -5
- package/src/styles/components/_menu.scss +20 -8
- package/src/styles/components/_segmented-button.scss +173 -63
- package/src/styles/main.scss +23 -23
- package/src/styles/utilities/_layout.scss +665 -0
- package/src/components/menu/features/items-manager.ts +0 -457
- package/src/components/menu/features/keyboard-navigation.ts +0 -133
- package/src/components/menu/features/positioning.ts +0 -127
- package/src/components/menu/features/visibility.ts +0 -230
- package/src/components/menu/menu-item.ts +0 -86
- package/src/components/menu/utils.ts +0 -67
- /package/src/{core/build → styles/utilities}/_ripple.scss +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/components/segmented-button/config.ts
|
|
2
2
|
import { createComponentConfig } from '../../core/config/component-config';
|
|
3
|
-
import { SegmentedButtonConfig, SelectionMode } from './types';
|
|
3
|
+
import { SegmentedButtonConfig, SelectionMode, Density } from './types';
|
|
4
4
|
|
|
5
5
|
export const DEFAULT_CHECKMARK_ICON = `
|
|
6
6
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -13,7 +13,8 @@ export const DEFAULT_CHECKMARK_ICON = `
|
|
|
13
13
|
*/
|
|
14
14
|
export const DEFAULT_CONFIG = {
|
|
15
15
|
mode: SelectionMode.SINGLE,
|
|
16
|
-
ripple: true
|
|
16
|
+
ripple: true,
|
|
17
|
+
density: Density.DEFAULT
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
/**
|
|
@@ -31,20 +32,56 @@ export const createBaseConfig = (config: SegmentedButtonConfig = {}): SegmentedB
|
|
|
31
32
|
* @returns {Object} Element configuration object for withElement
|
|
32
33
|
* @internal
|
|
33
34
|
*/
|
|
34
|
-
export const getContainerConfig = (config: SegmentedButtonConfig) =>
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
35
|
+
export const getContainerConfig = (config: SegmentedButtonConfig) => {
|
|
36
|
+
const density = config.density || Density.DEFAULT;
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
tag: 'div',
|
|
40
|
+
componentName: 'segmented-button',
|
|
41
|
+
attrs: {
|
|
42
|
+
role: 'group',
|
|
43
|
+
'aria-label': 'Segmented button',
|
|
44
|
+
'data-mode': config.mode || SelectionMode.SINGLE,
|
|
45
|
+
'data-density': density
|
|
46
|
+
},
|
|
47
|
+
className: [
|
|
48
|
+
config.class,
|
|
49
|
+
config.disabled ? `${config.prefix}-segmented-button--disabled` : null,
|
|
50
|
+
density !== Density.DEFAULT ? `${config.prefix}-segmented-button--${density}` : null
|
|
51
|
+
],
|
|
52
|
+
interactive: true
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Gets density-specific sizing and spacing values
|
|
58
|
+
* @param {string} density - The density level
|
|
59
|
+
* @returns {Object} CSS variables with sizing values
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
export const getDensityStyles = (density: string): Record<string, string> => {
|
|
63
|
+
switch (density) {
|
|
64
|
+
case Density.COMPACT:
|
|
65
|
+
return {
|
|
66
|
+
'--segment-padding': '4px 8px',
|
|
67
|
+
'--segment-height': '28px',
|
|
68
|
+
'--segment-font-size': '0.8125rem'
|
|
69
|
+
};
|
|
70
|
+
case Density.COMFORTABLE:
|
|
71
|
+
return {
|
|
72
|
+
'--segment-padding': '6px 12px',
|
|
73
|
+
'--segment-height': '32px',
|
|
74
|
+
'--segment-font-size': '0.875rem'
|
|
75
|
+
};
|
|
76
|
+
case Density.DEFAULT:
|
|
77
|
+
default:
|
|
78
|
+
return {
|
|
79
|
+
'--segment-padding': '8px 16px',
|
|
80
|
+
'--segment-height': '36px',
|
|
81
|
+
'--segment-font-size': '0.875rem'
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
};
|
|
48
85
|
|
|
49
86
|
/**
|
|
50
87
|
* Generates configuration for a segment element
|
|
@@ -57,6 +94,7 @@ export const getContainerConfig = (config: SegmentedButtonConfig) => ({
|
|
|
57
94
|
export const getSegmentConfig = (segment, prefix, groupDisabled = false) => {
|
|
58
95
|
const isDisabled = groupDisabled || segment.disabled;
|
|
59
96
|
|
|
97
|
+
// We use button as our base class, but add segment-specific classes for states
|
|
60
98
|
return {
|
|
61
99
|
tag: 'button',
|
|
62
100
|
attrs: {
|
|
@@ -67,10 +105,11 @@ export const getSegmentConfig = (segment, prefix, groupDisabled = false) => {
|
|
|
67
105
|
value: segment.value
|
|
68
106
|
},
|
|
69
107
|
className: [
|
|
70
|
-
`${prefix}-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
segment
|
|
108
|
+
`${prefix}-button`, // Base button class
|
|
109
|
+
`${prefix}-segmented-button-segment`, // Specific segment class
|
|
110
|
+
segment.selected ? `${prefix}-segment--selected` : null, // Selected state
|
|
111
|
+
isDisabled ? `${prefix}-segment--disabled` : null, // Disabled state
|
|
112
|
+
segment.class // Custom class if provided
|
|
74
113
|
],
|
|
75
114
|
forwardEvents: {
|
|
76
115
|
click: (component) => !isDisabled
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// src/components/segmented-button/index.ts
|
|
2
2
|
export { default, default as createSegmentedButton } from './segmented-button';
|
|
3
|
-
export { SelectionMode } from './types';
|
|
3
|
+
export { SelectionMode, Density } from './types';
|
|
4
4
|
export type { SegmentedButtonConfig, SegmentedButtonComponent, SegmentConfig, Segment } from './types';
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
// src/components/segmented-button/segment.ts
|
|
2
|
-
import
|
|
3
|
-
import { createRipple } from '../../core/build/ripple';
|
|
2
|
+
import createButton from '../button';
|
|
4
3
|
import { SegmentConfig, Segment } from './types';
|
|
5
|
-
import {
|
|
4
|
+
import { DEFAULT_CHECKMARK_ICON } from './config';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
|
-
* Creates a segment for the segmented button
|
|
7
|
+
* Creates a segment for the segmented button using the button component
|
|
9
8
|
* @param {SegmentConfig} config - Segment configuration
|
|
10
9
|
* @param {HTMLElement} container - Container element
|
|
11
10
|
* @param {string} prefix - Component prefix
|
|
@@ -21,59 +20,36 @@ export const createSegment = (
|
|
|
21
20
|
groupDisabled = false,
|
|
22
21
|
options = { ripple: true, rippleConfig: {} }
|
|
23
22
|
): Segment => {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
const isDisabled = groupDisabled || config.disabled;
|
|
24
|
+
const originalIcon = config.icon;
|
|
25
|
+
const checkmarkIcon = config.checkmarkIcon || DEFAULT_CHECKMARK_ICON;
|
|
26
|
+
|
|
27
|
+
// Create segment using button component with appropriate initial icon
|
|
28
|
+
const button = createButton({
|
|
29
|
+
text: config.text,
|
|
30
|
+
// If selected and has both icon and text, use checkmark instead of the original icon
|
|
31
|
+
icon: (config.selected && config.text && originalIcon) ? checkmarkIcon : originalIcon,
|
|
32
|
+
value: config.value || config.text || '',
|
|
33
|
+
disabled: isDisabled,
|
|
34
|
+
ripple: options.ripple,
|
|
35
|
+
rippleConfig: options.rippleConfig,
|
|
36
|
+
class: config.class,
|
|
37
|
+
// No variant - we'll style it via the segmented button styles
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Add segment-specific classes
|
|
41
|
+
button.element.classList.add(`${prefix}-segmented-button-segment`);
|
|
36
42
|
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
text: config.text,
|
|
44
|
-
container: element
|
|
45
|
-
});
|
|
43
|
+
// Set initial selected state
|
|
44
|
+
if (config.selected) {
|
|
45
|
+
button.element.classList.add(`${prefix}-segment--selected`);
|
|
46
|
+
button.element.setAttribute('aria-pressed', 'true');
|
|
47
|
+
} else {
|
|
48
|
+
button.element.setAttribute('aria-pressed', 'false');
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
if (config.icon) {
|
|
51
|
-
// Create icon element
|
|
52
|
-
iconElement = createElement({
|
|
53
|
-
tag: 'span',
|
|
54
|
-
className: `${prefix}-segmentedbutton-segment-icon`,
|
|
55
|
-
html: config.icon,
|
|
56
|
-
container: element
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// Create checkmark element (hidden initially)
|
|
60
|
-
checkmarkElement = createElement({
|
|
61
|
-
tag: 'span',
|
|
62
|
-
className: `${prefix}-segmentedbutton-segment-'checkmark'`,
|
|
63
|
-
html: 'icon',
|
|
64
|
-
container: element
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Hide checkmark if not selected
|
|
68
|
-
if (!config.selected) {
|
|
69
|
-
checkmarkElement.style.display = 'none';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Hide icon if selected and we have text (icon replaced by checkmark)
|
|
73
|
-
if (config.selected && config.text) {
|
|
74
|
-
iconElement.style.display = 'none';
|
|
75
|
-
}
|
|
76
|
-
}
|
|
51
|
+
// Add to container
|
|
52
|
+
container.appendChild(button.element);
|
|
77
53
|
|
|
78
54
|
/**
|
|
79
55
|
* Updates the visual state based on selection
|
|
@@ -81,45 +57,27 @@ export const createSegment = (
|
|
|
81
57
|
* @private
|
|
82
58
|
*/
|
|
83
59
|
const updateSelectedState = (selected: boolean) => {
|
|
84
|
-
element.classList.toggle(`${prefix}-
|
|
85
|
-
element.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
60
|
+
button.element.classList.toggle(`${prefix}-segment--selected`, selected);
|
|
61
|
+
button.element.setAttribute('aria-pressed', selected ? 'true' : 'false');
|
|
86
62
|
|
|
87
|
-
// Handle icon/checkmark swap if we have both text and icon
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Updates the disabled state
|
|
99
|
-
* @param {boolean} disabled - Whether the segment is disabled
|
|
100
|
-
* @private
|
|
101
|
-
*/
|
|
102
|
-
const updateDisabledState = (disabled: boolean) => {
|
|
103
|
-
const isDisabled = disabled || groupDisabled;
|
|
104
|
-
element.classList.toggle(`${prefix}-segmentedbutton-segment--disabled`, isDisabled);
|
|
105
|
-
|
|
106
|
-
if (isDisabled) {
|
|
107
|
-
element.setAttribute('disabled', 'true');
|
|
108
|
-
} else {
|
|
109
|
-
element.removeAttribute('disabled');
|
|
63
|
+
// Handle icon/checkmark swap if we have both text and original icon
|
|
64
|
+
if (config.text && originalIcon) {
|
|
65
|
+
if (selected) {
|
|
66
|
+
// When selected and has both text and icon, show checkmark
|
|
67
|
+
button.setIcon(checkmarkIcon);
|
|
68
|
+
} else {
|
|
69
|
+
// When not selected, restore original icon
|
|
70
|
+
button.setIcon(originalIcon);
|
|
71
|
+
}
|
|
110
72
|
}
|
|
111
73
|
};
|
|
112
74
|
|
|
113
|
-
// Value to use for the segment
|
|
114
|
-
const value = config.value || config.text || '';
|
|
115
|
-
|
|
116
75
|
// Initialize state
|
|
117
76
|
let isSelected = config.selected || false;
|
|
118
|
-
let isDisabled = config.disabled || false;
|
|
119
77
|
|
|
120
78
|
return {
|
|
121
|
-
element,
|
|
122
|
-
value,
|
|
79
|
+
element: button.element,
|
|
80
|
+
value: config.value || config.text || '',
|
|
123
81
|
|
|
124
82
|
isSelected() {
|
|
125
83
|
return isSelected;
|
|
@@ -131,24 +89,20 @@ export const createSegment = (
|
|
|
131
89
|
},
|
|
132
90
|
|
|
133
91
|
isDisabled() {
|
|
134
|
-
return isDisabled ||
|
|
92
|
+
return button.disabled?.isDisabled?.() || false;
|
|
135
93
|
},
|
|
136
94
|
|
|
137
95
|
setDisabled(disabled: boolean) {
|
|
138
|
-
|
|
139
|
-
|
|
96
|
+
if (disabled) {
|
|
97
|
+
button.disable();
|
|
98
|
+
} else {
|
|
99
|
+
button.enable();
|
|
100
|
+
}
|
|
140
101
|
},
|
|
141
102
|
|
|
142
103
|
destroy() {
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
ripple.unmount(element);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Remove from DOM
|
|
149
|
-
if (element.parentNode) {
|
|
150
|
-
element.parentNode.removeChild(element);
|
|
151
|
-
}
|
|
104
|
+
// Use the button's built-in destroy method for cleanup
|
|
105
|
+
button.destroy();
|
|
152
106
|
}
|
|
153
107
|
};
|
|
154
108
|
};
|
|
@@ -3,19 +3,61 @@ import { pipe } from '../../core/compose/pipe';
|
|
|
3
3
|
import { createBase, withElement } from '../../core/compose/component';
|
|
4
4
|
import { withEvents, withLifecycle } from '../../core/compose/features';
|
|
5
5
|
import { createEmitter } from '../../core/state/emitter';
|
|
6
|
-
import { SegmentedButtonConfig, SegmentedButtonComponent, SelectionMode, Segment } from './types';
|
|
7
|
-
import { createBaseConfig, getContainerConfig } from './config';
|
|
6
|
+
import { SegmentedButtonConfig, SegmentedButtonComponent, SelectionMode, Density, Segment } from './types';
|
|
7
|
+
import { createBaseConfig, getContainerConfig, getDensityStyles } from './config';
|
|
8
8
|
import { createSegment } from './segment';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Creates a new Segmented Button component
|
|
12
|
+
*
|
|
13
|
+
* The Segmented Button component provides a group of related buttons that can
|
|
14
|
+
* be used for selection and filtering. It supports single or multiple selection modes,
|
|
15
|
+
* configurable density, disabled states, and event handling.
|
|
16
|
+
*
|
|
12
17
|
* @param {SegmentedButtonConfig} config - Segmented Button configuration
|
|
13
18
|
* @returns {SegmentedButtonComponent} Segmented Button component instance
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* // Create a segmented button with three segments in single selection mode
|
|
22
|
+
* const viewToggle = createSegmentedButton({
|
|
23
|
+
* segments: [
|
|
24
|
+
* { text: 'Day', value: 'day', selected: true },
|
|
25
|
+
* { text: 'Week', value: 'week' },
|
|
26
|
+
* { text: 'Month', value: 'month' }
|
|
27
|
+
* ],
|
|
28
|
+
* mode: SelectionMode.SINGLE
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Listen for selection changes
|
|
32
|
+
* viewToggle.on('change', (event) => {
|
|
33
|
+
* console.log('Selected view:', event.value[0]);
|
|
34
|
+
* updateCalendarView(event.value[0]);
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Create a compact multi-select segmented button with icons
|
|
39
|
+
* const filterOptions = createSegmentedButton({
|
|
40
|
+
* segments: [
|
|
41
|
+
* {
|
|
42
|
+
* icon: '<svg>...</svg>',
|
|
43
|
+
* text: 'Filter 1',
|
|
44
|
+
* value: 'filter1'
|
|
45
|
+
* },
|
|
46
|
+
* {
|
|
47
|
+
* icon: '<svg>...</svg>',
|
|
48
|
+
* text: 'Filter 2',
|
|
49
|
+
* value: 'filter2'
|
|
50
|
+
* }
|
|
51
|
+
* ],
|
|
52
|
+
* mode: SelectionMode.MULTI,
|
|
53
|
+
* density: Density.COMPACT
|
|
54
|
+
* });
|
|
14
55
|
*/
|
|
15
56
|
const createSegmentedButton = (config: SegmentedButtonConfig = {}): SegmentedButtonComponent => {
|
|
16
57
|
// Process configuration
|
|
17
58
|
const baseConfig = createBaseConfig(config);
|
|
18
59
|
const mode = baseConfig.mode || SelectionMode.SINGLE;
|
|
60
|
+
const density = baseConfig.density || Density.DEFAULT;
|
|
19
61
|
const emitter = createEmitter();
|
|
20
62
|
|
|
21
63
|
try {
|
|
@@ -27,6 +69,12 @@ const createSegmentedButton = (config: SegmentedButtonConfig = {}): SegmentedBut
|
|
|
27
69
|
withLifecycle()
|
|
28
70
|
)(baseConfig);
|
|
29
71
|
|
|
72
|
+
// Apply density styles
|
|
73
|
+
const densityStyles = getDensityStyles(density as string);
|
|
74
|
+
Object.entries(densityStyles).forEach(([prop, value]) => {
|
|
75
|
+
component.element.style.setProperty(prop, value);
|
|
76
|
+
});
|
|
77
|
+
|
|
30
78
|
// Create segments
|
|
31
79
|
const segments: Segment[] = [];
|
|
32
80
|
if (baseConfig.segments && baseConfig.segments.length) {
|
|
@@ -125,6 +173,34 @@ const createSegmentedButton = (config: SegmentedButtonConfig = {}): SegmentedBut
|
|
|
125
173
|
*/
|
|
126
174
|
const findSegmentByValue = (value: string) => segments.find(segment => segment.value === value);
|
|
127
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Updates the density of the segmented button
|
|
178
|
+
* @param {string} newDensity - New density value
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
const updateDensity = (newDensity: string) => {
|
|
182
|
+
// Remove existing density classes
|
|
183
|
+
[Density.DEFAULT, Density.COMFORTABLE, Density.COMPACT].forEach(d => {
|
|
184
|
+
if (d !== Density.DEFAULT) {
|
|
185
|
+
component.element.classList.remove(`${baseConfig.prefix}-segmented-button--${d}`);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Add new density class if not default
|
|
190
|
+
if (newDensity !== Density.DEFAULT) {
|
|
191
|
+
component.element.classList.add(`${baseConfig.prefix}-segmented-button--${newDensity}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Update data attribute
|
|
195
|
+
component.element.setAttribute('data-density', newDensity);
|
|
196
|
+
|
|
197
|
+
// Apply density styles
|
|
198
|
+
const densityStyles = getDensityStyles(newDensity);
|
|
199
|
+
Object.entries(densityStyles).forEach(([prop, value]) => {
|
|
200
|
+
component.element.style.setProperty(prop, value);
|
|
201
|
+
});
|
|
202
|
+
};
|
|
203
|
+
|
|
128
204
|
// Create the component API
|
|
129
205
|
const segmentedButton: SegmentedButtonComponent = {
|
|
130
206
|
element: component.element,
|
|
@@ -205,15 +281,51 @@ const createSegmentedButton = (config: SegmentedButtonConfig = {}): SegmentedBut
|
|
|
205
281
|
enable() {
|
|
206
282
|
// Enable the entire component
|
|
207
283
|
component.element.classList.remove(`${baseConfig.prefix}-segmented-button--disabled`);
|
|
284
|
+
// Enable all segments (unless individually disabled)
|
|
285
|
+
segments.forEach(segment => {
|
|
286
|
+
// Only enable if it wasn't individually disabled
|
|
287
|
+
if (!baseConfig.segments?.find(s => s.value === segment.value)?.disabled) {
|
|
288
|
+
segment.setDisabled(false);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
208
291
|
return this;
|
|
209
292
|
},
|
|
210
293
|
|
|
211
294
|
disable() {
|
|
212
295
|
// Disable the entire component
|
|
213
296
|
component.element.classList.add(`${baseConfig.prefix}-segmented-button--disabled`);
|
|
297
|
+
// Disable all segments
|
|
298
|
+
segments.forEach(segment => {
|
|
299
|
+
segment.setDisabled(true);
|
|
300
|
+
});
|
|
214
301
|
return this;
|
|
215
302
|
},
|
|
216
303
|
|
|
304
|
+
enableSegment(value) {
|
|
305
|
+
const segment = findSegmentByValue(value);
|
|
306
|
+
if (segment) {
|
|
307
|
+
segment.setDisabled(false);
|
|
308
|
+
}
|
|
309
|
+
return this;
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
disableSegment(value) {
|
|
313
|
+
const segment = findSegmentByValue(value);
|
|
314
|
+
if (segment) {
|
|
315
|
+
segment.setDisabled(true);
|
|
316
|
+
}
|
|
317
|
+
return this;
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
setDensity(newDensity) {
|
|
321
|
+
updateDensity(newDensity);
|
|
322
|
+
return this;
|
|
323
|
+
},
|
|
324
|
+
|
|
325
|
+
getDensity() {
|
|
326
|
+
return component.element.getAttribute('data-density') || Density.DEFAULT;
|
|
327
|
+
},
|
|
328
|
+
|
|
217
329
|
on(event, handler) {
|
|
218
330
|
emitter.on(event, handler);
|
|
219
331
|
return this;
|
|
@@ -11,6 +11,20 @@ export enum SelectionMode {
|
|
|
11
11
|
MULTI = 'multi'
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Density options for segmented button
|
|
16
|
+
* Controls the overall sizing and spacing of the component.
|
|
17
|
+
* @category Components
|
|
18
|
+
*/
|
|
19
|
+
export enum Density {
|
|
20
|
+
/** Default size with standard spacing */
|
|
21
|
+
DEFAULT = 'default',
|
|
22
|
+
/** Reduced size and spacing, more compact */
|
|
23
|
+
COMFORTABLE = 'comfortable',
|
|
24
|
+
/** Minimal size and spacing, most compact */
|
|
25
|
+
COMPACT = 'compact'
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
/**
|
|
15
29
|
* Event types for segmented button
|
|
16
30
|
*/
|
|
@@ -77,6 +91,11 @@ export interface SegmentConfig {
|
|
|
77
91
|
* Additional CSS class names for this segment
|
|
78
92
|
*/
|
|
79
93
|
class?: string;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Custom icon to display when segment is selected (replaces default checkmark)
|
|
97
|
+
*/
|
|
98
|
+
checkmarkIcon?: string;
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
/**
|
|
@@ -122,6 +141,12 @@ export interface SegmentedButtonConfig {
|
|
|
122
141
|
* @default true
|
|
123
142
|
*/
|
|
124
143
|
ripple?: boolean;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Density setting that controls the component's size and spacing
|
|
147
|
+
* @default Density.DEFAULT
|
|
148
|
+
*/
|
|
149
|
+
density?: Density | string;
|
|
125
150
|
|
|
126
151
|
/**
|
|
127
152
|
* Ripple effect configuration
|
|
@@ -230,6 +255,33 @@ export interface SegmentedButtonComponent {
|
|
|
230
255
|
* @returns The SegmentedButtonComponent for chaining
|
|
231
256
|
*/
|
|
232
257
|
disable: () => SegmentedButtonComponent;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Enables a specific segment by its value
|
|
261
|
+
* @param value - The value of the segment to enable
|
|
262
|
+
* @returns The SegmentedButtonComponent for chaining
|
|
263
|
+
*/
|
|
264
|
+
enableSegment: (value: string) => SegmentedButtonComponent;
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Disables a specific segment by its value
|
|
268
|
+
* @param value - The value of the segment to disable
|
|
269
|
+
* @returns The SegmentedButtonComponent for chaining
|
|
270
|
+
*/
|
|
271
|
+
disableSegment: (value: string) => SegmentedButtonComponent;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Sets the density of the segmented button
|
|
275
|
+
* @param density - The density level to set
|
|
276
|
+
* @returns The SegmentedButtonComponent for chaining
|
|
277
|
+
*/
|
|
278
|
+
setDensity: (density: Density | string) => SegmentedButtonComponent;
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Gets the current density setting
|
|
282
|
+
* @returns The current density
|
|
283
|
+
*/
|
|
284
|
+
getDensity: () => string;
|
|
233
285
|
|
|
234
286
|
/**
|
|
235
287
|
* Adds an event listener to the segmented button
|
|
@@ -41,29 +41,31 @@ const updateCircularStyle = (component: ElementComponent, config: IconConfig): v
|
|
|
41
41
|
|
|
42
42
|
/**
|
|
43
43
|
* Adds icon management to a component
|
|
44
|
-
*
|
|
45
44
|
* @param config - Configuration object containing icon information
|
|
46
45
|
* @returns Function that enhances a component with icon capabilities
|
|
47
46
|
*/
|
|
48
47
|
export const withIcon = <T extends IconConfig>(config: T) =>
|
|
49
48
|
<C extends ElementComponent>(component: C): C & IconComponent => {
|
|
49
|
+
// Create the icon with configuration settings
|
|
50
50
|
const icon = createIcon(component.element, {
|
|
51
51
|
prefix: config.prefix,
|
|
52
52
|
type: config.componentName || 'component',
|
|
53
53
|
position: config.iconPosition,
|
|
54
54
|
iconSize: config.iconSize
|
|
55
55
|
});
|
|
56
|
-
|
|
57
|
-
if
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
|
|
57
|
+
// Set icon if provided in config
|
|
58
|
+
config.icon && icon.setIcon(config.icon);
|
|
59
|
+
|
|
60
|
+
// Apply button-specific styling if the component is a button
|
|
61
61
|
if (component.componentName === 'button') {
|
|
62
|
-
|
|
62
|
+
if (!config.text) {
|
|
63
|
+
updateCircularStyle(component, config);
|
|
64
|
+
} else if (config.icon && config.text) {
|
|
65
|
+
component.element.classList.add(`${component.getClass('button')}--icon`);
|
|
66
|
+
}
|
|
63
67
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
};
|
|
68
|
+
|
|
69
|
+
// Return enhanced component with icon capabilities
|
|
70
|
+
return Object.assign(component, { icon });
|
|
71
|
+
};
|