mtrl 0.2.3 → 0.2.4
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/core/build/_ripple.scss +79 -0
- package/src/core/build/constants.ts +48 -0
- package/src/core/build/icon.ts +137 -0
- package/src/core/build/ripple.ts +216 -0
- package/src/core/build/text.ts +91 -0
package/package.json
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/components/ripple/_ripple.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}-ripple';
|
|
9
|
+
|
|
10
|
+
.#{$component} {
|
|
11
|
+
// Ripple container
|
|
12
|
+
position: absolute;
|
|
13
|
+
top: 0;
|
|
14
|
+
left: 0;
|
|
15
|
+
right: 0;
|
|
16
|
+
bottom: 0;
|
|
17
|
+
overflow: hidden;
|
|
18
|
+
border-radius: inherit;
|
|
19
|
+
pointer-events: none;
|
|
20
|
+
z-index: 0;
|
|
21
|
+
|
|
22
|
+
// Ripple element
|
|
23
|
+
&-wave {
|
|
24
|
+
position: absolute;
|
|
25
|
+
border-radius: 50%;
|
|
26
|
+
background-color: currentColor;
|
|
27
|
+
transform: scale(0);
|
|
28
|
+
opacity: 0;
|
|
29
|
+
pointer-events: none;
|
|
30
|
+
will-change: transform, opacity;
|
|
31
|
+
|
|
32
|
+
// Animation
|
|
33
|
+
transition-property: transform, opacity;
|
|
34
|
+
transition-duration: v.motion('duration-short4');
|
|
35
|
+
transition-timing-function: v.motion('easing-standard');
|
|
36
|
+
|
|
37
|
+
// Active ripple
|
|
38
|
+
&.active {
|
|
39
|
+
transform: scale(1);
|
|
40
|
+
opacity: v.state('hover-state-layer-opacity');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
&.fade-out {
|
|
44
|
+
opacity: 0;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Standalone utility for adding ripple to any element
|
|
50
|
+
[data-ripple] {
|
|
51
|
+
position: relative;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
|
|
54
|
+
&::after {
|
|
55
|
+
content: '';
|
|
56
|
+
position: absolute;
|
|
57
|
+
top: 0;
|
|
58
|
+
left: 0;
|
|
59
|
+
right: 0;
|
|
60
|
+
bottom: 0;
|
|
61
|
+
z-index: 0;
|
|
62
|
+
pointer-events: none;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Handle ripple color based on data attribute
|
|
66
|
+
&[data-ripple="light"]::after {
|
|
67
|
+
background-color: rgba(255, 255, 255, 0.3);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
&[data-ripple="dark"]::after {
|
|
71
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Make content appear above ripple
|
|
75
|
+
> * {
|
|
76
|
+
position: relative;
|
|
77
|
+
z-index: 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/core/build/constants.ts
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Animation timing functions for ripple effect
|
|
5
|
+
*/
|
|
6
|
+
export enum RIPPLE_TIMING {
|
|
7
|
+
LINEAR = 'linear',
|
|
8
|
+
EASE = 'ease',
|
|
9
|
+
EASE_IN = 'ease-in',
|
|
10
|
+
EASE_OUT = 'ease-out',
|
|
11
|
+
EASE_IN_OUT = 'ease-in-out',
|
|
12
|
+
MATERIAL = 'cubic-bezier(0.4, 0.0, 0.2, 1)'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Default configuration for ripple effect
|
|
17
|
+
*/
|
|
18
|
+
export const RIPPLE_CONFIG = {
|
|
19
|
+
duration: 375,
|
|
20
|
+
timing: RIPPLE_TIMING.LINEAR,
|
|
21
|
+
opacity: ['1', '0.3'] as [string, string]
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validation schema for ripple configuration
|
|
26
|
+
*/
|
|
27
|
+
export const RIPPLE_SCHEMA = {
|
|
28
|
+
duration: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
minimum: 0,
|
|
31
|
+
default: RIPPLE_CONFIG.duration
|
|
32
|
+
},
|
|
33
|
+
timing: {
|
|
34
|
+
type: 'string',
|
|
35
|
+
enum: Object.values(RIPPLE_TIMING),
|
|
36
|
+
default: RIPPLE_CONFIG.timing
|
|
37
|
+
},
|
|
38
|
+
opacity: {
|
|
39
|
+
type: 'array',
|
|
40
|
+
items: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
pattern: '^[0-1](\\.\\d+)?$'
|
|
43
|
+
},
|
|
44
|
+
minItems: 2,
|
|
45
|
+
maxItems: 2,
|
|
46
|
+
default: RIPPLE_CONFIG.opacity
|
|
47
|
+
}
|
|
48
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// src/core/build/icon.ts
|
|
2
|
+
/**
|
|
3
|
+
* @module core/build
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for creating an icon element
|
|
8
|
+
*/
|
|
9
|
+
export interface IconElementOptions {
|
|
10
|
+
/**
|
|
11
|
+
* CSS class prefix
|
|
12
|
+
*/
|
|
13
|
+
prefix?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Additional CSS class
|
|
17
|
+
*/
|
|
18
|
+
class?: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Icon size variant
|
|
22
|
+
*/
|
|
23
|
+
size?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Configuration for icon manager
|
|
28
|
+
*/
|
|
29
|
+
export interface IconConfig {
|
|
30
|
+
/**
|
|
31
|
+
* CSS class prefix
|
|
32
|
+
*/
|
|
33
|
+
prefix?: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Component type
|
|
37
|
+
*/
|
|
38
|
+
type?: string;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Icon position ('start' or 'end')
|
|
42
|
+
*/
|
|
43
|
+
position?: 'start' | 'end';
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Icon size
|
|
47
|
+
*/
|
|
48
|
+
iconSize?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Icon manager interface
|
|
53
|
+
*/
|
|
54
|
+
export interface IconManager {
|
|
55
|
+
/**
|
|
56
|
+
* Sets icon HTML content
|
|
57
|
+
* @param html - Icon HTML content
|
|
58
|
+
* @returns IconManager instance for chaining
|
|
59
|
+
*/
|
|
60
|
+
setIcon: (html: string) => IconManager;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Gets current icon HTML content
|
|
64
|
+
* @returns Current icon HTML
|
|
65
|
+
*/
|
|
66
|
+
getIcon: () => string;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Gets icon element
|
|
70
|
+
* @returns Icon element or null if not created
|
|
71
|
+
*/
|
|
72
|
+
getElement: () => HTMLElement | null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Creates an icon DOM element
|
|
77
|
+
*
|
|
78
|
+
* @param html - Icon HTML content
|
|
79
|
+
* @param options - Icon options
|
|
80
|
+
* @returns Icon element
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
const createIconElement = (html: string, options: IconElementOptions = {}): HTMLElement => {
|
|
84
|
+
const PREFIX = options.prefix || 'mtrl';
|
|
85
|
+
const element = document.createElement('span');
|
|
86
|
+
element.className = `${PREFIX}-icon`;
|
|
87
|
+
|
|
88
|
+
if (options.class) {
|
|
89
|
+
element.classList.add(options.class);
|
|
90
|
+
}
|
|
91
|
+
if (options.size) {
|
|
92
|
+
element.classList.add(`${PREFIX}-icon--${options.size}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
element.innerHTML = html;
|
|
96
|
+
return element;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Creates an icon manager for a component
|
|
101
|
+
*
|
|
102
|
+
* @param element - Parent element
|
|
103
|
+
* @param config - Icon configuration
|
|
104
|
+
* @returns Icon manager interface
|
|
105
|
+
*/
|
|
106
|
+
export const createIcon = (element: HTMLElement, config: IconConfig = {}): IconManager => {
|
|
107
|
+
let iconElement: HTMLElement | null = null;
|
|
108
|
+
const PREFIX = config.prefix || 'mtrl';
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
setIcon(html: string): IconManager {
|
|
112
|
+
if (!iconElement && html) {
|
|
113
|
+
iconElement = createIconElement(html, {
|
|
114
|
+
prefix: PREFIX,
|
|
115
|
+
class: `${PREFIX}-${config.type || 'component'}-icon`,
|
|
116
|
+
size: config.iconSize
|
|
117
|
+
});
|
|
118
|
+
if (config.position === 'end') {
|
|
119
|
+
element.appendChild(iconElement);
|
|
120
|
+
} else {
|
|
121
|
+
element.insertBefore(iconElement, element.firstChild);
|
|
122
|
+
}
|
|
123
|
+
} else if (iconElement && html) {
|
|
124
|
+
iconElement.innerHTML = html;
|
|
125
|
+
}
|
|
126
|
+
return this;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
getIcon(): string {
|
|
130
|
+
return iconElement ? iconElement.innerHTML : '';
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
getElement(): HTMLElement | null {
|
|
134
|
+
return iconElement;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// src/core/build/ripple.ts
|
|
2
|
+
|
|
3
|
+
import { RIPPLE_CONFIG, RIPPLE_TIMING } from './constants';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Ripple animation configuration
|
|
7
|
+
*/
|
|
8
|
+
export interface RippleConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Animation duration in milliseconds
|
|
11
|
+
*/
|
|
12
|
+
duration?: number;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Animation timing function
|
|
16
|
+
*/
|
|
17
|
+
timing?: string;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Opacity start and end values
|
|
21
|
+
*/
|
|
22
|
+
opacity?: [string, string];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* End coordinates for ripple animation
|
|
27
|
+
*/
|
|
28
|
+
interface EndCoordinates {
|
|
29
|
+
size: string;
|
|
30
|
+
top: string;
|
|
31
|
+
left: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Document event listener
|
|
36
|
+
*/
|
|
37
|
+
interface DocumentListener {
|
|
38
|
+
event: string;
|
|
39
|
+
handler: EventListener;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Ripple controller interface
|
|
44
|
+
*/
|
|
45
|
+
export interface RippleController {
|
|
46
|
+
/**
|
|
47
|
+
* Attaches ripple effect to an element
|
|
48
|
+
* @param element - Target element
|
|
49
|
+
*/
|
|
50
|
+
mount: (element: HTMLElement) => void;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Removes ripple effect from an element
|
|
54
|
+
* @param element - Target element
|
|
55
|
+
*/
|
|
56
|
+
unmount: (element: HTMLElement) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Creates a ripple effect instance
|
|
61
|
+
*
|
|
62
|
+
* @param config - Ripple configuration
|
|
63
|
+
* @returns Ripple controller instance
|
|
64
|
+
*/
|
|
65
|
+
export const createRipple = (config: RippleConfig = {}): RippleController => {
|
|
66
|
+
// Make sure we fully merge the config options
|
|
67
|
+
const options = {
|
|
68
|
+
...RIPPLE_CONFIG,
|
|
69
|
+
...config,
|
|
70
|
+
// Handle nested objects like opacity array
|
|
71
|
+
opacity: config.opacity || RIPPLE_CONFIG.opacity
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const getEndCoordinates = (bounds: DOMRect): EndCoordinates => {
|
|
75
|
+
const size = Math.max(bounds.width, bounds.height);
|
|
76
|
+
const top = bounds.height > bounds.width
|
|
77
|
+
? -bounds.height / 2
|
|
78
|
+
: -(bounds.width - bounds.height / 2);
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
size: `${size * 2}px`,
|
|
82
|
+
top: `${top}px`,
|
|
83
|
+
left: `${size / -2}px`
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const createRippleElement = (): HTMLDivElement => {
|
|
88
|
+
const ripple = document.createElement('div');
|
|
89
|
+
ripple.className = 'ripple';
|
|
90
|
+
// Initial styles already set in CSS
|
|
91
|
+
ripple.style.transition = `all ${options.duration}ms ${options.timing}`;
|
|
92
|
+
return ripple;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Store document event listeners for cleanup
|
|
96
|
+
let documentListeners: DocumentListener[] = [];
|
|
97
|
+
|
|
98
|
+
// Safe document event handling
|
|
99
|
+
const addDocumentListener = (event: string, handler: EventListener): void => {
|
|
100
|
+
if (typeof document.addEventListener === 'function') {
|
|
101
|
+
document.addEventListener(event, handler);
|
|
102
|
+
documentListeners.push({ event, handler });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const removeDocumentListener = (event: string, handler: EventListener): void => {
|
|
107
|
+
if (typeof document.removeEventListener === 'function') {
|
|
108
|
+
document.removeEventListener(event, handler);
|
|
109
|
+
documentListeners = documentListeners.filter(
|
|
110
|
+
listener => !(listener.event === event && listener.handler === handler)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const animate = (event: MouseEvent, container: HTMLElement): void => {
|
|
116
|
+
if (!container) return;
|
|
117
|
+
|
|
118
|
+
const bounds = container.getBoundingClientRect();
|
|
119
|
+
const ripple = createRippleElement();
|
|
120
|
+
|
|
121
|
+
// Set initial position and state
|
|
122
|
+
Object.assign(ripple.style, {
|
|
123
|
+
left: `${event.offsetX || bounds.width / 2}px`,
|
|
124
|
+
top: `${event.offsetY || bounds.height / 2}px`,
|
|
125
|
+
transform: 'scale(0)',
|
|
126
|
+
opacity: options.opacity[0]
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
container.appendChild(ripple);
|
|
130
|
+
|
|
131
|
+
// Force reflow
|
|
132
|
+
// eslint-disable-next-line no-unused-expressions
|
|
133
|
+
ripple.offsetHeight;
|
|
134
|
+
|
|
135
|
+
// Animate to end position
|
|
136
|
+
const end = getEndCoordinates(bounds);
|
|
137
|
+
Object.assign(ripple.style, {
|
|
138
|
+
...end,
|
|
139
|
+
transform: 'scale(1)',
|
|
140
|
+
opacity: options.opacity[1]
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const cleanup = () => {
|
|
144
|
+
ripple.style.opacity = '0';
|
|
145
|
+
|
|
146
|
+
// Use setTimeout to remove element after animation
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
if (ripple.parentNode) {
|
|
149
|
+
ripple.parentNode.removeChild(ripple);
|
|
150
|
+
}
|
|
151
|
+
}, options.duration);
|
|
152
|
+
|
|
153
|
+
removeDocumentListener('mouseup', cleanup);
|
|
154
|
+
removeDocumentListener('mouseleave', cleanup);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
addDocumentListener('mouseup', cleanup);
|
|
158
|
+
addDocumentListener('mouseleave', cleanup);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
mount: (element: HTMLElement): void => {
|
|
163
|
+
if (!element) return;
|
|
164
|
+
|
|
165
|
+
// Ensure proper positioning context
|
|
166
|
+
const currentPosition = window.getComputedStyle(element).position;
|
|
167
|
+
if (currentPosition === 'static') {
|
|
168
|
+
element.style.position = 'relative';
|
|
169
|
+
}
|
|
170
|
+
element.style.overflow = 'hidden';
|
|
171
|
+
|
|
172
|
+
// Store the mousedown handler to be able to remove it later
|
|
173
|
+
const mousedownHandler = (e: MouseEvent) => animate(e, element);
|
|
174
|
+
|
|
175
|
+
// Store handler reference on the element
|
|
176
|
+
if (!element.__rippleHandlers) {
|
|
177
|
+
element.__rippleHandlers = [];
|
|
178
|
+
}
|
|
179
|
+
element.__rippleHandlers.push(mousedownHandler);
|
|
180
|
+
|
|
181
|
+
element.addEventListener('mousedown', mousedownHandler);
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
unmount: (element: HTMLElement): void => {
|
|
185
|
+
if (!element) return;
|
|
186
|
+
|
|
187
|
+
// Clear document event listeners
|
|
188
|
+
documentListeners.forEach(({ event, handler }) => {
|
|
189
|
+
removeDocumentListener(event, handler);
|
|
190
|
+
});
|
|
191
|
+
documentListeners = [];
|
|
192
|
+
|
|
193
|
+
// Remove event listeners
|
|
194
|
+
if (element.__rippleHandlers) {
|
|
195
|
+
element.__rippleHandlers.forEach(handler => {
|
|
196
|
+
element.removeEventListener('mousedown', handler);
|
|
197
|
+
});
|
|
198
|
+
element.__rippleHandlers = [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Remove all ripple elements
|
|
202
|
+
const ripples = element.querySelectorAll('.ripple');
|
|
203
|
+
ripples.forEach(ripple => {
|
|
204
|
+
// Call remove directly to match the test expectation
|
|
205
|
+
ripple.remove();
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Extend the HTMLElement interface to add rippleHandlers property
|
|
212
|
+
declare global {
|
|
213
|
+
interface HTMLElement {
|
|
214
|
+
__rippleHandlers?: Array<(e: MouseEvent) => void>;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// src/core/build/text.ts
|
|
2
|
+
/**
|
|
3
|
+
* @module core/build
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration for text manager
|
|
8
|
+
*/
|
|
9
|
+
export interface TextConfig {
|
|
10
|
+
/**
|
|
11
|
+
* CSS class prefix
|
|
12
|
+
*/
|
|
13
|
+
prefix?: string;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Component type
|
|
17
|
+
*/
|
|
18
|
+
type?: string;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Element to insert before
|
|
22
|
+
*/
|
|
23
|
+
beforeElement?: HTMLElement;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Text manager interface
|
|
28
|
+
*/
|
|
29
|
+
export interface TextManager {
|
|
30
|
+
/**
|
|
31
|
+
* Sets text content
|
|
32
|
+
* @param text - Text content to set
|
|
33
|
+
* @returns TextManager instance for chaining
|
|
34
|
+
*/
|
|
35
|
+
setText: (text: string) => TextManager;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Gets current text content
|
|
39
|
+
* @returns Current text
|
|
40
|
+
*/
|
|
41
|
+
getText: () => string;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Gets text element
|
|
45
|
+
* @returns Text element or null if not created
|
|
46
|
+
*/
|
|
47
|
+
getElement: () => HTMLElement | null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a text manager for a component
|
|
52
|
+
*
|
|
53
|
+
* @param element - Parent element
|
|
54
|
+
* @param config - Text configuration
|
|
55
|
+
* @returns Text manager interface
|
|
56
|
+
*/
|
|
57
|
+
export const createText = (element: HTMLElement, config: TextConfig = {}): TextManager => {
|
|
58
|
+
let textElement: HTMLElement | null = null;
|
|
59
|
+
const PREFIX = config.prefix || 'mtrl';
|
|
60
|
+
|
|
61
|
+
const createElement = (content: string): HTMLElement => {
|
|
62
|
+
const span = document.createElement('span');
|
|
63
|
+
span.className = `${PREFIX}-${config.type || 'component'}-text`;
|
|
64
|
+
span.textContent = content;
|
|
65
|
+
return span;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
setText(text: string): TextManager {
|
|
70
|
+
if (!textElement && text) {
|
|
71
|
+
textElement = createElement(text);
|
|
72
|
+
if (config.beforeElement) {
|
|
73
|
+
element.insertBefore(textElement, config.beforeElement);
|
|
74
|
+
} else {
|
|
75
|
+
element.appendChild(textElement);
|
|
76
|
+
}
|
|
77
|
+
} else if (textElement) {
|
|
78
|
+
textElement.textContent = text;
|
|
79
|
+
}
|
|
80
|
+
return this;
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
getText(): string {
|
|
84
|
+
return textElement ? textElement.textContent || '' : '';
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
getElement(): HTMLElement | null {
|
|
88
|
+
return textElement;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
};
|