mtrl 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -22
- package/index.ts +33 -0
- package/package.json +14 -5
- package/src/components/button/{styles.scss → _styles.scss} +2 -2
- package/src/components/button/api.ts +89 -0
- package/src/components/button/button.ts +50 -0
- package/src/components/button/config.ts +75 -0
- package/src/components/button/constants.ts +17 -0
- package/src/components/button/index.ts +4 -0
- package/src/components/button/types.ts +118 -0
- package/src/components/card/_styles.scss +359 -0
- package/src/components/card/actions.ts +48 -0
- package/src/components/card/api.ts +102 -0
- package/src/components/card/card.ts +41 -0
- package/src/components/card/config.ts +99 -0
- package/src/components/card/constants.ts +69 -0
- package/src/components/card/content.ts +48 -0
- package/src/components/card/features.ts +228 -0
- package/src/components/card/header.ts +88 -0
- package/src/components/card/index.ts +19 -0
- package/src/components/card/media.ts +52 -0
- package/src/components/card/types.ts +174 -0
- package/src/components/checkbox/api.ts +82 -0
- package/src/components/checkbox/checkbox.ts +75 -0
- package/src/components/checkbox/config.ts +90 -0
- package/src/components/checkbox/index.ts +4 -0
- package/src/components/checkbox/types.ts +146 -0
- package/src/components/chip/_styles.scss +372 -0
- package/src/components/chip/api.ts +115 -0
- package/src/components/chip/chip-set.ts +225 -0
- package/src/components/chip/chip.ts +82 -0
- package/src/components/chip/config.ts +92 -0
- package/src/components/chip/constants.ts +38 -0
- package/src/components/chip/index.ts +4 -0
- package/src/components/chip/types.ts +172 -0
- package/src/components/list/api.ts +72 -0
- package/src/components/list/config.ts +43 -0
- package/src/components/list/{constants.js → constants.ts} +34 -7
- package/src/components/list/features.ts +224 -0
- package/src/components/list/index.ts +14 -0
- package/src/components/list/list-item.ts +120 -0
- package/src/components/list/list.ts +37 -0
- package/src/components/list/types.ts +179 -0
- package/src/components/list/utils.ts +47 -0
- package/src/components/menu/api.ts +119 -0
- package/src/components/menu/config.ts +54 -0
- package/src/components/menu/constants.ts +154 -0
- package/src/components/menu/features/items-manager.ts +457 -0
- package/src/components/menu/features/keyboard-navigation.ts +133 -0
- package/src/components/menu/features/positioning.ts +127 -0
- package/src/components/menu/features/{visibility.js → visibility.ts} +66 -64
- package/src/components/menu/index.ts +14 -0
- package/src/components/menu/menu-item.ts +43 -0
- package/src/components/menu/menu.ts +53 -0
- package/src/components/menu/types.ts +178 -0
- package/src/components/navigation/api.ts +79 -0
- package/src/components/navigation/config.ts +61 -0
- package/src/components/navigation/{constants.js → constants.ts} +10 -10
- package/src/components/navigation/index.ts +14 -0
- package/src/components/navigation/nav-item.ts +148 -0
- package/src/components/navigation/navigation.ts +50 -0
- package/src/components/navigation/types.ts +212 -0
- package/src/components/progress/_styles.scss +204 -0
- package/src/components/progress/api.ts +179 -0
- package/src/components/progress/config.ts +124 -0
- package/src/components/progress/constants.ts +43 -0
- package/src/components/progress/index.ts +5 -0
- package/src/components/progress/progress.ts +163 -0
- package/src/components/progress/types.ts +102 -0
- package/src/components/snackbar/api.ts +162 -0
- package/src/components/snackbar/config.ts +62 -0
- package/src/components/snackbar/{constants.js → constants.ts} +21 -4
- package/src/components/snackbar/features.ts +76 -0
- package/src/components/snackbar/index.ts +4 -0
- package/src/components/snackbar/position.ts +71 -0
- package/src/components/snackbar/queue.ts +76 -0
- package/src/components/snackbar/snackbar.ts +60 -0
- package/src/components/snackbar/types.ts +58 -0
- package/src/components/switch/api.ts +77 -0
- package/src/components/switch/config.ts +74 -0
- package/src/components/switch/index.ts +4 -0
- package/src/components/switch/switch.ts +52 -0
- package/src/components/switch/types.ts +142 -0
- package/src/components/textfield/api.ts +72 -0
- package/src/components/textfield/config.ts +54 -0
- package/src/components/textfield/{constants.js → constants.ts} +38 -5
- package/src/components/textfield/index.ts +4 -0
- package/src/components/textfield/textfield.ts +50 -0
- package/src/components/textfield/types.ts +139 -0
- package/src/core/compose/base.ts +43 -0
- package/src/core/compose/component.ts +247 -0
- package/src/core/compose/features/checkable.ts +155 -0
- package/src/core/compose/features/disabled.ts +116 -0
- package/src/core/compose/features/events.ts +65 -0
- package/src/core/compose/features/icon.ts +67 -0
- package/src/core/compose/features/index.ts +35 -0
- package/src/core/compose/features/input.ts +174 -0
- package/src/core/compose/features/lifecycle.ts +139 -0
- package/src/core/compose/features/position.ts +94 -0
- package/src/core/compose/features/ripple.ts +55 -0
- package/src/core/compose/features/size.ts +29 -0
- package/src/core/compose/features/style.ts +31 -0
- package/src/core/compose/features/text.ts +44 -0
- package/src/core/compose/features/textinput.ts +225 -0
- package/src/core/compose/features/textlabel.ts +92 -0
- package/src/core/compose/features/track.ts +84 -0
- package/src/core/compose/features/variant.ts +29 -0
- package/src/core/compose/features/withEvents.ts +137 -0
- package/src/core/compose/index.ts +54 -0
- package/src/core/compose/{pipe.js → pipe.ts} +16 -11
- package/src/core/config/component-config.ts +136 -0
- package/src/core/config.ts +211 -0
- package/src/core/dom/{attributes.js → attributes.ts} +11 -11
- package/src/core/dom/classes.ts +60 -0
- package/src/core/dom/create.ts +188 -0
- package/src/core/dom/events.ts +209 -0
- package/src/core/dom/index.ts +10 -0
- package/src/core/dom/utils.ts +97 -0
- package/src/core/index.ts +111 -0
- package/src/core/state/disabled.ts +81 -0
- package/src/core/state/emitter.ts +94 -0
- package/src/core/state/events.ts +88 -0
- package/src/core/state/index.ts +16 -0
- package/src/core/state/lifecycle.ts +131 -0
- package/src/core/state/store.ts +197 -0
- package/src/core/utils/index.ts +45 -0
- package/src/core/utils/{mobile.js → mobile.ts} +48 -24
- package/src/core/utils/object.ts +41 -0
- package/src/core/utils/validate.ts +234 -0
- package/src/{index.js → index.ts} +4 -2
- package/index.js +0 -11
- package/src/components/button/api.js +0 -54
- package/src/components/button/button.js +0 -81
- package/src/components/button/config.js +0 -10
- package/src/components/button/constants.js +0 -63
- package/src/components/button/index.js +0 -2
- package/src/components/checkbox/api.js +0 -45
- package/src/components/checkbox/checkbox.js +0 -96
- package/src/components/checkbox/index.js +0 -2
- package/src/components/container/api.js +0 -42
- package/src/components/container/container.js +0 -45
- package/src/components/container/index.js +0 -2
- package/src/components/container/styles.scss +0 -66
- package/src/components/list/index.js +0 -2
- package/src/components/list/list-item.js +0 -147
- package/src/components/list/list.js +0 -267
- package/src/components/menu/api.js +0 -117
- package/src/components/menu/constants.js +0 -42
- package/src/components/menu/features/items-manager.js +0 -375
- package/src/components/menu/features/keyboard-navigation.js +0 -129
- package/src/components/menu/features/positioning.js +0 -125
- package/src/components/menu/index.js +0 -2
- package/src/components/menu/menu-item.js +0 -41
- package/src/components/menu/menu.js +0 -54
- package/src/components/navigation/api.js +0 -43
- package/src/components/navigation/index.js +0 -2
- package/src/components/navigation/nav-item.js +0 -137
- package/src/components/navigation/navigation.js +0 -55
- package/src/components/snackbar/api.js +0 -125
- package/src/components/snackbar/features.js +0 -69
- package/src/components/snackbar/index.js +0 -2
- package/src/components/snackbar/position.js +0 -63
- package/src/components/snackbar/queue.js +0 -74
- package/src/components/snackbar/snackbar.js +0 -70
- package/src/components/switch/api.js +0 -44
- package/src/components/switch/index.js +0 -2
- package/src/components/switch/switch.js +0 -71
- package/src/components/textfield/api.js +0 -49
- package/src/components/textfield/index.js +0 -2
- package/src/components/textfield/textfield.js +0 -68
- package/src/core/build/_ripple.scss +0 -79
- package/src/core/build/constants.js +0 -51
- package/src/core/build/icon.js +0 -78
- package/src/core/build/ripple.js +0 -159
- package/src/core/build/text.js +0 -54
- package/src/core/compose/base.js +0 -8
- package/src/core/compose/component.js +0 -225
- package/src/core/compose/features/checkable.js +0 -114
- package/src/core/compose/features/disabled.js +0 -64
- package/src/core/compose/features/events.js +0 -48
- package/src/core/compose/features/icon.js +0 -33
- package/src/core/compose/features/index.js +0 -20
- package/src/core/compose/features/input.js +0 -100
- package/src/core/compose/features/lifecycle.js +0 -69
- package/src/core/compose/features/position.js +0 -60
- package/src/core/compose/features/ripple.js +0 -32
- package/src/core/compose/features/size.js +0 -9
- package/src/core/compose/features/style.js +0 -12
- package/src/core/compose/features/text.js +0 -17
- package/src/core/compose/features/textinput.js +0 -114
- package/src/core/compose/features/textlabel.js +0 -28
- package/src/core/compose/features/track.js +0 -49
- package/src/core/compose/features/variant.js +0 -9
- package/src/core/compose/features/withEvents.js +0 -67
- package/src/core/compose/index.js +0 -16
- package/src/core/config.js +0 -140
- package/src/core/dom/classes.js +0 -70
- package/src/core/dom/create.js +0 -132
- package/src/core/dom/events.js +0 -175
- package/src/core/dom/index.js +0 -5
- package/src/core/dom/utils.js +0 -22
- package/src/core/index.js +0 -23
- package/src/core/state/disabled.js +0 -51
- package/src/core/state/emitter.js +0 -63
- package/src/core/state/events.js +0 -29
- package/src/core/state/index.js +0 -6
- package/src/core/state/lifecycle.js +0 -64
- package/src/core/state/store.js +0 -112
- package/src/core/utils/index.js +0 -39
- package/src/core/utils/object.js +0 -22
- package/src/core/utils/validate.js +0 -37
- /package/src/components/checkbox/{styles.scss → _styles.scss} +0 -0
- /package/src/components/checkbox/{constants.js → constants.ts} +0 -0
- /package/src/components/list/{styles.scss → _styles.scss} +0 -0
- /package/src/components/menu/{styles.scss → _styles.scss} +0 -0
- /package/src/components/navigation/{styles.scss → _styles.scss} +0 -0
- /package/src/components/snackbar/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{styles.scss → _styles.scss} +0 -0
- /package/src/components/switch/{constants.js → constants.ts} +0 -0
- /package/src/components/textfield/{styles.scss → _styles.scss} +0 -0
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
// src/core/compose/component.js
|
|
2
|
-
/**
|
|
3
|
-
* @module core/compose/component
|
|
4
|
-
* @description Core utilities for component composition and creation with built-in mobile support
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { createElement } from '../dom/create'
|
|
8
|
-
import {
|
|
9
|
-
normalizeEvent,
|
|
10
|
-
hasTouchSupport,
|
|
11
|
-
TOUCH_CONFIG,
|
|
12
|
-
PASSIVE_EVENTS
|
|
13
|
-
} from '../utils/mobile'
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Creates helper functions for managing CSS class names with a prefix
|
|
17
|
-
* @param {string} prefix - Prefix to apply to class names
|
|
18
|
-
* @returns {Object} Class name utilities
|
|
19
|
-
* @property {Function} getClass - Gets a class name with prefix
|
|
20
|
-
* @property {Function} getModifierClass - Gets a modifier class with prefix
|
|
21
|
-
* @property {Function} getElementClass - Gets an element class with prefix
|
|
22
|
-
* @example
|
|
23
|
-
* const { getClass } = withPrefix('mtrl');
|
|
24
|
-
* getClass('button'); // Returns 'mtrl-button'
|
|
25
|
-
*/
|
|
26
|
-
const withPrefix = prefix => ({
|
|
27
|
-
/**
|
|
28
|
-
* Gets a prefixed class name
|
|
29
|
-
* @param {string} name - Base class name
|
|
30
|
-
* @returns {string} Prefixed class name
|
|
31
|
-
*/
|
|
32
|
-
getClass: (name) => `${prefix}-${name}`,
|
|
33
|
-
/**
|
|
34
|
-
* Gets a prefixed modifier class name
|
|
35
|
-
* @param {string} base - Base class name
|
|
36
|
-
* @param {string} modifier - Modifier name
|
|
37
|
-
* @returns {string} Prefixed modifier class
|
|
38
|
-
*/
|
|
39
|
-
getModifierClass: (base, modifier) => `${base}--${modifier}`,
|
|
40
|
-
/**
|
|
41
|
-
* Gets a prefixed element class name
|
|
42
|
-
* @param {string} base - Base class name
|
|
43
|
-
* @param {string} element - Element name
|
|
44
|
-
* @returns {string} Prefixed element class
|
|
45
|
-
*/
|
|
46
|
-
getElementClass: (base, element) => `${base}-${element}`
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Creates a base component with configuration and prefix utilities.
|
|
51
|
-
* This forms the foundation for all components in the system.
|
|
52
|
-
*
|
|
53
|
-
* @param {Object} config - Component configuration
|
|
54
|
-
* @param {string} [config.prefix='mtrl'] - CSS class prefix
|
|
55
|
-
* @param {string} [config.componentName] - Component name for class generation
|
|
56
|
-
* @returns {Object} Base component with prefix utilities
|
|
57
|
-
*/
|
|
58
|
-
export const createBase = (config = {}) => ({
|
|
59
|
-
config,
|
|
60
|
-
componentName: config.componentName,
|
|
61
|
-
...withPrefix(config.prefix || 'mtrl'),
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Manages the touch interaction state for the component.
|
|
65
|
-
* This helps track touch gestures and interactions.
|
|
66
|
-
*/
|
|
67
|
-
touchState: {
|
|
68
|
-
startTime: 0,
|
|
69
|
-
startPosition: { x: 0, y: 0 },
|
|
70
|
-
isTouching: false,
|
|
71
|
-
activeTarget: null
|
|
72
|
-
},
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Updates the component's touch state based on user interactions.
|
|
76
|
-
* Tracks touch position and timing for gesture recognition.
|
|
77
|
-
*/
|
|
78
|
-
updateTouchState (event, status) {
|
|
79
|
-
const normalized = normalizeEvent(event)
|
|
80
|
-
|
|
81
|
-
if (status === 'start') {
|
|
82
|
-
this.touchState = {
|
|
83
|
-
startTime: Date.now(),
|
|
84
|
-
startPosition: {
|
|
85
|
-
x: normalized.clientX,
|
|
86
|
-
y: normalized.clientY
|
|
87
|
-
},
|
|
88
|
-
isTouching: true,
|
|
89
|
-
activeTarget: normalized.target
|
|
90
|
-
}
|
|
91
|
-
} else if (status === 'end') {
|
|
92
|
-
this.touchState.isTouching = false
|
|
93
|
-
this.touchState.activeTarget = null
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Higher-order function that adds a DOM element to a component
|
|
100
|
-
* @param {Object} options - Element creation options
|
|
101
|
-
* @param {string} [options.tag='div'] - HTML tag name
|
|
102
|
-
* @param {string} [options.componentName] - Component name for class generation
|
|
103
|
-
* @param {Object} [options.attrs] - HTML attributes
|
|
104
|
-
* @param {string|string[]} [options.className] - Additional CSS classes
|
|
105
|
-
* @param {Object} [options.forwardEvents] - Native events to forward to component events
|
|
106
|
-
* @returns {Function} Component enhancer
|
|
107
|
-
* @example
|
|
108
|
-
* pipe(
|
|
109
|
-
* createBase,
|
|
110
|
-
* withElement({
|
|
111
|
-
* tag: 'button',
|
|
112
|
-
* componentName: 'button',
|
|
113
|
-
* attrs: { type: 'button' },
|
|
114
|
-
* forwardEvents: {
|
|
115
|
-
* click: component => !component.element.disabled
|
|
116
|
-
* }
|
|
117
|
-
* })
|
|
118
|
-
* )({ prefix: 'app' })
|
|
119
|
-
*/
|
|
120
|
-
export const withElement = (options = {}) => (base) => {
|
|
121
|
-
/**
|
|
122
|
-
* Handles the start of a touch interaction.
|
|
123
|
-
* Initializes touch tracking and provides visual feedback.
|
|
124
|
-
*/
|
|
125
|
-
const handleTouchStart = (event) => {
|
|
126
|
-
base.updateTouchState(event, 'start')
|
|
127
|
-
element.classList.add(`${base.getClass('touch-active')}`)
|
|
128
|
-
|
|
129
|
-
if (options.forwardEvents?.touchstart) {
|
|
130
|
-
base.emit?.('touchstart', normalizeEvent(event))
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Handles the end of a touch interaction.
|
|
136
|
-
* Detects taps and cleans up touch state.
|
|
137
|
-
*/
|
|
138
|
-
const handleTouchEnd = (event) => {
|
|
139
|
-
if (!base.touchState.isTouching) return
|
|
140
|
-
|
|
141
|
-
const touchDuration = Date.now() - base.touchState.startTime
|
|
142
|
-
element.classList.remove(`${base.getClass('touch-active')}`)
|
|
143
|
-
base.updateTouchState(event, 'end')
|
|
144
|
-
|
|
145
|
-
// Emit tap event for short touches
|
|
146
|
-
if (touchDuration < TOUCH_CONFIG.TAP_THRESHOLD) {
|
|
147
|
-
base.emit?.('tap', normalizeEvent(event))
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (options.forwardEvents?.touchend) {
|
|
151
|
-
base.emit?.('touchend', normalizeEvent(event))
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Handles touch movement.
|
|
157
|
-
* Detects swipes and other gesture-based interactions.
|
|
158
|
-
*/
|
|
159
|
-
const handleTouchMove = (event) => {
|
|
160
|
-
if (!base.touchState.isTouching) return
|
|
161
|
-
|
|
162
|
-
const normalized = normalizeEvent(event)
|
|
163
|
-
const deltaX = normalized.clientX - base.touchState.startPosition.x
|
|
164
|
-
const deltaY = normalized.clientY - base.touchState.startPosition.y
|
|
165
|
-
|
|
166
|
-
// Detect and emit swipe gestures
|
|
167
|
-
if (Math.abs(deltaX) > TOUCH_CONFIG.SWIPE_THRESHOLD) {
|
|
168
|
-
base.emit?.('swipe', {
|
|
169
|
-
direction: deltaX > 0 ? 'right' : 'left',
|
|
170
|
-
deltaX,
|
|
171
|
-
deltaY
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (options.forwardEvents?.touchmove) {
|
|
176
|
-
base.emit?.('touchmove', { ...normalized, deltaX, deltaY })
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Create the element with appropriate classes
|
|
181
|
-
const element = createElement({
|
|
182
|
-
...options,
|
|
183
|
-
className: [
|
|
184
|
-
base.getClass(options.componentName || base.componentName || 'component'),
|
|
185
|
-
hasTouchSupport() && options.interactive ? base.getClass('interactive') : null,
|
|
186
|
-
options.className
|
|
187
|
-
].filter(Boolean),
|
|
188
|
-
context: base
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
// Add event listeners only if touch is supported and the component is interactive
|
|
192
|
-
if (hasTouchSupport() && options.interactive) {
|
|
193
|
-
element.addEventListener('touchstart', handleTouchStart, PASSIVE_EVENTS)
|
|
194
|
-
element.addEventListener('touchend', handleTouchEnd)
|
|
195
|
-
element.addEventListener('touchmove', handleTouchMove, PASSIVE_EVENTS)
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return {
|
|
199
|
-
...base,
|
|
200
|
-
element,
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Adds CSS classes to the element
|
|
204
|
-
* @param {...string} classes - CSS classes to add
|
|
205
|
-
* @returns {Object} Component instance for chaining
|
|
206
|
-
*/
|
|
207
|
-
addClass (...classes) {
|
|
208
|
-
element.classList.add(...classes.filter(Boolean))
|
|
209
|
-
return this
|
|
210
|
-
},
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Removes the element and cleans up event listeners.
|
|
214
|
-
* Ensures proper resource cleanup when the component is destroyed.
|
|
215
|
-
*/
|
|
216
|
-
destroy () {
|
|
217
|
-
if (hasTouchSupport() && options.interactive) {
|
|
218
|
-
element.removeEventListener('touchstart', handleTouchStart)
|
|
219
|
-
element.removeEventListener('touchend', handleTouchEnd)
|
|
220
|
-
element.removeEventListener('touchmove', handleTouchMove)
|
|
221
|
-
}
|
|
222
|
-
element.remove()
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/checkable.js
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Adds checked state management to a component with an input
|
|
5
|
-
* Manages visual state and event emission for checked changes
|
|
6
|
-
*
|
|
7
|
-
* @param {Object} config - Checkable configuration
|
|
8
|
-
* @param {boolean} [config.checked] - Initial checked state
|
|
9
|
-
*
|
|
10
|
-
* @returns {Function} Component transformer that adds checkable functionality
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* const component = pipe(
|
|
14
|
-
* createBase,
|
|
15
|
-
* withEvents(),
|
|
16
|
-
* withInput(config),
|
|
17
|
-
* withCheckable({ checked: true })
|
|
18
|
-
* )(config);
|
|
19
|
-
*
|
|
20
|
-
* // Use the checkable API
|
|
21
|
-
* component.checkable.toggle();
|
|
22
|
-
* component.checkable.check();
|
|
23
|
-
* component.checkable.uncheck();
|
|
24
|
-
*
|
|
25
|
-
* // Listen for changes
|
|
26
|
-
* component.on('change', ({ checked }) => {
|
|
27
|
-
* console.log('State changed:', checked);
|
|
28
|
-
* });
|
|
29
|
-
*/
|
|
30
|
-
export const withCheckable = (config = {}) => (component) => {
|
|
31
|
-
if (!component.input) return component
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Updates component classes to reflect checked state
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
const updateStateClasses = () => {
|
|
38
|
-
component.element.classList.toggle(
|
|
39
|
-
`${component.getClass('switch')}--checked`,
|
|
40
|
-
component.input.checked
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Set initial state
|
|
45
|
-
if (config.checked) {
|
|
46
|
-
component.input.checked = true
|
|
47
|
-
updateStateClasses()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Update classes whenever checked state changes
|
|
51
|
-
component.on('change', updateStateClasses)
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
...component,
|
|
55
|
-
checkable: {
|
|
56
|
-
/**
|
|
57
|
-
* Sets the checked state to true
|
|
58
|
-
* Emits change event if state changes
|
|
59
|
-
* @returns {Object} Checkable interface
|
|
60
|
-
*/
|
|
61
|
-
check () {
|
|
62
|
-
if (!component.input.checked) {
|
|
63
|
-
component.input.checked = true
|
|
64
|
-
updateStateClasses()
|
|
65
|
-
component.emit('change', {
|
|
66
|
-
checked: true,
|
|
67
|
-
value: component.input.value
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
return this
|
|
71
|
-
},
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Sets the checked state to false
|
|
75
|
-
* Emits change event if state changes
|
|
76
|
-
* @returns {Object} Checkable interface
|
|
77
|
-
*/
|
|
78
|
-
uncheck () {
|
|
79
|
-
if (component.input.checked) {
|
|
80
|
-
component.input.checked = false
|
|
81
|
-
updateStateClasses()
|
|
82
|
-
component.emit('change', {
|
|
83
|
-
checked: false,
|
|
84
|
-
value: component.input.value
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
return this
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Toggles the current checked state
|
|
92
|
-
* Always emits change event
|
|
93
|
-
* @returns {Object} Checkable interface
|
|
94
|
-
*/
|
|
95
|
-
toggle () {
|
|
96
|
-
component.input.checked = !component.input.checked
|
|
97
|
-
updateStateClasses()
|
|
98
|
-
component.emit('change', {
|
|
99
|
-
checked: component.input.checked,
|
|
100
|
-
value: component.input.value
|
|
101
|
-
})
|
|
102
|
-
return this
|
|
103
|
-
},
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Gets the current checked state
|
|
107
|
-
* @returns {boolean} Whether component is checked
|
|
108
|
-
*/
|
|
109
|
-
isChecked () {
|
|
110
|
-
return component.input.checked
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/disabled.js
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Adds disabled state management to a component
|
|
5
|
-
* @param {Object} config - Disabled configuration
|
|
6
|
-
* @returns {Function} Component enhancer
|
|
7
|
-
*/
|
|
8
|
-
export const withDisabled = (config = {}) => (component) => {
|
|
9
|
-
// Get the disabled class based on component name
|
|
10
|
-
const disabledClass = `${component.getClass(component.componentName)}--disabled`
|
|
11
|
-
|
|
12
|
-
// Directly implement disabled functionality
|
|
13
|
-
const disabled = {
|
|
14
|
-
enable () {
|
|
15
|
-
component.element.classList.remove(disabledClass)
|
|
16
|
-
if (component.input) {
|
|
17
|
-
component.input.disabled = false
|
|
18
|
-
component.input.removeAttribute('disabled')
|
|
19
|
-
} else {
|
|
20
|
-
component.element.disabled = false
|
|
21
|
-
component.element.removeAttribute('disabled')
|
|
22
|
-
}
|
|
23
|
-
return this
|
|
24
|
-
},
|
|
25
|
-
|
|
26
|
-
disable () {
|
|
27
|
-
component.element.classList.add(disabledClass)
|
|
28
|
-
if (component.input) {
|
|
29
|
-
component.input.disabled = true
|
|
30
|
-
component.input.setAttribute('disabled', 'true')
|
|
31
|
-
} else {
|
|
32
|
-
component.element.disabled = true
|
|
33
|
-
component.element.setAttribute('disabled', 'true')
|
|
34
|
-
}
|
|
35
|
-
return this
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
toggle () {
|
|
39
|
-
if (this.isDisabled()) {
|
|
40
|
-
this.enable()
|
|
41
|
-
} else {
|
|
42
|
-
this.disable()
|
|
43
|
-
}
|
|
44
|
-
return this
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
isDisabled () {
|
|
48
|
-
return component.input ? component.input.disabled : component.element.disabled
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Initialize disabled state if configured
|
|
53
|
-
if (config.disabled) {
|
|
54
|
-
// Use requestAnimationFrame to ensure DOM is ready
|
|
55
|
-
requestAnimationFrame(() => {
|
|
56
|
-
disabled.disable()
|
|
57
|
-
})
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
return {
|
|
61
|
-
...component,
|
|
62
|
-
disabled
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/withEvents.js
|
|
2
|
-
/**
|
|
3
|
-
* @module core/compose/features
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { createEmitter } from '../../state/emitter'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Adds event handling capabilities to a component
|
|
10
|
-
* @memberof module:core/compose/features
|
|
11
|
-
* @function withEvents
|
|
12
|
-
* @param {HTMLElement} [target] - Event target element
|
|
13
|
-
* @returns {Function} Component transformer
|
|
14
|
-
* @example
|
|
15
|
-
* const button = pipe(
|
|
16
|
-
* createBase({ componentName: 'button' }),
|
|
17
|
-
* withElement(),
|
|
18
|
-
* withEvents()
|
|
19
|
-
* )({})
|
|
20
|
-
*
|
|
21
|
-
* button.on('click', () => console.log('clicked'))
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Adds event handling capabilities to a component
|
|
26
|
-
* Returns event system ready to use immediately
|
|
27
|
-
*/
|
|
28
|
-
export const withEvents = () => (component) => {
|
|
29
|
-
const emitter = createEmitter()
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
...component,
|
|
33
|
-
on (event, handler) {
|
|
34
|
-
emitter.on(event, handler)
|
|
35
|
-
return this
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
off (event, handler) {
|
|
39
|
-
emitter.off(event, handler)
|
|
40
|
-
return this
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
emit (event, data) {
|
|
44
|
-
emitter.emit(event, data)
|
|
45
|
-
return this
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/icon.js
|
|
2
|
-
import { createIcon } from '../../../core/build/icon'
|
|
3
|
-
|
|
4
|
-
const updateCircularStyle = (component, config) => {
|
|
5
|
-
const hasText = config.text
|
|
6
|
-
const hasIcon = config.icon
|
|
7
|
-
|
|
8
|
-
const circularClass = `${component.getClass('button')}--circular`
|
|
9
|
-
if (!hasText && hasIcon) {
|
|
10
|
-
component.element.classList.add(circularClass)
|
|
11
|
-
} else {
|
|
12
|
-
component.element.classList.remove(circularClass)
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export const withIcon = (config = {}) => (component) => {
|
|
17
|
-
const icon = createIcon(component.element, {
|
|
18
|
-
prefix: config.prefix,
|
|
19
|
-
type: 'button',
|
|
20
|
-
position: config.iconPosition
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
if (config.icon) {
|
|
24
|
-
icon.setIcon(config.icon)
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
updateCircularStyle(component, config)
|
|
28
|
-
|
|
29
|
-
return {
|
|
30
|
-
...component,
|
|
31
|
-
icon
|
|
32
|
-
}
|
|
33
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/index.js
|
|
2
|
-
|
|
3
|
-
// Core features
|
|
4
|
-
export { withEvents } from './events'
|
|
5
|
-
export { withText } from './text'
|
|
6
|
-
export { withIcon } from './icon'
|
|
7
|
-
export { withVariant } from './variant'
|
|
8
|
-
export { withSize } from './size'
|
|
9
|
-
export { withPosition } from './position'
|
|
10
|
-
export { withInput } from './input'
|
|
11
|
-
export { withTrack } from './track'
|
|
12
|
-
export { withTextInput } from './textinput'
|
|
13
|
-
export { withTextLabel } from './textlabel'
|
|
14
|
-
export { withRipple } from './ripple'
|
|
15
|
-
|
|
16
|
-
// State management features
|
|
17
|
-
export { withDisabled } from './disabled'
|
|
18
|
-
export { withCheckable } from './checkable'
|
|
19
|
-
|
|
20
|
-
export { withLifecycle } from './lifecycle'
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/input.js
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Creates an input element and adds it to a component
|
|
5
|
-
* Handles both input creation and event emission for state changes
|
|
6
|
-
*
|
|
7
|
-
* @param {Object} config - Input configuration
|
|
8
|
-
* @param {string} [config.name] - Input name attribute
|
|
9
|
-
* @param {boolean} [config.checked] - Initial checked state
|
|
10
|
-
* @param {boolean} [config.required] - Whether input is required
|
|
11
|
-
* @param {boolean} [config.disabled] - Whether input is disabled
|
|
12
|
-
* @param {string} [config.value='on'] - Input value attribute
|
|
13
|
-
* @param {string} [config.label] - Accessibility label text
|
|
14
|
-
* @param {string} [config.ariaLabel] - Alternative accessibility label
|
|
15
|
-
*
|
|
16
|
-
* @returns {Function} Component transformer that adds input functionality
|
|
17
|
-
*/
|
|
18
|
-
export const withInput = (config = {}) => (component) => {
|
|
19
|
-
const input = document.createElement('input')
|
|
20
|
-
const name = component.componentName
|
|
21
|
-
input.type = 'checkbox'
|
|
22
|
-
input.className = `${component.getClass(name)}-input`
|
|
23
|
-
|
|
24
|
-
// Ensure input can receive focus
|
|
25
|
-
input.style.position = 'absolute'
|
|
26
|
-
input.style.opacity = '0'
|
|
27
|
-
input.style.cursor = 'pointer'
|
|
28
|
-
// Don't use display: none or visibility: hidden as they prevent focus
|
|
29
|
-
|
|
30
|
-
// The input itself should be focusable, not the wrapper
|
|
31
|
-
component.element.setAttribute('role', 'presentation')
|
|
32
|
-
input.setAttribute('role', name)
|
|
33
|
-
|
|
34
|
-
const attributes = {
|
|
35
|
-
name: config.name,
|
|
36
|
-
checked: config.checked,
|
|
37
|
-
required: config.required,
|
|
38
|
-
disabled: config.disabled,
|
|
39
|
-
value: config.value || 'on',
|
|
40
|
-
'aria-label': config.label || config.ariaLabel
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
Object.entries(attributes).forEach(([key, value]) => {
|
|
44
|
-
if (value !== null && value !== undefined) {
|
|
45
|
-
if (key === 'disabled' && value === true) {
|
|
46
|
-
input.disabled = true
|
|
47
|
-
input.setAttribute('disabled', 'true')
|
|
48
|
-
// Note: We don't add the class here because that's handled by withDisabled
|
|
49
|
-
} else if (value === true) {
|
|
50
|
-
input.setAttribute(key, key)
|
|
51
|
-
} else {
|
|
52
|
-
input.setAttribute(key, value)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
// Bridge native checkbox events to our event system
|
|
58
|
-
input.addEventListener('change', (event) => {
|
|
59
|
-
component.emit('change', {
|
|
60
|
-
checked: input.checked,
|
|
61
|
-
value: input.value,
|
|
62
|
-
nativeEvent: event
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
// Add keyboard handling
|
|
67
|
-
input.addEventListener('keydown', (event) => {
|
|
68
|
-
if (event.key === ' ' || event.key === 'Enter') {
|
|
69
|
-
event.preventDefault()
|
|
70
|
-
if (!input.disabled) {
|
|
71
|
-
input.checked = !input.checked
|
|
72
|
-
input.dispatchEvent(new Event('change', { bubbles: true }))
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
component.element.appendChild(input)
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
...component,
|
|
81
|
-
input,
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Gets the current input value
|
|
85
|
-
* @returns {string} Current value
|
|
86
|
-
*/
|
|
87
|
-
getValue: () => input.value,
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Sets the input value and emits a value event
|
|
91
|
-
* @param {string} value - New value to set
|
|
92
|
-
* @returns {Object} Component instance
|
|
93
|
-
*/
|
|
94
|
-
setValue (value) {
|
|
95
|
-
input.value = value
|
|
96
|
-
component.emit('value', { value })
|
|
97
|
-
return this
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
// src/core/compose/features/withLifecycle.js
|
|
2
|
-
import { createEmitter } from '../../state/emitter'
|
|
3
|
-
|
|
4
|
-
export const withLifecycle = () => (component) => {
|
|
5
|
-
if (!component.element) return component
|
|
6
|
-
|
|
7
|
-
let mounted = false
|
|
8
|
-
const emitter = createEmitter()
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
...component,
|
|
12
|
-
lifecycle: {
|
|
13
|
-
// Mount/Unmount state management
|
|
14
|
-
onMount: (handler) => emitter.on('mount', handler),
|
|
15
|
-
onUnmount: (handler) => emitter.on('unmount', handler),
|
|
16
|
-
|
|
17
|
-
mount: () => {
|
|
18
|
-
if (!mounted) {
|
|
19
|
-
mounted = true
|
|
20
|
-
emitter.emit('mount')
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
unmount: () => {
|
|
25
|
-
if (mounted) {
|
|
26
|
-
mounted = false
|
|
27
|
-
emitter.emit('unmount')
|
|
28
|
-
emitter.clear()
|
|
29
|
-
}
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
isMounted: () => mounted,
|
|
33
|
-
|
|
34
|
-
// Cleanup and destruction
|
|
35
|
-
destroy () {
|
|
36
|
-
// First trigger unmount
|
|
37
|
-
if (mounted) {
|
|
38
|
-
this.unmount()
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Clean up all event listeners
|
|
42
|
-
if (component.events) {
|
|
43
|
-
component.events.destroy()
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// Clean up text element
|
|
47
|
-
if (component.text) {
|
|
48
|
-
const textElement = component.text.getElement()
|
|
49
|
-
if (textElement) {
|
|
50
|
-
textElement.remove()
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Clean up icon element
|
|
55
|
-
if (component.icon) {
|
|
56
|
-
const iconElement = component.icon.getElement()
|
|
57
|
-
if (iconElement) {
|
|
58
|
-
iconElement.remove()
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Remove the main element
|
|
63
|
-
if (component.element) {
|
|
64
|
-
component.element.remove()
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|