ywana-core8 0.1.74 → 0.1.76
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/ACCORDION_EVALUATION.md +583 -0
- package/CHECKBOX_EVALUATION.md +273 -0
- package/CHIP_EVALUATION.md +542 -0
- package/COLOR_EVALUATION.md +524 -0
- package/COMPONENTS_EVALUATION.md +477 -0
- package/FORM_EVALUATION.md +459 -0
- package/HEADER_EVALUATION.md +436 -0
- package/ICON_EVALUATION.md +254 -0
- package/LIST_EVALUATION.md +574 -0
- package/PROGRESS_EVALUATION.md +450 -0
- package/RADIO_EVALUATION.md +439 -0
- package/RADIO_VISUAL_FIX.md +183 -0
- package/SECTION_IMPROVEMENTS.md +153 -0
- package/SWITCH_EVALUATION.md +335 -0
- package/SWITCH_VISUAL_FIX.md +232 -0
- package/TAB_EVALUATION.md +626 -0
- package/TEXTFIELD_EVALUATION.md +747 -0
- package/TOOLTIP_FIX.md +157 -0
- package/TREE_EVALUATION.md +708 -0
- package/dist/index.cjs +7900 -1615
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +6094 -1122
- package/dist/index.css.map +1 -1
- package/dist/index.modern.js +7929 -1645
- package/dist/index.modern.js.map +1 -1
- package/dist/index.umd.js +7900 -1615
- package/dist/index.umd.js.map +1 -1
- package/jest.config.js +24 -0
- package/package.json +10 -1
- package/src/html/accordion.css +208 -4
- package/src/html/accordion.example.js +390 -0
- package/src/html/accordion.js +284 -28
- package/src/html/accordion.unit.test.js +334 -0
- package/src/html/button.css +157 -16
- package/src/html/button.example.js +374 -0
- package/src/html/button.js +240 -60
- package/src/html/button.test.js +422 -0
- package/src/html/checkbox.css +74 -2
- package/src/html/checkbox.example.js +316 -0
- package/src/html/checkbox.js +113 -26
- package/src/html/checkbox.test.js +285 -0
- package/src/html/chip.css +230 -19
- package/src/html/chip.example.js +355 -0
- package/src/html/chip.js +321 -25
- package/src/html/chip.test.js +425 -0
- package/src/html/color.css +435 -6
- package/src/html/color.example.js +527 -0
- package/src/html/color.js +458 -9
- package/src/html/color.test.js +362 -4
- package/src/html/components.example.js +492 -0
- package/src/html/components_enhanced.test.js +581 -0
- package/src/html/form.css +70 -3
- package/src/html/form.example.js +385 -0
- package/src/html/form.js +232 -34
- package/src/html/form.test.js +369 -0
- package/src/html/header2.css +264 -0
- package/src/html/header2.example.js +411 -0
- package/src/html/header2.js +203 -0
- package/src/html/header2.test.js +377 -0
- package/src/html/icon.css +20 -2
- package/src/html/icon.example.js +268 -0
- package/src/html/icon.js +86 -16
- package/src/html/icon.test.js +231 -0
- package/src/html/index.js +1 -1
- package/src/html/list.css +393 -1
- package/src/html/list.example.js +404 -0
- package/src/html/list.js +583 -40
- package/src/html/list.test.js +383 -0
- package/src/html/progress.css +707 -17
- package/src/html/progress.example.js +424 -0
- package/src/html/progress.js +906 -9
- package/src/html/progress.test.js +313 -0
- package/src/html/property.css +399 -0
- package/src/html/property.example.js +553 -0
- package/src/html/property.js +393 -15
- package/src/html/property.test.js +351 -2
- package/src/html/radio-visual-test.js +289 -0
- package/src/html/radio.css +137 -11
- package/src/html/radio.example.js +389 -0
- package/src/html/radio.js +234 -10
- package/src/html/radio.test.js +318 -0
- package/src/html/section.example.js +99 -0
- package/src/html/section.js +40 -3
- package/src/html/section.test.js +131 -0
- package/src/html/selector.css +329 -3
- package/src/html/selector.js +369 -23
- package/src/html/switch-debug.js +197 -0
- package/src/html/switch-test-visual.js +294 -0
- package/src/html/switch.css +200 -0
- package/src/html/switch.example.js +461 -0
- package/src/html/switch.js +283 -23
- package/src/html/switch.test.js +355 -0
- package/src/html/tab.css +288 -0
- package/src/html/tab.example.js +446 -0
- package/src/html/tab.js +387 -22
- package/src/html/tab_enhanced.js +378 -0
- package/src/html/tab_enhanced.test.js +504 -0
- package/src/html/table2.css +576 -0
- package/src/html/table2.example.js +703 -0
- package/src/html/table2.js +1252 -0
- package/src/html/table2.migration.md +328 -0
- package/src/html/table2.test.js +582 -0
- package/src/html/text.css +375 -0
- package/src/html/text.js +311 -20
- package/src/html/textfield.js +1 -1
- package/src/html/textfield2.css +842 -0
- package/src/html/textfield2.example.js +499 -0
- package/src/html/textfield2.js +1130 -0
- package/src/html/textfield2.test.js +950 -0
- package/src/html/thumbnail.css +289 -2
- package/src/html/thumbnail.js +214 -9
- package/src/html/tokenfield.css +449 -1
- package/src/html/tokenfield.example.js +503 -0
- package/src/html/tokenfield.js +561 -56
- package/src/html/tokenfield.test.js +423 -0
- package/src/html/tooltip-positioning-demo.js +187 -0
- package/src/html/tooltip.css +25 -2
- package/src/html/tree.css +228 -0
- package/src/html/tree.example.js +475 -0
- package/src/html/tree.js +712 -28
- package/src/html/tree_enhanced.test.js +495 -0
- package/table2.test.js +454 -0
- package/src/html/button.tsx +0 -38
@@ -0,0 +1,375 @@
|
|
1
|
+
/* Enhanced Text Component Styles */
|
2
|
+
|
3
|
+
.text {
|
4
|
+
display: inline;
|
5
|
+
font-family: inherit;
|
6
|
+
line-height: inherit;
|
7
|
+
margin: 0;
|
8
|
+
padding: 0;
|
9
|
+
transition: color 0.2s ease;
|
10
|
+
}
|
11
|
+
|
12
|
+
/* Size variants */
|
13
|
+
.text--xs {
|
14
|
+
font-size: 0.75rem;
|
15
|
+
line-height: 1.2;
|
16
|
+
}
|
17
|
+
|
18
|
+
.text--sm {
|
19
|
+
font-size: 0.875rem;
|
20
|
+
line-height: 1.25;
|
21
|
+
}
|
22
|
+
|
23
|
+
.text--md {
|
24
|
+
font-size: 1rem;
|
25
|
+
line-height: 1.5;
|
26
|
+
}
|
27
|
+
|
28
|
+
.text--lg {
|
29
|
+
font-size: 1.125rem;
|
30
|
+
line-height: 1.5;
|
31
|
+
}
|
32
|
+
|
33
|
+
.text--xl {
|
34
|
+
font-size: 1.25rem;
|
35
|
+
line-height: 1.4;
|
36
|
+
}
|
37
|
+
|
38
|
+
.text--xxl {
|
39
|
+
font-size: 1.5rem;
|
40
|
+
line-height: 1.3;
|
41
|
+
}
|
42
|
+
|
43
|
+
/* Weight variants */
|
44
|
+
.text--light {
|
45
|
+
font-weight: 300;
|
46
|
+
}
|
47
|
+
|
48
|
+
.text--normal {
|
49
|
+
font-weight: 400;
|
50
|
+
}
|
51
|
+
|
52
|
+
.text--medium {
|
53
|
+
font-weight: 500;
|
54
|
+
}
|
55
|
+
|
56
|
+
.text--semibold {
|
57
|
+
font-weight: 600;
|
58
|
+
}
|
59
|
+
|
60
|
+
.text--bold {
|
61
|
+
font-weight: 700;
|
62
|
+
}
|
63
|
+
|
64
|
+
/* Color variants */
|
65
|
+
.text--primary {
|
66
|
+
color: var(--primary-color, #2196f3);
|
67
|
+
}
|
68
|
+
|
69
|
+
.text--secondary {
|
70
|
+
color: var(--secondary-color, #757575);
|
71
|
+
}
|
72
|
+
|
73
|
+
.text--success {
|
74
|
+
color: var(--success-color, #4caf50);
|
75
|
+
}
|
76
|
+
|
77
|
+
.text--warning {
|
78
|
+
color: var(--warning-color, #ff9800);
|
79
|
+
}
|
80
|
+
|
81
|
+
.text--error {
|
82
|
+
color: var(--error-color, #f44336);
|
83
|
+
}
|
84
|
+
|
85
|
+
.text--muted {
|
86
|
+
color: var(--text-color-light, #666666);
|
87
|
+
}
|
88
|
+
|
89
|
+
/* Alignment variants */
|
90
|
+
.text--left {
|
91
|
+
text-align: left;
|
92
|
+
}
|
93
|
+
|
94
|
+
.text--center {
|
95
|
+
text-align: center;
|
96
|
+
}
|
97
|
+
|
98
|
+
.text--right {
|
99
|
+
text-align: right;
|
100
|
+
}
|
101
|
+
|
102
|
+
.text--justify {
|
103
|
+
text-align: justify;
|
104
|
+
}
|
105
|
+
|
106
|
+
/* Transform variants */
|
107
|
+
.text--capitalize {
|
108
|
+
text-transform: capitalize;
|
109
|
+
}
|
110
|
+
|
111
|
+
.text--uppercase {
|
112
|
+
text-transform: uppercase;
|
113
|
+
}
|
114
|
+
|
115
|
+
.text--lowercase {
|
116
|
+
text-transform: lowercase;
|
117
|
+
}
|
118
|
+
|
119
|
+
/* Decoration variants */
|
120
|
+
.text--underline {
|
121
|
+
text-decoration: underline;
|
122
|
+
}
|
123
|
+
|
124
|
+
.text--line-through {
|
125
|
+
text-decoration: line-through;
|
126
|
+
}
|
127
|
+
|
128
|
+
/* State variants */
|
129
|
+
.text--truncate {
|
130
|
+
overflow: hidden;
|
131
|
+
text-overflow: ellipsis;
|
132
|
+
white-space: nowrap;
|
133
|
+
max-width: 100%;
|
134
|
+
}
|
135
|
+
|
136
|
+
.text--no-select {
|
137
|
+
user-select: none;
|
138
|
+
-webkit-user-select: none;
|
139
|
+
-moz-user-select: none;
|
140
|
+
-ms-user-select: none;
|
141
|
+
}
|
142
|
+
|
143
|
+
.text--copyable {
|
144
|
+
cursor: pointer;
|
145
|
+
position: relative;
|
146
|
+
display: inline-flex;
|
147
|
+
align-items: center;
|
148
|
+
gap: 0.25rem;
|
149
|
+
}
|
150
|
+
|
151
|
+
.text--copyable:hover {
|
152
|
+
background-color: var(--hover-color, rgba(0, 0, 0, 0.04));
|
153
|
+
border-radius: 2px;
|
154
|
+
padding: 0.125rem 0.25rem;
|
155
|
+
margin: -0.125rem -0.25rem;
|
156
|
+
}
|
157
|
+
|
158
|
+
.text--copyable:focus {
|
159
|
+
outline: 2px solid var(--primary-color, #2196f3);
|
160
|
+
outline-offset: 2px;
|
161
|
+
border-radius: 2px;
|
162
|
+
}
|
163
|
+
|
164
|
+
.text--loading {
|
165
|
+
opacity: 0.7;
|
166
|
+
pointer-events: none;
|
167
|
+
}
|
168
|
+
|
169
|
+
.text--skeleton {
|
170
|
+
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
171
|
+
background-size: 200% 100%;
|
172
|
+
animation: skeleton-loading 1.5s infinite;
|
173
|
+
border-radius: 2px;
|
174
|
+
color: transparent;
|
175
|
+
user-select: none;
|
176
|
+
}
|
177
|
+
|
178
|
+
/* Copy indicator */
|
179
|
+
.text__copy-indicator {
|
180
|
+
opacity: 0;
|
181
|
+
transition: opacity 0.2s ease;
|
182
|
+
font-size: 0.8em;
|
183
|
+
}
|
184
|
+
|
185
|
+
.text--copyable:hover .text__copy-indicator {
|
186
|
+
opacity: 0.6;
|
187
|
+
}
|
188
|
+
|
189
|
+
/* Skeleton placeholder */
|
190
|
+
.text__skeleton {
|
191
|
+
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
192
|
+
background-size: 200% 100%;
|
193
|
+
animation: skeleton-loading 1.5s infinite;
|
194
|
+
border-radius: 2px;
|
195
|
+
color: transparent;
|
196
|
+
user-select: none;
|
197
|
+
display: inline-block;
|
198
|
+
min-width: 4rem;
|
199
|
+
min-height: 1em;
|
200
|
+
}
|
201
|
+
|
202
|
+
/* Animations */
|
203
|
+
@keyframes skeleton-loading {
|
204
|
+
0% {
|
205
|
+
background-position: 200% 0;
|
206
|
+
}
|
207
|
+
100% {
|
208
|
+
background-position: -200% 0;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
/* Responsive Design */
|
213
|
+
@media (max-width: 768px) {
|
214
|
+
.text--xs {
|
215
|
+
font-size: 0.7rem;
|
216
|
+
}
|
217
|
+
|
218
|
+
.text--sm {
|
219
|
+
font-size: 0.8rem;
|
220
|
+
}
|
221
|
+
|
222
|
+
.text--md {
|
223
|
+
font-size: 0.9rem;
|
224
|
+
}
|
225
|
+
|
226
|
+
.text--lg {
|
227
|
+
font-size: 1rem;
|
228
|
+
}
|
229
|
+
|
230
|
+
.text--xl {
|
231
|
+
font-size: 1.1rem;
|
232
|
+
}
|
233
|
+
|
234
|
+
.text--xxl {
|
235
|
+
font-size: 1.25rem;
|
236
|
+
}
|
237
|
+
|
238
|
+
.text--copyable:hover {
|
239
|
+
padding: 0.25rem;
|
240
|
+
margin: -0.25rem;
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
/* Dark Theme Support */
|
245
|
+
@media (prefers-color-scheme: dark) {
|
246
|
+
.text--primary {
|
247
|
+
color: var(--primary-color-dark, #64b5f6);
|
248
|
+
}
|
249
|
+
|
250
|
+
.text--secondary {
|
251
|
+
color: var(--secondary-color-dark, #bdbdbd);
|
252
|
+
}
|
253
|
+
|
254
|
+
.text--success {
|
255
|
+
color: var(--success-color-dark, #81c784);
|
256
|
+
}
|
257
|
+
|
258
|
+
.text--warning {
|
259
|
+
color: var(--warning-color-dark, #ffb74d);
|
260
|
+
}
|
261
|
+
|
262
|
+
.text--error {
|
263
|
+
color: var(--error-color-dark, #e57373);
|
264
|
+
}
|
265
|
+
|
266
|
+
.text--muted {
|
267
|
+
color: var(--text-color-light-dark, #cccccc);
|
268
|
+
}
|
269
|
+
|
270
|
+
.text--copyable:hover {
|
271
|
+
background-color: var(--hover-color-dark, rgba(255, 255, 255, 0.08));
|
272
|
+
}
|
273
|
+
|
274
|
+
.text--skeleton,
|
275
|
+
.text__skeleton {
|
276
|
+
background: linear-gradient(90deg, #424242 25%, #616161 50%, #424242 75%);
|
277
|
+
background-size: 200% 100%;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
|
281
|
+
/* High Contrast Mode */
|
282
|
+
@media (prefers-contrast: high) {
|
283
|
+
.text--copyable:focus {
|
284
|
+
outline-width: 3px;
|
285
|
+
outline-color: currentColor;
|
286
|
+
}
|
287
|
+
|
288
|
+
.text--primary,
|
289
|
+
.text--secondary,
|
290
|
+
.text--success,
|
291
|
+
.text--warning,
|
292
|
+
.text--error {
|
293
|
+
font-weight: 600;
|
294
|
+
}
|
295
|
+
|
296
|
+
.text--underline {
|
297
|
+
text-decoration-thickness: 2px;
|
298
|
+
}
|
299
|
+
}
|
300
|
+
|
301
|
+
/* Reduced Motion */
|
302
|
+
@media (prefers-reduced-motion: reduce) {
|
303
|
+
.text {
|
304
|
+
transition: none;
|
305
|
+
}
|
306
|
+
|
307
|
+
.text--skeleton,
|
308
|
+
.text__skeleton {
|
309
|
+
animation: none;
|
310
|
+
background: #e0e0e0;
|
311
|
+
}
|
312
|
+
|
313
|
+
.text__copy-indicator {
|
314
|
+
transition: none;
|
315
|
+
}
|
316
|
+
}
|
317
|
+
|
318
|
+
/* Print Styles */
|
319
|
+
@media print {
|
320
|
+
.text {
|
321
|
+
color: black !important;
|
322
|
+
background: transparent !important;
|
323
|
+
}
|
324
|
+
|
325
|
+
.text--copyable {
|
326
|
+
cursor: default !important;
|
327
|
+
}
|
328
|
+
|
329
|
+
.text__copy-indicator {
|
330
|
+
display: none !important;
|
331
|
+
}
|
332
|
+
|
333
|
+
.text--skeleton,
|
334
|
+
.text__skeleton {
|
335
|
+
background: #f0f0f0 !important;
|
336
|
+
animation: none !important;
|
337
|
+
}
|
338
|
+
|
339
|
+
.text--primary,
|
340
|
+
.text--secondary,
|
341
|
+
.text--success,
|
342
|
+
.text--warning,
|
343
|
+
.text--error,
|
344
|
+
.text--muted {
|
345
|
+
color: black !important;
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
/* Focus visible for better accessibility */
|
350
|
+
.text--copyable:focus-visible {
|
351
|
+
outline: 2px solid var(--primary-color, #2196f3);
|
352
|
+
outline-offset: 2px;
|
353
|
+
}
|
354
|
+
|
355
|
+
/* Selection styling */
|
356
|
+
.text::selection {
|
357
|
+
background-color: var(--primary-color-light, rgba(33, 150, 243, 0.2));
|
358
|
+
color: inherit;
|
359
|
+
}
|
360
|
+
|
361
|
+
/* Ensure proper line height for different sizes */
|
362
|
+
.text--xs,
|
363
|
+
.text--sm {
|
364
|
+
line-height: 1.4;
|
365
|
+
}
|
366
|
+
|
367
|
+
.text--md {
|
368
|
+
line-height: 1.5;
|
369
|
+
}
|
370
|
+
|
371
|
+
.text--lg,
|
372
|
+
.text--xl,
|
373
|
+
.text--xxl {
|
374
|
+
line-height: 1.3;
|
375
|
+
}
|
package/src/html/text.js
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
import React, { useContext } from "react"
|
1
|
+
import React, { useContext, useMemo, useCallback } from "react"
|
2
|
+
import PropTypes from 'prop-types'
|
2
3
|
import { SiteContext } from "../site/siteContext"
|
4
|
+
import './text.css'
|
3
5
|
|
4
6
|
export const TEXTFORMATS = {
|
5
7
|
NONE: '',
|
@@ -10,39 +12,328 @@ export const TEXTFORMATS = {
|
|
10
12
|
HTML: 'HTML',
|
11
13
|
URL: 'URL',
|
12
14
|
STRING: 'string',
|
15
|
+
// New formats
|
16
|
+
CURRENCY: 'currency',
|
17
|
+
PERCENTAGE: 'percentage',
|
18
|
+
PHONE: 'phone',
|
19
|
+
CAPITALIZE: 'capitalize',
|
20
|
+
UPPERCASE: 'uppercase',
|
21
|
+
LOWERCASE: 'lowercase',
|
22
|
+
TRUNCATE: 'truncate',
|
23
|
+
MARKDOWN: 'markdown'
|
13
24
|
}
|
14
25
|
|
15
26
|
/**
|
16
|
-
* Text
|
27
|
+
* Enhanced Text component with improved functionality while maintaining 100% compatibility
|
17
28
|
*/
|
18
|
-
export const Text = (
|
29
|
+
export const Text = (props) => {
|
30
|
+
const {
|
31
|
+
// Original props (100% compatible)
|
32
|
+
format = TEXTFORMATS.HTML,
|
33
|
+
children,
|
34
|
+
className,
|
35
|
+
// New enhanced props (all optional for compatibility)
|
36
|
+
size,
|
37
|
+
weight,
|
38
|
+
color,
|
39
|
+
align,
|
40
|
+
transform,
|
41
|
+
decoration,
|
42
|
+
truncate,
|
43
|
+
maxLength,
|
44
|
+
locale,
|
45
|
+
currency = 'USD',
|
46
|
+
minimumFractionDigits,
|
47
|
+
maximumFractionDigits,
|
48
|
+
dateStyle = 'medium',
|
49
|
+
timeStyle = 'medium',
|
50
|
+
fallback = '',
|
51
|
+
loading = false,
|
52
|
+
skeleton = false,
|
53
|
+
copyable = false,
|
54
|
+
selectable = true,
|
55
|
+
as = 'span',
|
56
|
+
style,
|
57
|
+
onClick,
|
58
|
+
...restProps
|
59
|
+
} = props
|
19
60
|
|
20
61
|
const site = useContext(SiteContext)
|
21
|
-
let value = children
|
22
62
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
63
|
+
// Memoize formatted value for performance
|
64
|
+
const formattedValue = useMemo(() => {
|
65
|
+
let value = children
|
66
|
+
|
67
|
+
// Handle empty/null values
|
68
|
+
if (children === null || children === undefined || children === '') {
|
69
|
+
return fallback || ''
|
70
|
+
}
|
71
|
+
|
72
|
+
// Internationalization (maintaining original logic)
|
73
|
+
if (site) {
|
74
|
+
const { lang, dictionary = {} } = site
|
75
|
+
const term = dictionary[children]
|
76
|
+
const text = term ? term[lang] : children
|
77
|
+
if (text) value = text
|
78
|
+
}
|
79
|
+
|
80
|
+
// Use provided locale or site locale or browser locale
|
81
|
+
const currentLocale = locale || (site && site.lang) || navigator.language || 'en-US'
|
82
|
+
|
83
|
+
try {
|
84
|
+
switch (format) {
|
85
|
+
case TEXTFORMATS.NUMERIC: {
|
86
|
+
const formatter = new Intl.NumberFormat(currentLocale, {
|
87
|
+
minimumFractionDigits,
|
88
|
+
maximumFractionDigits
|
89
|
+
})
|
90
|
+
return formatter.format(Number(children))
|
91
|
+
}
|
92
|
+
|
93
|
+
case TEXTFORMATS.CURRENCY: {
|
94
|
+
const formatter = new Intl.NumberFormat(currentLocale, {
|
95
|
+
style: 'currency',
|
96
|
+
currency,
|
97
|
+
minimumFractionDigits,
|
98
|
+
maximumFractionDigits
|
99
|
+
})
|
100
|
+
return formatter.format(Number(children))
|
101
|
+
}
|
102
|
+
|
103
|
+
case TEXTFORMATS.PERCENTAGE: {
|
104
|
+
const formatter = new Intl.NumberFormat(currentLocale, {
|
105
|
+
style: 'percent',
|
106
|
+
minimumFractionDigits,
|
107
|
+
maximumFractionDigits
|
108
|
+
})
|
109
|
+
return formatter.format(Number(children) / 100)
|
110
|
+
}
|
111
|
+
|
112
|
+
case TEXTFORMATS.DATE: {
|
113
|
+
const date = new Date(children)
|
114
|
+
if (isNaN(date.getTime())) return String(children)
|
115
|
+
return date.toLocaleDateString(currentLocale, { dateStyle })
|
116
|
+
}
|
117
|
+
|
118
|
+
case TEXTFORMATS.TIME: {
|
119
|
+
const date = new Date(children)
|
120
|
+
if (isNaN(date.getTime())) return String(children)
|
121
|
+
return date.toLocaleTimeString(currentLocale, { timeStyle })
|
122
|
+
}
|
123
|
+
|
124
|
+
case TEXTFORMATS.EMAIL: {
|
125
|
+
return String(value)
|
126
|
+
}
|
127
|
+
|
128
|
+
case TEXTFORMATS.PHONE: {
|
129
|
+
// Basic phone formatting for US numbers
|
130
|
+
const cleaned = String(value).replace(/\D/g, '')
|
131
|
+
if (cleaned.length === 10) {
|
132
|
+
return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`
|
133
|
+
}
|
134
|
+
return String(value)
|
135
|
+
}
|
136
|
+
|
137
|
+
case TEXTFORMATS.URL: {
|
138
|
+
return String(value)
|
139
|
+
}
|
140
|
+
|
141
|
+
case TEXTFORMATS.CAPITALIZE: {
|
142
|
+
return String(value).charAt(0).toUpperCase() + String(value).slice(1).toLowerCase()
|
143
|
+
}
|
144
|
+
|
145
|
+
case TEXTFORMATS.UPPERCASE: {
|
146
|
+
return String(value).toUpperCase()
|
147
|
+
}
|
29
148
|
|
30
|
-
|
31
|
-
|
32
|
-
|
149
|
+
case TEXTFORMATS.LOWERCASE: {
|
150
|
+
return String(value).toLowerCase()
|
151
|
+
}
|
152
|
+
|
153
|
+
case TEXTFORMATS.TRUNCATE: {
|
154
|
+
const text = String(value)
|
155
|
+
if (maxLength && text.length > maxLength) {
|
156
|
+
return text.slice(0, maxLength) + '...'
|
157
|
+
}
|
158
|
+
return text
|
159
|
+
}
|
160
|
+
|
161
|
+
case TEXTFORMATS.STRING: {
|
162
|
+
return String(value)
|
163
|
+
}
|
164
|
+
|
165
|
+
case TEXTFORMATS.HTML:
|
166
|
+
default: {
|
167
|
+
return String(value)
|
168
|
+
}
|
169
|
+
}
|
170
|
+
} catch (error) {
|
171
|
+
console.warn('Text formatting error:', error)
|
172
|
+
return String(value)
|
173
|
+
}
|
174
|
+
}, [children, format, site, locale, currency, minimumFractionDigits, maximumFractionDigits, dateStyle, timeStyle, maxLength, fallback])
|
175
|
+
|
176
|
+
// Handle copy functionality
|
177
|
+
const handleCopy = useCallback(async () => {
|
178
|
+
if (!copyable || !formattedValue) return
|
179
|
+
|
180
|
+
try {
|
181
|
+
await navigator.clipboard.writeText(formattedValue)
|
182
|
+
// Could trigger a toast notification here
|
183
|
+
} catch (error) {
|
184
|
+
console.warn('Failed to copy text:', error)
|
185
|
+
}
|
186
|
+
}, [copyable, formattedValue])
|
187
|
+
|
188
|
+
// Handle click
|
189
|
+
const handleClick = useCallback((event) => {
|
190
|
+
if (copyable) {
|
191
|
+
handleCopy()
|
192
|
+
}
|
193
|
+
if (onClick) {
|
194
|
+
onClick(event)
|
195
|
+
}
|
196
|
+
}, [copyable, handleCopy, onClick])
|
197
|
+
|
198
|
+
// Generate CSS classes
|
199
|
+
const cssClasses = [
|
200
|
+
'text',
|
201
|
+
size && `text--${size}`,
|
202
|
+
weight && `text--${weight}`,
|
203
|
+
color && `text--${color}`,
|
204
|
+
align && `text--${align}`,
|
205
|
+
transform && `text--${transform}`,
|
206
|
+
decoration && `text--${decoration}`,
|
207
|
+
truncate && 'text--truncate',
|
208
|
+
loading && 'text--loading',
|
209
|
+
skeleton && 'text--skeleton',
|
210
|
+
copyable && 'text--copyable',
|
211
|
+
!selectable && 'text--no-select',
|
212
|
+
className
|
213
|
+
].filter(Boolean).join(' ')
|
214
|
+
|
215
|
+
// Generate inline styles
|
216
|
+
const inlineStyle = {
|
217
|
+
...style
|
33
218
|
}
|
34
219
|
|
35
|
-
|
36
|
-
|
37
|
-
|
220
|
+
// Handle loading state
|
221
|
+
if (loading || skeleton) {
|
222
|
+
const Component = as
|
223
|
+
return (
|
224
|
+
<Component
|
225
|
+
className={cssClasses}
|
226
|
+
style={inlineStyle}
|
227
|
+
{...restProps}
|
228
|
+
>
|
229
|
+
<span className="text__skeleton">
|
230
|
+
{skeleton ? '████████' : 'Loading...'}
|
231
|
+
</span>
|
232
|
+
</Component>
|
233
|
+
)
|
38
234
|
}
|
39
235
|
|
40
|
-
|
236
|
+
// Handle empty content (maintaining original logic)
|
237
|
+
if (!children && !fallback) return ''
|
41
238
|
|
42
|
-
|
43
|
-
|
44
|
-
|
239
|
+
// Return string for STRING format (maintaining original logic)
|
240
|
+
if (format === TEXTFORMATS.STRING) {
|
241
|
+
return formattedValue
|
45
242
|
}
|
46
243
|
|
244
|
+
// Render as component
|
245
|
+
const Component = as
|
246
|
+
|
247
|
+
return (
|
248
|
+
<Component
|
249
|
+
className={cssClasses}
|
250
|
+
style={inlineStyle}
|
251
|
+
onClick={handleClick}
|
252
|
+
title={copyable ? 'Click to copy' : undefined}
|
253
|
+
role={copyable ? 'button' : undefined}
|
254
|
+
tabIndex={copyable ? 0 : undefined}
|
255
|
+
{...restProps}
|
256
|
+
>
|
257
|
+
{formattedValue}
|
258
|
+
{copyable && (
|
259
|
+
<span className="text__copy-indicator" aria-hidden="true">
|
260
|
+
📋
|
261
|
+
</span>
|
262
|
+
)}
|
263
|
+
</Component>
|
264
|
+
)
|
265
|
+
}
|
266
|
+
|
267
|
+
// PropTypes
|
268
|
+
Text.propTypes = {
|
269
|
+
/** Text format */
|
270
|
+
format: PropTypes.oneOf(Object.values(TEXTFORMATS)),
|
271
|
+
/** Text content */
|
272
|
+
children: PropTypes.oneOfType([
|
273
|
+
PropTypes.string,
|
274
|
+
PropTypes.number,
|
275
|
+
PropTypes.node
|
276
|
+
]),
|
277
|
+
/** Additional CSS classes */
|
278
|
+
className: PropTypes.string,
|
279
|
+
/** Text size */
|
280
|
+
size: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', 'xxl']),
|
281
|
+
/** Font weight */
|
282
|
+
weight: PropTypes.oneOf(['light', 'normal', 'medium', 'semibold', 'bold']),
|
283
|
+
/** Text color */
|
284
|
+
color: PropTypes.oneOf(['primary', 'secondary', 'success', 'warning', 'error', 'muted']),
|
285
|
+
/** Text alignment */
|
286
|
+
align: PropTypes.oneOf(['left', 'center', 'right', 'justify']),
|
287
|
+
/** Text transform */
|
288
|
+
transform: PropTypes.oneOf(['none', 'capitalize', 'uppercase', 'lowercase']),
|
289
|
+
/** Text decoration */
|
290
|
+
decoration: PropTypes.oneOf(['none', 'underline', 'line-through']),
|
291
|
+
/** Enable truncation */
|
292
|
+
truncate: PropTypes.bool,
|
293
|
+
/** Maximum length for truncation */
|
294
|
+
maxLength: PropTypes.number,
|
295
|
+
/** Locale for formatting */
|
296
|
+
locale: PropTypes.string,
|
297
|
+
/** Currency code for currency format */
|
298
|
+
currency: PropTypes.string,
|
299
|
+
/** Minimum fraction digits for numbers */
|
300
|
+
minimumFractionDigits: PropTypes.number,
|
301
|
+
/** Maximum fraction digits for numbers */
|
302
|
+
maximumFractionDigits: PropTypes.number,
|
303
|
+
/** Date formatting style */
|
304
|
+
dateStyle: PropTypes.oneOf(['full', 'long', 'medium', 'short']),
|
305
|
+
/** Time formatting style */
|
306
|
+
timeStyle: PropTypes.oneOf(['full', 'long', 'medium', 'short']),
|
307
|
+
/** Fallback text for empty values */
|
308
|
+
fallback: PropTypes.string,
|
309
|
+
/** Loading state */
|
310
|
+
loading: PropTypes.bool,
|
311
|
+
/** Skeleton placeholder */
|
312
|
+
skeleton: PropTypes.bool,
|
313
|
+
/** Enable copy functionality */
|
314
|
+
copyable: PropTypes.bool,
|
315
|
+
/** Enable text selection */
|
316
|
+
selectable: PropTypes.bool,
|
317
|
+
/** HTML element to render as */
|
318
|
+
as: PropTypes.string,
|
319
|
+
/** Inline styles */
|
320
|
+
style: PropTypes.object,
|
321
|
+
/** Click handler */
|
322
|
+
onClick: PropTypes.func
|
47
323
|
}
|
48
324
|
|
325
|
+
Text.defaultProps = {
|
326
|
+
format: TEXTFORMATS.HTML,
|
327
|
+
currency: 'USD',
|
328
|
+
dateStyle: 'medium',
|
329
|
+
timeStyle: 'medium',
|
330
|
+
fallback: '',
|
331
|
+
loading: false,
|
332
|
+
skeleton: false,
|
333
|
+
copyable: false,
|
334
|
+
selectable: true,
|
335
|
+
as: 'span'
|
336
|
+
}
|
337
|
+
|
338
|
+
export default Text
|
339
|
+
|