tekivex-ui 2.0.2 → 2.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/dist/index.cjs +12 -12
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2207 -2018
- package/dist/src/components/TkxAppBar.d.ts +12 -0
- package/dist/src/components/TkxAppBar.d.ts.map +1 -0
- package/dist/src/components/TkxAutocomplete.d.ts +25 -0
- package/dist/src/components/TkxAutocomplete.d.ts.map +1 -0
- package/dist/src/components/TkxBottomNav.d.ts +15 -0
- package/dist/src/components/TkxBottomNav.d.ts.map +1 -0
- package/dist/src/components/TkxBreadcrumb.d.ts +17 -0
- package/dist/src/components/TkxBreadcrumb.d.ts.map +1 -0
- package/dist/src/components/TkxCard.d.ts.map +1 -1
- package/dist/src/components/TkxDataGrid.d.ts +33 -0
- package/dist/src/components/TkxDataGrid.d.ts.map +1 -0
- package/dist/src/components/TkxMasonry.d.ts +12 -0
- package/dist/src/components/TkxMasonry.d.ts.map +1 -0
- package/dist/src/components/TkxModal.d.ts.map +1 -1
- package/dist/src/components/TkxPopover.d.ts +15 -0
- package/dist/src/components/TkxPopover.d.ts.map +1 -0
- package/dist/src/components/TkxRichTextDisplay.d.ts +19 -0
- package/dist/src/components/TkxRichTextDisplay.d.ts.map +1 -0
- package/dist/src/components/TkxSelect.d.ts.map +1 -1
- package/dist/src/components/TkxSlider.d.ts.map +1 -1
- package/dist/src/components/TkxSnackbar.d.ts +16 -0
- package/dist/src/components/TkxSnackbar.d.ts.map +1 -0
- package/dist/src/components/TkxSpeedDial.d.ts +18 -0
- package/dist/src/components/TkxSpeedDial.d.ts.map +1 -0
- package/dist/src/components/TkxToolbar.d.ts +22 -0
- package/dist/src/components/TkxToolbar.d.ts.map +1 -0
- package/dist/src/components/TkxTooltip.d.ts.map +1 -1
- package/dist/src/components/TkxTransferList.d.ts +20 -0
- package/dist/src/components/TkxTransferList.d.ts.map +1 -0
- package/dist/src/components/TkxTreeView.d.ts +23 -0
- package/dist/src/components/TkxTreeView.d.ts.map +1 -0
- package/dist/src/components/index.d.ts +13 -0
- package/dist/src/components/index.d.ts.map +1 -1
- package/dist/src/i18n/I18nProvider.d.ts +10 -0
- package/dist/src/i18n/I18nProvider.d.ts.map +1 -0
- package/dist/src/i18n/index.d.ts +50 -0
- package/dist/src/i18n/index.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/components/TkxAppBar.tsx +260 -0
- package/src/components/TkxAutocomplete.tsx +453 -0
- package/src/components/TkxBottomNav.tsx +239 -0
- package/src/components/TkxBreadcrumb.tsx +322 -0
- package/src/components/TkxCard.tsx +3 -1
- package/src/components/TkxDataGrid.tsx +540 -0
- package/src/components/TkxMasonry.tsx +176 -0
- package/src/components/TkxModal.tsx +4 -3
- package/src/components/TkxPopover.tsx +295 -0
- package/src/components/TkxRichTextDisplay.tsx +394 -0
- package/src/components/TkxSelect.tsx +2 -0
- package/src/components/TkxSlider.tsx +1 -0
- package/src/components/TkxSnackbar.tsx +314 -0
- package/src/components/TkxSpeedDial.tsx +395 -0
- package/src/components/TkxToolbar.tsx +260 -0
- package/src/components/TkxTooltip.tsx +3 -2
- package/src/components/TkxTransferList.tsx +487 -0
- package/src/components/TkxTreeView.tsx +421 -0
- package/src/components/index.ts +13 -0
- package/src/hooks/index.ts +1 -1
- package/src/i18n/I18nProvider.tsx +36 -0
- package/src/i18n/index.ts +237 -0
- package/dist/tests/TkxButton.test.d.ts +0 -2
- package/dist/tests/TkxButton.test.d.ts.map +0 -1
- package/dist/tests/a11y/components.test.d.ts +0 -2
- package/dist/tests/a11y/components.test.d.ts.map +0 -1
- package/dist/tests/quantum.test.d.ts +0 -2
- package/dist/tests/quantum.test.d.ts.map +0 -1
- package/dist/tests/security.test.d.ts +0 -2
- package/dist/tests/security.test.d.ts.map +0 -1
- package/dist/tests/setup.d.ts +0 -2
- package/dist/tests/setup.d.ts.map +0 -1
- package/dist/tests/tkx.test.d.ts +0 -2
- package/dist/tests/tkx.test.d.ts.map +0 -1
- package/dist/tests/wcag.test.d.ts +0 -2
- package/dist/tests/wcag.test.d.ts.map +0 -1
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useMemo,
|
|
3
|
+
type ReactNode,
|
|
4
|
+
type CSSProperties,
|
|
5
|
+
} from 'react';
|
|
6
|
+
import { useTheme } from '../themes';
|
|
7
|
+
import { sanitizeString } from '../engine/security';
|
|
8
|
+
import { useReducedMotion } from '../hooks';
|
|
9
|
+
import { tkx } from '../engine/tkx';
|
|
10
|
+
|
|
11
|
+
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export interface RichTextBlock {
|
|
14
|
+
type: 'heading' | 'paragraph' | 'blockquote' | 'code' | 'list' | 'divider' | 'image' | 'callout';
|
|
15
|
+
content?: string;
|
|
16
|
+
level?: 1 | 2 | 3;
|
|
17
|
+
language?: string;
|
|
18
|
+
items?: string[];
|
|
19
|
+
ordered?: boolean;
|
|
20
|
+
src?: string;
|
|
21
|
+
alt?: string;
|
|
22
|
+
variant?: 'info' | 'warning' | 'success' | 'danger';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface TkxRichTextDisplayProps {
|
|
26
|
+
blocks: RichTextBlock[];
|
|
27
|
+
className?: string;
|
|
28
|
+
style?: CSSProperties;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ── Callout icons ───────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
const CALLOUT_ICONS: Record<string, ReactNode> = {
|
|
34
|
+
info: (
|
|
35
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
36
|
+
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" />
|
|
37
|
+
</svg>
|
|
38
|
+
),
|
|
39
|
+
warning: (
|
|
40
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
41
|
+
<path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z" />
|
|
42
|
+
</svg>
|
|
43
|
+
),
|
|
44
|
+
success: (
|
|
45
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
46
|
+
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm-2 14.5l-4-4 1.41-1.41L10 13.67l6.59-6.59L18 8.5l-8 8z" />
|
|
47
|
+
</svg>
|
|
48
|
+
),
|
|
49
|
+
danger: (
|
|
50
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
51
|
+
<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z" />
|
|
52
|
+
</svg>
|
|
53
|
+
),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// ── Heading sizes ───────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
const HEADING_SIZES: Record<number, CSSProperties> = {
|
|
59
|
+
1: { fontSize: '1.75rem', fontWeight: 700, lineHeight: 1.25, marginTop: 24, marginBottom: 12 },
|
|
60
|
+
2: { fontSize: '1.375rem', fontWeight: 600, lineHeight: 1.3, marginTop: 20, marginBottom: 10 },
|
|
61
|
+
3: { fontSize: '1.1rem', fontWeight: 600, lineHeight: 1.35, marginTop: 16, marginBottom: 8 },
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// ── Block renderers ─────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
interface BlockRendererProps {
|
|
67
|
+
block: RichTextBlock;
|
|
68
|
+
index: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function HeadingBlock({ block }: BlockRendererProps) {
|
|
72
|
+
const theme = useTheme();
|
|
73
|
+
const level = block.level ?? 1;
|
|
74
|
+
const Tag = `h${level}` as 'h1' | 'h2' | 'h3';
|
|
75
|
+
const safeContent = sanitizeString(block.content ?? '');
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Tag
|
|
79
|
+
className={tkx('m-0')}
|
|
80
|
+
style={{
|
|
81
|
+
...HEADING_SIZES[level],
|
|
82
|
+
color: theme.text,
|
|
83
|
+
}}
|
|
84
|
+
>
|
|
85
|
+
{safeContent}
|
|
86
|
+
</Tag>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function ParagraphBlock({ block }: BlockRendererProps) {
|
|
91
|
+
const theme = useTheme();
|
|
92
|
+
const safeContent = sanitizeString(block.content ?? '');
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<p
|
|
96
|
+
className={tkx('m-0')}
|
|
97
|
+
style={{
|
|
98
|
+
fontSize: '0.938rem',
|
|
99
|
+
lineHeight: 1.7,
|
|
100
|
+
color: theme.text,
|
|
101
|
+
marginBottom: 12,
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{safeContent}
|
|
105
|
+
</p>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function BlockquoteBlock({ block }: BlockRendererProps) {
|
|
110
|
+
const theme = useTheme();
|
|
111
|
+
const safeContent = sanitizeString(block.content ?? '');
|
|
112
|
+
|
|
113
|
+
return (
|
|
114
|
+
<blockquote
|
|
115
|
+
className={tkx('m-0')}
|
|
116
|
+
style={{
|
|
117
|
+
borderLeft: `3px solid ${theme.primary}`,
|
|
118
|
+
paddingLeft: 16,
|
|
119
|
+
paddingTop: 8,
|
|
120
|
+
paddingBottom: 8,
|
|
121
|
+
marginTop: 12,
|
|
122
|
+
marginBottom: 12,
|
|
123
|
+
backgroundColor: `${theme.surfaceAlt}`,
|
|
124
|
+
borderRadius: '0 6px 6px 0',
|
|
125
|
+
color: theme.textMuted,
|
|
126
|
+
fontStyle: 'italic',
|
|
127
|
+
fontSize: '0.938rem',
|
|
128
|
+
lineHeight: 1.6,
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
{safeContent}
|
|
132
|
+
</blockquote>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function CodeBlock({ block }: BlockRendererProps) {
|
|
137
|
+
const theme = useTheme();
|
|
138
|
+
const safeContent = sanitizeString(block.content ?? '');
|
|
139
|
+
const safeLanguage = block.language ? sanitizeString(block.language) : null;
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<div
|
|
143
|
+
className={tkx('rounded-lg overflow-hidden')}
|
|
144
|
+
style={{
|
|
145
|
+
marginTop: 12,
|
|
146
|
+
marginBottom: 12,
|
|
147
|
+
border: `1px solid ${theme.border}`,
|
|
148
|
+
backgroundColor: theme.surfaceAlt,
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{/* Language label */}
|
|
152
|
+
{safeLanguage && (
|
|
153
|
+
<div
|
|
154
|
+
className={tkx('px-3 py-1 text-[11px] font-semibold uppercase tracking-wider')}
|
|
155
|
+
style={{
|
|
156
|
+
color: theme.textMuted,
|
|
157
|
+
borderBottom: `1px solid ${theme.border}`,
|
|
158
|
+
backgroundColor: theme.surface,
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
{safeLanguage}
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{/* Code content */}
|
|
166
|
+
<pre
|
|
167
|
+
className={tkx('m-0 p-4 overflow-x-auto')}
|
|
168
|
+
style={{
|
|
169
|
+
fontFamily: "'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace",
|
|
170
|
+
fontSize: '0.85rem',
|
|
171
|
+
lineHeight: 1.6,
|
|
172
|
+
color: theme.text,
|
|
173
|
+
tabSize: 2,
|
|
174
|
+
}}
|
|
175
|
+
>
|
|
176
|
+
<code aria-label={safeLanguage ? `Code block in ${safeLanguage}` : 'Code block'}>
|
|
177
|
+
{safeContent}
|
|
178
|
+
</code>
|
|
179
|
+
</pre>
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function ListBlock({ block }: BlockRendererProps) {
|
|
185
|
+
const theme = useTheme();
|
|
186
|
+
const Tag = block.ordered ? 'ol' : 'ul';
|
|
187
|
+
const safeItems = (block.items ?? []).map((item) => sanitizeString(item));
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<Tag
|
|
191
|
+
className={tkx('m-0')}
|
|
192
|
+
style={{
|
|
193
|
+
paddingLeft: 24,
|
|
194
|
+
marginTop: 8,
|
|
195
|
+
marginBottom: 12,
|
|
196
|
+
color: theme.text,
|
|
197
|
+
fontSize: '0.938rem',
|
|
198
|
+
lineHeight: 1.7,
|
|
199
|
+
listStyleType: block.ordered ? 'decimal' : 'disc',
|
|
200
|
+
}}
|
|
201
|
+
role="list"
|
|
202
|
+
>
|
|
203
|
+
{safeItems.map((item, i) => (
|
|
204
|
+
<li
|
|
205
|
+
key={i}
|
|
206
|
+
role="listitem"
|
|
207
|
+
style={{ marginBottom: 4 }}
|
|
208
|
+
>
|
|
209
|
+
{item}
|
|
210
|
+
</li>
|
|
211
|
+
))}
|
|
212
|
+
</Tag>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function DividerBlock() {
|
|
217
|
+
const theme = useTheme();
|
|
218
|
+
return (
|
|
219
|
+
<hr
|
|
220
|
+
aria-hidden="true"
|
|
221
|
+
style={{
|
|
222
|
+
border: 'none',
|
|
223
|
+
borderTop: `1px solid ${theme.border}`,
|
|
224
|
+
marginTop: 20,
|
|
225
|
+
marginBottom: 20,
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function ImageBlock({ block }: BlockRendererProps) {
|
|
232
|
+
const theme = useTheme();
|
|
233
|
+
const safeSrc = sanitizeString(block.src ?? '');
|
|
234
|
+
const safeAlt = sanitizeString(block.alt ?? 'Image');
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<figure
|
|
238
|
+
className={tkx('m-0')}
|
|
239
|
+
style={{ marginTop: 12, marginBottom: 12 }}
|
|
240
|
+
>
|
|
241
|
+
<img
|
|
242
|
+
src={safeSrc}
|
|
243
|
+
alt={safeAlt}
|
|
244
|
+
loading="lazy"
|
|
245
|
+
className={tkx('rounded-lg')}
|
|
246
|
+
style={{
|
|
247
|
+
maxWidth: '100%',
|
|
248
|
+
height: 'auto',
|
|
249
|
+
display: 'block',
|
|
250
|
+
border: `1px solid ${theme.border}`,
|
|
251
|
+
}}
|
|
252
|
+
/>
|
|
253
|
+
{block.alt && (
|
|
254
|
+
<figcaption
|
|
255
|
+
className={tkx('text-xs mt-2 text-center')}
|
|
256
|
+
style={{ color: theme.textMuted }}
|
|
257
|
+
>
|
|
258
|
+
{safeAlt}
|
|
259
|
+
</figcaption>
|
|
260
|
+
)}
|
|
261
|
+
</figure>
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function CalloutBlock({ block }: BlockRendererProps) {
|
|
266
|
+
const theme = useTheme();
|
|
267
|
+
const variant = block.variant ?? 'info';
|
|
268
|
+
const safeContent = sanitizeString(block.content ?? '');
|
|
269
|
+
|
|
270
|
+
const variantColorMap: Record<string, string> = {
|
|
271
|
+
info: theme.info,
|
|
272
|
+
warning: theme.warning,
|
|
273
|
+
success: theme.success,
|
|
274
|
+
danger: theme.danger,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const accentColor = variantColorMap[variant];
|
|
278
|
+
const icon = CALLOUT_ICONS[variant];
|
|
279
|
+
|
|
280
|
+
// Role mapping for semantics
|
|
281
|
+
const roleMap: Record<string, string> = {
|
|
282
|
+
info: 'note',
|
|
283
|
+
warning: 'alert',
|
|
284
|
+
success: 'status',
|
|
285
|
+
danger: 'alert',
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
return (
|
|
289
|
+
<div
|
|
290
|
+
role={roleMap[variant]}
|
|
291
|
+
className={tkx('flex gap-3 px-4 py-3 rounded-lg')}
|
|
292
|
+
style={{
|
|
293
|
+
backgroundColor: `${accentColor}12`,
|
|
294
|
+
border: `1px solid ${accentColor}33`,
|
|
295
|
+
marginTop: 12,
|
|
296
|
+
marginBottom: 12,
|
|
297
|
+
}}
|
|
298
|
+
>
|
|
299
|
+
<span
|
|
300
|
+
className={tkx('shrink-0 mt-[2px]')}
|
|
301
|
+
style={{ color: accentColor }}
|
|
302
|
+
>
|
|
303
|
+
{icon}
|
|
304
|
+
</span>
|
|
305
|
+
<div
|
|
306
|
+
className={tkx('flex-1')}
|
|
307
|
+
style={{
|
|
308
|
+
fontSize: '0.9rem',
|
|
309
|
+
lineHeight: 1.6,
|
|
310
|
+
color: theme.text,
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
{safeContent}
|
|
314
|
+
</div>
|
|
315
|
+
</div>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ── Block dispatcher ────────────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
const BLOCK_RENDERERS: Record<string, (props: BlockRendererProps) => ReactNode> = {
|
|
322
|
+
heading: HeadingBlock,
|
|
323
|
+
paragraph: ParagraphBlock,
|
|
324
|
+
blockquote: BlockquoteBlock,
|
|
325
|
+
code: CodeBlock,
|
|
326
|
+
list: ListBlock,
|
|
327
|
+
divider: DividerBlock,
|
|
328
|
+
image: ImageBlock,
|
|
329
|
+
callout: CalloutBlock,
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
// ── Main component ──────────────────────────────────────────────────────────
|
|
333
|
+
|
|
334
|
+
export function TkxRichTextDisplay({
|
|
335
|
+
blocks,
|
|
336
|
+
className,
|
|
337
|
+
style,
|
|
338
|
+
}: TkxRichTextDisplayProps) {
|
|
339
|
+
const theme = useTheme();
|
|
340
|
+
const reduced = useReducedMotion();
|
|
341
|
+
|
|
342
|
+
// Memoize the rendered blocks
|
|
343
|
+
const renderedBlocks = useMemo(() => {
|
|
344
|
+
return blocks.map((block, index) => {
|
|
345
|
+
const Renderer = BLOCK_RENDERERS[block.type];
|
|
346
|
+
if (!Renderer) return null;
|
|
347
|
+
|
|
348
|
+
return (
|
|
349
|
+
<div
|
|
350
|
+
key={index}
|
|
351
|
+
style={
|
|
352
|
+
reduced
|
|
353
|
+
? undefined
|
|
354
|
+
: {
|
|
355
|
+
animation: `tkx-rich-text-fade-in 300ms ease both`,
|
|
356
|
+
animationDelay: `${Math.min(index * 30, 300)}ms`,
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
>
|
|
360
|
+
<Renderer block={block} index={index} />
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
});
|
|
364
|
+
}, [blocks, reduced]);
|
|
365
|
+
|
|
366
|
+
// Inject keyframes
|
|
367
|
+
useMemo(() => {
|
|
368
|
+
if (typeof document === 'undefined') return;
|
|
369
|
+
if (document.getElementById('tkx-rich-text-kf')) return;
|
|
370
|
+
const styleEl = document.createElement('style');
|
|
371
|
+
styleEl.id = 'tkx-rich-text-kf';
|
|
372
|
+
styleEl.textContent = `
|
|
373
|
+
@keyframes tkx-rich-text-fade-in {
|
|
374
|
+
from { opacity: 0; }
|
|
375
|
+
to { opacity: 1; }
|
|
376
|
+
}
|
|
377
|
+
`;
|
|
378
|
+
document.head.appendChild(styleEl);
|
|
379
|
+
}, []);
|
|
380
|
+
|
|
381
|
+
return (
|
|
382
|
+
<article
|
|
383
|
+
aria-label="Rich text content"
|
|
384
|
+
className={tkx('font-sans', className ?? '')}
|
|
385
|
+
style={{
|
|
386
|
+
color: theme.text,
|
|
387
|
+
maxWidth: 720,
|
|
388
|
+
...style,
|
|
389
|
+
}}
|
|
390
|
+
>
|
|
391
|
+
{renderedBlocks}
|
|
392
|
+
</article>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
@@ -609,6 +609,7 @@ export function TkxSelect({
|
|
|
609
609
|
transition: 'background-color 80ms',
|
|
610
610
|
opacity: opt.disabled ? 0.5 : 1,
|
|
611
611
|
userSelect: 'none',
|
|
612
|
+
touchAction: 'manipulation',
|
|
612
613
|
}}
|
|
613
614
|
>
|
|
614
615
|
{optionContent}
|
|
@@ -714,6 +715,7 @@ export function TkxSelect({
|
|
|
714
715
|
outline: 'none',
|
|
715
716
|
minHeight: size === 'sm' ? 34 : size === 'lg' ? 50 : 42,
|
|
716
717
|
flexWrap: 'wrap',
|
|
718
|
+
touchAction: 'manipulation',
|
|
717
719
|
}}
|
|
718
720
|
>
|
|
719
721
|
{/* Multi-select tags or single label */}
|