@trustquery/browser 0.1.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/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/trustquery.js +2904 -0
- package/dist/trustquery.js.map +1 -0
- package/package.json +49 -0
- package/src/AutoGrow.js +66 -0
- package/src/BubbleManager.js +219 -0
- package/src/CommandHandlers.js +350 -0
- package/src/CommandScanner.js +285 -0
- package/src/DropdownManager.js +592 -0
- package/src/InteractionHandler.js +225 -0
- package/src/OverlayRenderer.js +241 -0
- package/src/StyleManager.js +523 -0
- package/src/TrustQuery.js +402 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
// StyleManager - Handles all inline styling for textarea, wrapper, and overlay
|
|
2
|
+
// Makes TrustQuery completely self-contained without requiring external CSS
|
|
3
|
+
|
|
4
|
+
export default class StyleManager {
|
|
5
|
+
/**
|
|
6
|
+
* Create style manager
|
|
7
|
+
* @param {Object} options - Theme and style options
|
|
8
|
+
*/
|
|
9
|
+
constructor(options = {}) {
|
|
10
|
+
this.options = {
|
|
11
|
+
// Theme colors
|
|
12
|
+
backgroundColor: options.backgroundColor || '#fff',
|
|
13
|
+
textColor: options.textColor || '#333',
|
|
14
|
+
caretColor: options.caretColor || '#000',
|
|
15
|
+
borderColor: options.borderColor || '#ddd',
|
|
16
|
+
borderColorFocus: options.borderColorFocus || '#4a90e2',
|
|
17
|
+
|
|
18
|
+
// Match colors (can be overridden)
|
|
19
|
+
matchBackgroundColor: options.matchBackgroundColor || 'rgba(74, 144, 226, 0.15)',
|
|
20
|
+
matchTextColor: options.matchTextColor || '#2b6cb0',
|
|
21
|
+
matchHoverBackgroundColor: options.matchHoverBackgroundColor || 'rgba(74, 144, 226, 0.25)',
|
|
22
|
+
|
|
23
|
+
// Font settings (optional, will use textarea's if not specified)
|
|
24
|
+
fontFamily: options.fontFamily || null,
|
|
25
|
+
fontSize: options.fontSize || null,
|
|
26
|
+
lineHeight: options.lineHeight || null,
|
|
27
|
+
|
|
28
|
+
...options
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
console.log('[StyleManager] Initialized with theme:', this.options);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Apply all styles to wrapper, textarea, and overlay
|
|
36
|
+
* @param {HTMLElement} wrapper - Wrapper element
|
|
37
|
+
* @param {HTMLElement} textarea - Textarea element
|
|
38
|
+
* @param {HTMLElement} overlay - Overlay element
|
|
39
|
+
*/
|
|
40
|
+
applyAllStyles(wrapper, textarea, overlay) {
|
|
41
|
+
// Get computed styles from original textarea
|
|
42
|
+
const computedStyle = window.getComputedStyle(textarea);
|
|
43
|
+
|
|
44
|
+
// Apply styles to each element
|
|
45
|
+
this.applyWrapperStyles(wrapper, computedStyle);
|
|
46
|
+
this.applyTextareaStyles(textarea, computedStyle);
|
|
47
|
+
this.applyOverlayStyles(overlay, computedStyle);
|
|
48
|
+
|
|
49
|
+
// Apply focus handlers
|
|
50
|
+
this.setupFocusStyles(wrapper, textarea);
|
|
51
|
+
|
|
52
|
+
console.log('[StyleManager] All styles applied');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Apply wrapper styles (container for both textarea and overlay)
|
|
57
|
+
* @param {HTMLElement} wrapper - Wrapper element
|
|
58
|
+
* @param {CSSStyleDeclaration} computedStyle - Computed styles from textarea
|
|
59
|
+
*/
|
|
60
|
+
applyWrapperStyles(wrapper, computedStyle) {
|
|
61
|
+
Object.assign(wrapper.style, {
|
|
62
|
+
position: 'relative',
|
|
63
|
+
display: 'block',
|
|
64
|
+
width: '100%',
|
|
65
|
+
background: this.options.backgroundColor,
|
|
66
|
+
border: `1px solid ${this.options.borderColor}`,
|
|
67
|
+
borderRadius: '4px',
|
|
68
|
+
boxSizing: 'border-box',
|
|
69
|
+
transition: 'border-color 0.15s ease, box-shadow 0.15s ease'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Apply textarea styles (transparent text, visible caret)
|
|
75
|
+
* @param {HTMLElement} textarea - Textarea element
|
|
76
|
+
* @param {CSSStyleDeclaration} computedStyle - Original computed styles
|
|
77
|
+
*/
|
|
78
|
+
applyTextareaStyles(textarea, computedStyle) {
|
|
79
|
+
// Use provided font settings or fall back to computed/defaults
|
|
80
|
+
const fontFamily = this.options.fontFamily || computedStyle.fontFamily || "'Courier New', monospace";
|
|
81
|
+
const fontSize = this.options.fontSize || computedStyle.fontSize || '14px';
|
|
82
|
+
const lineHeight = this.options.lineHeight || computedStyle.lineHeight || '1.5';
|
|
83
|
+
const padding = computedStyle.padding || '12px';
|
|
84
|
+
|
|
85
|
+
// Store existing transition if any (for FOUC prevention)
|
|
86
|
+
const existingTransition = textarea.style.transition;
|
|
87
|
+
|
|
88
|
+
Object.assign(textarea.style, {
|
|
89
|
+
fontFamily,
|
|
90
|
+
fontSize,
|
|
91
|
+
lineHeight,
|
|
92
|
+
padding,
|
|
93
|
+
border: 'none',
|
|
94
|
+
borderRadius: '0',
|
|
95
|
+
background: 'transparent',
|
|
96
|
+
color: this.options.textColor, // Set color for caret visibility
|
|
97
|
+
WebkitTextFillColor: 'transparent', // Make text transparent but keep caret
|
|
98
|
+
caretColor: this.options.caretColor,
|
|
99
|
+
resize: 'none',
|
|
100
|
+
width: '100%',
|
|
101
|
+
boxSizing: 'border-box',
|
|
102
|
+
position: 'relative',
|
|
103
|
+
zIndex: '0', // Below overlay so hover events reach overlay matches
|
|
104
|
+
whiteSpace: 'pre-wrap',
|
|
105
|
+
wordWrap: 'break-word',
|
|
106
|
+
overflowWrap: 'break-word',
|
|
107
|
+
outline: 'none',
|
|
108
|
+
margin: '0',
|
|
109
|
+
transition: existingTransition // Preserve opacity transition from HTML
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Add CSS to make placeholder visible (not affected by -webkit-text-fill-color)
|
|
113
|
+
this.ensurePlaceholderStyles();
|
|
114
|
+
|
|
115
|
+
// Store computed values for overlay to use
|
|
116
|
+
this._textareaStyles = {
|
|
117
|
+
fontFamily,
|
|
118
|
+
fontSize,
|
|
119
|
+
lineHeight,
|
|
120
|
+
padding
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Apply overlay styles (must match textarea exactly for alignment)
|
|
126
|
+
* @param {HTMLElement} overlay - Overlay element
|
|
127
|
+
* @param {CSSStyleDeclaration} computedStyle - Computed styles from textarea
|
|
128
|
+
*/
|
|
129
|
+
applyOverlayStyles(overlay, computedStyle) {
|
|
130
|
+
// Use the stored textarea styles to ensure perfect alignment
|
|
131
|
+
const { fontFamily, fontSize, lineHeight, padding } = this._textareaStyles;
|
|
132
|
+
|
|
133
|
+
Object.assign(overlay.style, {
|
|
134
|
+
position: 'absolute',
|
|
135
|
+
top: '0',
|
|
136
|
+
left: '0',
|
|
137
|
+
right: '0',
|
|
138
|
+
bottom: '0',
|
|
139
|
+
fontFamily,
|
|
140
|
+
fontSize,
|
|
141
|
+
lineHeight,
|
|
142
|
+
padding,
|
|
143
|
+
color: this.options.textColor,
|
|
144
|
+
pointerEvents: 'none', // Let clicks pass through to textarea (except on match spans with pointer-events: auto)
|
|
145
|
+
overflow: 'hidden',
|
|
146
|
+
whiteSpace: 'pre-wrap',
|
|
147
|
+
wordWrap: 'break-word',
|
|
148
|
+
overflowWrap: 'break-word',
|
|
149
|
+
zIndex: '1', // Above textarea so match spans can receive hover/click events
|
|
150
|
+
boxSizing: 'border-box',
|
|
151
|
+
margin: '0'
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Setup focus styles (apply on focus, remove on blur)
|
|
157
|
+
* @param {HTMLElement} wrapper - Wrapper element
|
|
158
|
+
* @param {HTMLElement} textarea - Textarea element
|
|
159
|
+
*/
|
|
160
|
+
setupFocusStyles(wrapper, textarea) {
|
|
161
|
+
textarea.addEventListener('focus', () => {
|
|
162
|
+
wrapper.style.borderColor = this.options.borderColorFocus;
|
|
163
|
+
wrapper.style.boxShadow = `0 0 0 3px ${this.options.borderColorFocus}1a`; // 1a = 10% opacity
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
textarea.addEventListener('blur', () => {
|
|
167
|
+
wrapper.style.borderColor = this.options.borderColor;
|
|
168
|
+
wrapper.style.boxShadow = 'none';
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Apply match (highlighted word) styles
|
|
174
|
+
* @param {HTMLElement} matchElement - Span element for matched word
|
|
175
|
+
* @param {string} matchType - Type of match (keyword, mention, command, etc.)
|
|
176
|
+
*/
|
|
177
|
+
applyMatchStyles(matchElement, matchType = 'default') {
|
|
178
|
+
// Base match styles
|
|
179
|
+
Object.assign(matchElement.style, {
|
|
180
|
+
pointerEvents: 'auto', // Enable interactions
|
|
181
|
+
cursor: 'pointer',
|
|
182
|
+
padding: '2px 4px',
|
|
183
|
+
margin: '-2px -4px',
|
|
184
|
+
borderRadius: '3px',
|
|
185
|
+
transition: 'background-color 0.15s ease',
|
|
186
|
+
backgroundColor: this.options.matchBackgroundColor,
|
|
187
|
+
color: this.options.matchTextColor
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Hover styles (on mouseover)
|
|
191
|
+
matchElement.addEventListener('mouseenter', () => {
|
|
192
|
+
matchElement.style.backgroundColor = this.options.matchHoverBackgroundColor;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
matchElement.addEventListener('mouseleave', () => {
|
|
196
|
+
matchElement.style.backgroundColor = this.options.matchBackgroundColor;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Apply bubble (tooltip) styles
|
|
202
|
+
* @param {HTMLElement} bubble - Bubble element
|
|
203
|
+
*/
|
|
204
|
+
applyBubbleStyles(bubble) {
|
|
205
|
+
Object.assign(bubble.style, {
|
|
206
|
+
position: 'absolute',
|
|
207
|
+
background: '#ffffff',
|
|
208
|
+
border: `1px solid ${this.options.borderColor}`,
|
|
209
|
+
borderRadius: '6px',
|
|
210
|
+
padding: '0',
|
|
211
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
212
|
+
zIndex: '10000',
|
|
213
|
+
maxWidth: '300px',
|
|
214
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
215
|
+
fontSize: '13px',
|
|
216
|
+
lineHeight: '1.4',
|
|
217
|
+
color: this.options.textColor,
|
|
218
|
+
pointerEvents: 'auto',
|
|
219
|
+
opacity: '1',
|
|
220
|
+
overflow: 'hidden',
|
|
221
|
+
animation: 'tq-bubble-appear 0.15s ease-out'
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Add animation keyframes if not already added
|
|
225
|
+
this.ensureAnimationStyles();
|
|
226
|
+
|
|
227
|
+
// Style the content container
|
|
228
|
+
const contentContainer = bubble.querySelector('.tq-bubble-content');
|
|
229
|
+
if (contentContainer) {
|
|
230
|
+
Object.assign(contentContainer.style, {
|
|
231
|
+
padding: '8px 12px',
|
|
232
|
+
fontSize: '12px',
|
|
233
|
+
lineHeight: '1.4'
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Apply bubble header styles
|
|
240
|
+
* @param {HTMLElement} header - Header container element
|
|
241
|
+
* @param {string} messageState - Message state (error, warning, info)
|
|
242
|
+
*/
|
|
243
|
+
applyBubbleHeaderStyles(header, messageState) {
|
|
244
|
+
const colorMap = {
|
|
245
|
+
'error': '#991b1b',
|
|
246
|
+
'warning': '#92400e',
|
|
247
|
+
'info': '#065f46'
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const bgColorMap = {
|
|
251
|
+
'error': '#fee2e2',
|
|
252
|
+
'warning': '#fef3c7',
|
|
253
|
+
'info': '#d1fae5'
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
const color = colorMap[messageState] || '#2b6cb0';
|
|
257
|
+
const bgColor = bgColorMap[messageState] || '#e0f2fe';
|
|
258
|
+
|
|
259
|
+
Object.assign(header.style, {
|
|
260
|
+
display: 'flex',
|
|
261
|
+
alignItems: 'center',
|
|
262
|
+
gap: '8px',
|
|
263
|
+
padding: '10px 12px',
|
|
264
|
+
backgroundColor: bgColor,
|
|
265
|
+
color: color,
|
|
266
|
+
fontWeight: '600',
|
|
267
|
+
fontSize: '11px',
|
|
268
|
+
borderBottom: `1px solid ${this.options.borderColor}`
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Apply dropdown (menu) styles
|
|
274
|
+
* @param {HTMLElement} dropdown - Dropdown element
|
|
275
|
+
*/
|
|
276
|
+
applyDropdownStyles(dropdown) {
|
|
277
|
+
Object.assign(dropdown.style, {
|
|
278
|
+
position: 'absolute',
|
|
279
|
+
background: '#ffffff',
|
|
280
|
+
border: `1px solid ${this.options.borderColor}`,
|
|
281
|
+
borderRadius: '6px',
|
|
282
|
+
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
|
|
283
|
+
zIndex: '10000',
|
|
284
|
+
minWidth: '150px',
|
|
285
|
+
maxWidth: '300px',
|
|
286
|
+
overflow: 'hidden',
|
|
287
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
288
|
+
fontSize: '14px',
|
|
289
|
+
opacity: '1',
|
|
290
|
+
animation: 'tq-dropdown-appear 0.15s ease-out'
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
this.ensureAnimationStyles();
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Apply dropdown header styles
|
|
298
|
+
* @param {HTMLElement} header - Header container element
|
|
299
|
+
* @param {string} messageState - Message state (error, warning, info)
|
|
300
|
+
*/
|
|
301
|
+
applyDropdownHeaderStyles(header, messageState) {
|
|
302
|
+
const colorMap = {
|
|
303
|
+
'error': '#991b1b',
|
|
304
|
+
'warning': '#92400e',
|
|
305
|
+
'info': '#065f46'
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const bgColorMap = {
|
|
309
|
+
'error': '#fee2e2',
|
|
310
|
+
'warning': '#fef3c7',
|
|
311
|
+
'info': '#d1fae5'
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const color = colorMap[messageState] || '#2b6cb0';
|
|
315
|
+
const bgColor = bgColorMap[messageState] || '#e0f2fe';
|
|
316
|
+
|
|
317
|
+
Object.assign(header.style, {
|
|
318
|
+
display: 'flex',
|
|
319
|
+
alignItems: 'center',
|
|
320
|
+
gap: '8px',
|
|
321
|
+
padding: '10px 12px',
|
|
322
|
+
backgroundColor: bgColor,
|
|
323
|
+
color: color,
|
|
324
|
+
fontWeight: '600',
|
|
325
|
+
fontSize: '11px',
|
|
326
|
+
borderBottom: `1px solid ${this.options.borderColor}`
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Apply dropdown description styles
|
|
332
|
+
* @param {HTMLElement} description - Description element
|
|
333
|
+
* @param {string} messageState - Message state (error, warning, info)
|
|
334
|
+
*/
|
|
335
|
+
applyDropdownDescriptionStyles(description, messageState) {
|
|
336
|
+
Object.assign(description.style, {
|
|
337
|
+
padding: '8px 12px',
|
|
338
|
+
fontSize: '12px',
|
|
339
|
+
lineHeight: '1.5',
|
|
340
|
+
color: '#4a5568',
|
|
341
|
+
backgroundColor: '#f7fafc',
|
|
342
|
+
borderBottom: `1px solid ${this.options.borderColor}`
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Apply dropdown item styles
|
|
348
|
+
* @param {HTMLElement} item - Dropdown item element
|
|
349
|
+
*/
|
|
350
|
+
applyDropdownItemStyles(item) {
|
|
351
|
+
Object.assign(item.style, {
|
|
352
|
+
padding: '8px 12px',
|
|
353
|
+
cursor: 'pointer',
|
|
354
|
+
color: this.options.textColor,
|
|
355
|
+
transition: 'background-color 0.1s ease'
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
item.addEventListener('mouseenter', () => {
|
|
359
|
+
item.style.backgroundColor = '#f0f4f8';
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
item.addEventListener('mouseleave', () => {
|
|
363
|
+
item.style.backgroundColor = 'transparent';
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
item.addEventListener('mousedown', () => {
|
|
367
|
+
item.style.backgroundColor = '#e2e8f0';
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Apply user input styles
|
|
373
|
+
* @param {HTMLElement} container - User input container
|
|
374
|
+
* @param {HTMLElement} input - Input element
|
|
375
|
+
*/
|
|
376
|
+
applyUserInputStyles(container, input) {
|
|
377
|
+
// Container is now a tq-dropdown-item, so don't add padding here
|
|
378
|
+
// Just set the border and background
|
|
379
|
+
Object.assign(container.style, {
|
|
380
|
+
backgroundColor: 'transparent',
|
|
381
|
+
borderTop: `1px solid ${this.options.borderColor}`,
|
|
382
|
+
// Override cursor from dropdown-item styles
|
|
383
|
+
cursor: 'text'
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
Object.assign(input.style, {
|
|
387
|
+
width: '100%',
|
|
388
|
+
padding: '8px 12px',
|
|
389
|
+
border: 'none',
|
|
390
|
+
borderBottom: '1px solid #ccc',
|
|
391
|
+
borderRadius: '0',
|
|
392
|
+
fontSize: '14px',
|
|
393
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
394
|
+
color: this.options.textColor,
|
|
395
|
+
backgroundColor: 'transparent',
|
|
396
|
+
boxSizing: 'border-box',
|
|
397
|
+
outline: 'none'
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
input.addEventListener('focus', () => {
|
|
401
|
+
input.style.borderBottom = '1px solid #ccc';
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
input.addEventListener('blur', () => {
|
|
405
|
+
input.style.borderBottom = '1px solid #ccc';
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Apply dropdown filter input styles
|
|
411
|
+
* @param {HTMLElement} filterInput - Filter input element
|
|
412
|
+
*/
|
|
413
|
+
applyDropdownFilterStyles(filterInput) {
|
|
414
|
+
Object.assign(filterInput.style, {
|
|
415
|
+
width: '100%',
|
|
416
|
+
padding: '8px 12px',
|
|
417
|
+
border: 'none',
|
|
418
|
+
borderBottom: `1px solid ${this.options.borderColor}`,
|
|
419
|
+
outline: 'none',
|
|
420
|
+
fontSize: '14px',
|
|
421
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
|
|
422
|
+
color: this.options.textColor,
|
|
423
|
+
backgroundColor: '#f7fafc',
|
|
424
|
+
boxSizing: 'border-box'
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Focus styles
|
|
428
|
+
filterInput.addEventListener('focus', () => {
|
|
429
|
+
filterInput.style.backgroundColor = '#fff';
|
|
430
|
+
filterInput.style.borderBottomColor = this.options.borderColorFocus;
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
filterInput.addEventListener('blur', () => {
|
|
434
|
+
filterInput.style.backgroundColor = '#f7fafc';
|
|
435
|
+
filterInput.style.borderBottomColor = this.options.borderColor;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Ensure animation keyframes are added to document (only once)
|
|
441
|
+
*/
|
|
442
|
+
ensureAnimationStyles() {
|
|
443
|
+
if (document.getElementById('tq-animations')) {
|
|
444
|
+
return; // Already added
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const style = document.createElement('style');
|
|
448
|
+
style.id = 'tq-animations';
|
|
449
|
+
style.textContent = `
|
|
450
|
+
@keyframes tq-bubble-appear {
|
|
451
|
+
from {
|
|
452
|
+
opacity: 0;
|
|
453
|
+
transform: translateY(-4px);
|
|
454
|
+
}
|
|
455
|
+
to {
|
|
456
|
+
opacity: 1;
|
|
457
|
+
transform: translateY(0);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
@keyframes tq-dropdown-appear {
|
|
462
|
+
from {
|
|
463
|
+
opacity: 0;
|
|
464
|
+
transform: scale(0.95);
|
|
465
|
+
}
|
|
466
|
+
to {
|
|
467
|
+
opacity: 1;
|
|
468
|
+
transform: scale(1);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.tq-dropdown-item-selected {
|
|
473
|
+
background-color: #e2e8f0 !important;
|
|
474
|
+
}
|
|
475
|
+
`;
|
|
476
|
+
document.head.appendChild(style);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Ensure placeholder styles are added to document (only once)
|
|
481
|
+
*/
|
|
482
|
+
ensurePlaceholderStyles() {
|
|
483
|
+
if (document.getElementById('tq-placeholder-styles')) {
|
|
484
|
+
return; // Already added
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const style = document.createElement('style');
|
|
488
|
+
style.id = 'tq-placeholder-styles';
|
|
489
|
+
style.textContent = `
|
|
490
|
+
.tq-textarea::placeholder {
|
|
491
|
+
color: #a0aec0 !important;
|
|
492
|
+
opacity: 1 !important;
|
|
493
|
+
-webkit-text-fill-color: #a0aec0 !important;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.tq-textarea::-webkit-input-placeholder {
|
|
497
|
+
color: #a0aec0 !important;
|
|
498
|
+
opacity: 1 !important;
|
|
499
|
+
-webkit-text-fill-color: #a0aec0 !important;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.tq-textarea::-moz-placeholder {
|
|
503
|
+
color: #a0aec0 !important;
|
|
504
|
+
opacity: 1 !important;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.tq-textarea:-ms-input-placeholder {
|
|
508
|
+
color: #a0aec0 !important;
|
|
509
|
+
opacity: 1 !important;
|
|
510
|
+
}
|
|
511
|
+
`;
|
|
512
|
+
document.head.appendChild(style);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Update theme colors dynamically
|
|
517
|
+
* @param {Object} newColors - New color options
|
|
518
|
+
*/
|
|
519
|
+
updateTheme(newColors) {
|
|
520
|
+
Object.assign(this.options, newColors);
|
|
521
|
+
console.log('[StyleManager] Theme updated:', this.options);
|
|
522
|
+
}
|
|
523
|
+
}
|