ywana-core8 0.1.75 → 0.1.77

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.
Files changed (122) hide show
  1. package/ACCORDION_EVALUATION.md +583 -0
  2. package/CHECKBOX_EVALUATION.md +273 -0
  3. package/CHIP_EVALUATION.md +542 -0
  4. package/COLOR_EVALUATION.md +524 -0
  5. package/COMPONENTS_EVALUATION.md +477 -0
  6. package/FORM_EVALUATION.md +459 -0
  7. package/HEADER_EVALUATION.md +436 -0
  8. package/ICON_EVALUATION.md +254 -0
  9. package/LIST_EVALUATION.md +574 -0
  10. package/PROGRESS_EVALUATION.md +450 -0
  11. package/RADIO_EVALUATION.md +439 -0
  12. package/RADIO_VISUAL_FIX.md +183 -0
  13. package/SECTION_IMPROVEMENTS.md +153 -0
  14. package/SWITCH_EVALUATION.md +335 -0
  15. package/SWITCH_VISUAL_FIX.md +232 -0
  16. package/TAB_EVALUATION.md +626 -0
  17. package/TEXTFIELD_EVALUATION.md +747 -0
  18. package/TOOLTIP_FIX.md +157 -0
  19. package/TREE_EVALUATION.md +708 -0
  20. package/dist/index.cjs +10893 -1969
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.css +7768 -1096
  23. package/dist/index.css.map +1 -1
  24. package/dist/index.modern.js +10921 -2005
  25. package/dist/index.modern.js.map +1 -1
  26. package/dist/index.umd.js +10893 -1969
  27. package/dist/index.umd.js.map +1 -1
  28. package/jest.config.js +24 -0
  29. package/package.json +10 -1
  30. package/src/html/accordion.css +208 -4
  31. package/src/html/accordion.example.js +390 -0
  32. package/src/html/accordion.js +284 -28
  33. package/src/html/accordion.unit.test.js +334 -0
  34. package/src/html/button.css +157 -16
  35. package/src/html/button.example.js +374 -0
  36. package/src/html/button.js +240 -60
  37. package/src/html/button.test.js +422 -0
  38. package/src/html/checkbox.css +74 -2
  39. package/src/html/checkbox.example.js +316 -0
  40. package/src/html/checkbox.js +113 -26
  41. package/src/html/checkbox.test.js +285 -0
  42. package/src/html/chip.css +230 -19
  43. package/src/html/chip.example.js +355 -0
  44. package/src/html/chip.js +321 -25
  45. package/src/html/chip.test.js +425 -0
  46. package/src/html/color.css +435 -6
  47. package/src/html/color.example.js +527 -0
  48. package/src/html/color.js +458 -9
  49. package/src/html/color.test.js +362 -4
  50. package/src/html/components.example.js +492 -0
  51. package/src/html/components_enhanced.test.js +581 -0
  52. package/src/html/form.css +70 -3
  53. package/src/html/form.example.js +385 -0
  54. package/src/html/form.js +232 -34
  55. package/src/html/form.test.js +369 -0
  56. package/src/html/header2.css +264 -0
  57. package/src/html/header2.example.js +411 -0
  58. package/src/html/header2.js +203 -0
  59. package/src/html/header2.test.js +377 -0
  60. package/src/html/icon.css +20 -2
  61. package/src/html/icon.example.js +268 -0
  62. package/src/html/icon.js +86 -16
  63. package/src/html/icon.test.js +231 -0
  64. package/src/html/index.js +4 -1
  65. package/src/html/list.css +393 -1
  66. package/src/html/list.example.js +404 -0
  67. package/src/html/list.js +583 -40
  68. package/src/html/list.test.js +383 -0
  69. package/src/html/progress.css +707 -17
  70. package/src/html/progress.example.js +424 -0
  71. package/src/html/progress.js +906 -9
  72. package/src/html/progress.test.js +313 -0
  73. package/src/html/property.css +399 -0
  74. package/src/html/property.example.js +553 -0
  75. package/src/html/property.js +393 -15
  76. package/src/html/property.test.js +351 -2
  77. package/src/html/radio-visual-test.js +289 -0
  78. package/src/html/radio.css +137 -11
  79. package/src/html/radio.example.js +389 -0
  80. package/src/html/radio.js +234 -10
  81. package/src/html/radio.test.js +318 -0
  82. package/src/html/section.example.js +99 -0
  83. package/src/html/section.js +40 -3
  84. package/src/html/section.test.js +131 -0
  85. package/src/html/selector.css +329 -3
  86. package/src/html/selector.js +369 -23
  87. package/src/html/switch-debug.js +197 -0
  88. package/src/html/switch-test-visual.js +294 -0
  89. package/src/html/switch.css +200 -0
  90. package/src/html/switch.example.js +461 -0
  91. package/src/html/switch.js +283 -23
  92. package/src/html/switch.test.js +355 -0
  93. package/src/html/tab.css +289 -0
  94. package/src/html/tab.example.js +446 -0
  95. package/src/html/tab.js +387 -22
  96. package/src/html/tab_enhanced.js +378 -0
  97. package/src/html/tab_enhanced.test.js +504 -0
  98. package/src/html/table2.css +576 -0
  99. package/src/html/table2.example.js +703 -0
  100. package/src/html/table2.js +1252 -0
  101. package/src/html/table2.migration.md +328 -0
  102. package/src/html/table2.test.js +582 -0
  103. package/src/html/text.css +375 -0
  104. package/src/html/text.js +311 -20
  105. package/src/html/textfield2.css +841 -0
  106. package/src/html/textfield2.example.js +1370 -0
  107. package/src/html/textfield2.js +1143 -0
  108. package/src/html/textfield2.test.js +950 -0
  109. package/src/html/thumbnail.css +289 -2
  110. package/src/html/thumbnail.js +214 -9
  111. package/src/html/tokenfield.css +449 -1
  112. package/src/html/tokenfield.example.js +503 -0
  113. package/src/html/tokenfield.js +561 -56
  114. package/src/html/tokenfield.test.js +423 -0
  115. package/src/html/tooltip-positioning-demo.js +187 -0
  116. package/src/html/tooltip.css +25 -2
  117. package/src/html/tree.css +240 -10
  118. package/src/html/tree.example.js +475 -0
  119. package/src/html/tree.js +714 -28
  120. package/src/html/tree_enhanced.test.js +495 -0
  121. package/table2.test.js +454 -0
  122. package/src/html/button.tsx +0 -38
@@ -1,28 +1,925 @@
1
- import React from 'react'
1
+ import React, { useState, useEffect, useCallback, useMemo } from 'react'
2
+ import PropTypes from 'prop-types'
2
3
  import { Icon } from './icon'
3
4
  import './progress.css'
4
5
 
5
6
  /**
6
- * Circular Progress
7
+ * Enhanced Circular Progress component with professional features
7
8
  */
8
9
  export const CircularProgress = (props) => {
10
+ const {
11
+ value = 0,
12
+ max = 100,
13
+ size = 'medium',
14
+ thickness = 4,
15
+ variant = 'indeterminate',
16
+ color = 'primary',
17
+ showValue = false,
18
+ showLabel = false,
19
+ label,
20
+ icon,
21
+ animated = true,
22
+ disabled = false,
23
+ className,
24
+ style,
25
+ children,
26
+ onComplete,
27
+ formatValue,
28
+ ariaLabel,
29
+ ...restProps
30
+ } = props
31
+
32
+ const [animatedValue, setAnimatedValue] = useState(0)
33
+ const [isComplete, setIsComplete] = useState(false)
34
+
35
+ // Animate value changes
36
+ useEffect(() => {
37
+ if (variant === 'determinate' && animated) {
38
+ const startValue = animatedValue
39
+ const endValue = Math.min(Math.max(value, 0), max)
40
+ const duration = 300
41
+ const startTime = Date.now()
42
+
43
+ const animate = () => {
44
+ const elapsed = Date.now() - startTime
45
+ const progress = Math.min(elapsed / duration, 1)
46
+ const easeOutQuart = 1 - Math.pow(1 - progress, 4)
47
+ const currentValue = startValue + (endValue - startValue) * easeOutQuart
48
+
49
+ setAnimatedValue(currentValue)
50
+
51
+ if (progress < 1) {
52
+ requestAnimationFrame(animate)
53
+ } else {
54
+ if (endValue >= max && !isComplete) {
55
+ setIsComplete(true)
56
+ if (onComplete) onComplete()
57
+ }
58
+ }
59
+ }
60
+
61
+ requestAnimationFrame(animate)
62
+ } else {
63
+ setAnimatedValue(value)
64
+ }
65
+ }, [value, max, variant, animated, animatedValue, isComplete, onComplete])
66
+
67
+ // Calculate dimensions based on size
68
+ const dimensions = useMemo(() => {
69
+ const sizes = {
70
+ small: { size: 24, strokeWidth: 2 },
71
+ medium: { size: 40, strokeWidth: 3 },
72
+ large: { size: 56, strokeWidth: 4 },
73
+ xlarge: { size: 80, strokeWidth: 5 }
74
+ }
75
+ return sizes[size] || sizes.medium
76
+ }, [size])
77
+
78
+ // Calculate circle properties
79
+ const radius = (dimensions.size - thickness) / 2
80
+ const circumference = 2 * Math.PI * radius
81
+ const strokeDasharray = circumference
82
+ const strokeDashoffset = variant === 'determinate'
83
+ ? circumference - (animatedValue / max) * circumference
84
+ : 0
85
+
86
+ // Format display value
87
+ const displayValue = useMemo(() => {
88
+ if (formatValue) {
89
+ return formatValue(animatedValue, max)
90
+ }
91
+ return variant === 'determinate'
92
+ ? `${Math.round((animatedValue / max) * 100)}%`
93
+ : ''
94
+ }, [animatedValue, max, formatValue, variant])
95
+
96
+ // Generate CSS classes
97
+ const cssClasses = [
98
+ 'circular-progress',
99
+ `circular-progress--${size}`,
100
+ `circular-progress--${variant}`,
101
+ `circular-progress--${color}`,
102
+ disabled && 'circular-progress--disabled',
103
+ isComplete && 'circular-progress--complete',
104
+ animated && 'circular-progress--animated',
105
+ className
106
+ ].filter(Boolean).join(' ')
107
+
108
+ // Accessibility attributes
109
+ const ariaAttributes = {
110
+ 'aria-label': ariaLabel || (variant === 'determinate' ? `Progress: ${displayValue}` : 'Loading'),
111
+ 'aria-valuenow': variant === 'determinate' ? animatedValue : undefined,
112
+ 'aria-valuemin': variant === 'determinate' ? 0 : undefined,
113
+ 'aria-valuemax': variant === 'determinate' ? max : undefined,
114
+ 'aria-valuetext': variant === 'determinate' ? displayValue : 'Loading',
115
+ role: 'progressbar'
116
+ }
117
+
9
118
  return (
10
- <div className="circular-progress">
11
- <Icon icon="refresh" size="small" />
119
+ <div
120
+ className={cssClasses}
121
+ style={{
122
+ width: dimensions.size,
123
+ height: dimensions.size,
124
+ ...style
125
+ }}
126
+ {...ariaAttributes}
127
+ {...restProps}
128
+ >
129
+ <svg
130
+ className="circular-progress__svg"
131
+ width={dimensions.size}
132
+ height={dimensions.size}
133
+ viewBox={`0 0 ${dimensions.size} ${dimensions.size}`}
134
+ >
135
+ {/* Background circle */}
136
+ <circle
137
+ className="circular-progress__background"
138
+ cx={dimensions.size / 2}
139
+ cy={dimensions.size / 2}
140
+ r={radius}
141
+ fill="none"
142
+ strokeWidth={thickness}
143
+ />
144
+
145
+ {/* Progress circle */}
146
+ <circle
147
+ className="circular-progress__progress"
148
+ cx={dimensions.size / 2}
149
+ cy={dimensions.size / 2}
150
+ r={radius}
151
+ fill="none"
152
+ strokeWidth={thickness}
153
+ strokeDasharray={strokeDasharray}
154
+ strokeDashoffset={strokeDashoffset}
155
+ strokeLinecap="round"
156
+ transform={`rotate(-90 ${dimensions.size / 2} ${dimensions.size / 2})`}
157
+ />
158
+ </svg>
159
+
160
+ {/* Content overlay */}
161
+ <div className="circular-progress__content">
162
+ {icon && (
163
+ <Icon
164
+ icon={icon}
165
+ size={size === 'small' ? 'small' : 'medium'}
166
+ className="circular-progress__icon"
167
+ />
168
+ )}
169
+
170
+ {showValue && variant === 'determinate' && (
171
+ <span className="circular-progress__value">
172
+ {displayValue}
173
+ </span>
174
+ )}
175
+
176
+ {children}
177
+ </div>
178
+
179
+ {(showLabel || label) && (
180
+ <div className="circular-progress__label">
181
+ {label || (variant === 'determinate' ? displayValue : 'Loading...')}
182
+ </div>
183
+ )}
12
184
  </div>
13
185
  )
14
186
  }
15
187
 
16
188
  /**
17
- * Linear Progress
189
+ * Enhanced Linear Progress component with professional features
18
190
  */
19
191
  export const LinearProgress = (props) => {
192
+ const {
193
+ value = 0,
194
+ max = 100,
195
+ variant = 'determinate',
196
+ color = 'primary',
197
+ size = 'medium',
198
+ showValue = false,
199
+ showLabel = false,
200
+ label,
201
+ buffer,
202
+ animated = true,
203
+ striped = false,
204
+ disabled = false,
205
+ rounded = true,
206
+ className,
207
+ style,
208
+ onComplete,
209
+ formatValue,
210
+ ariaLabel,
211
+ estimatedTime,
212
+ speed,
213
+ ...restProps
214
+ } = props
215
+
216
+ const [animatedValue, setAnimatedValue] = useState(0)
217
+ const [animatedBuffer, setAnimatedBuffer] = useState(0)
218
+ const [isComplete, setIsComplete] = useState(false)
219
+ const [timeRemaining, setTimeRemaining] = useState(null)
220
+
221
+ // Animate value changes
222
+ useEffect(() => {
223
+ if (variant === 'determinate' && animated) {
224
+ const startValue = animatedValue
225
+ const endValue = Math.min(Math.max(value, 0), max)
226
+ const duration = 300
227
+ const startTime = Date.now()
228
+
229
+ const animate = () => {
230
+ const elapsed = Date.now() - startTime
231
+ const progress = Math.min(elapsed / duration, 1)
232
+ const easeOutQuart = 1 - Math.pow(1 - progress, 4)
233
+ const currentValue = startValue + (endValue - startValue) * easeOutQuart
234
+
235
+ setAnimatedValue(currentValue)
236
+
237
+ if (progress < 1) {
238
+ requestAnimationFrame(animate)
239
+ } else {
240
+ if (endValue >= max && !isComplete) {
241
+ setIsComplete(true)
242
+ if (onComplete) onComplete()
243
+ }
244
+ }
245
+ }
246
+
247
+ requestAnimationFrame(animate)
248
+ } else {
249
+ setAnimatedValue(value)
250
+ }
251
+ }, [value, max, variant, animated, animatedValue, isComplete, onComplete])
252
+
253
+ // Animate buffer changes
254
+ useEffect(() => {
255
+ if (buffer !== undefined && animated) {
256
+ const startBuffer = animatedBuffer
257
+ const endBuffer = Math.min(Math.max(buffer, 0), max)
258
+ const duration = 200
259
+ const startTime = Date.now()
260
+
261
+ const animate = () => {
262
+ const elapsed = Date.now() - startTime
263
+ const progress = Math.min(elapsed / duration, 1)
264
+ const easeOutQuart = 1 - Math.pow(1 - progress, 4)
265
+ const currentBuffer = startBuffer + (endBuffer - startBuffer) * easeOutQuart
266
+
267
+ setAnimatedBuffer(currentBuffer)
268
+
269
+ if (progress < 1) {
270
+ requestAnimationFrame(animate)
271
+ }
272
+ }
20
273
 
21
- const { progress= 0, max = 100 } = props
274
+ requestAnimationFrame(animate)
275
+ } else if (buffer !== undefined) {
276
+ setAnimatedBuffer(buffer)
277
+ }
278
+ }, [buffer, max, animated, animatedBuffer])
279
+
280
+ // Calculate time remaining
281
+ useEffect(() => {
282
+ if (estimatedTime && variant === 'determinate') {
283
+ const progressPercent = animatedValue / max
284
+ const remaining = progressPercent > 0
285
+ ? estimatedTime * (1 - progressPercent)
286
+ : estimatedTime
287
+ setTimeRemaining(remaining)
288
+ } else if (speed && variant === 'determinate') {
289
+ const remaining = (max - animatedValue) / speed
290
+ setTimeRemaining(remaining)
291
+ }
292
+ }, [animatedValue, max, estimatedTime, speed, variant])
293
+
294
+ // Calculate percentages
295
+ const progressPercent = (animatedValue / max) * 100
296
+ const bufferPercent = buffer !== undefined ? (animatedBuffer / max) * 100 : 0
297
+
298
+ // Format display value
299
+ const displayValue = useMemo(() => {
300
+ if (formatValue) {
301
+ return formatValue(animatedValue, max)
302
+ }
303
+ return variant === 'determinate'
304
+ ? `${Math.round(progressPercent)}%`
305
+ : ''
306
+ }, [animatedValue, max, formatValue, variant, progressPercent])
307
+
308
+ // Format time remaining
309
+ const displayTimeRemaining = useMemo(() => {
310
+ if (!timeRemaining) return ''
311
+
312
+ const minutes = Math.floor(timeRemaining / 60)
313
+ const seconds = Math.floor(timeRemaining % 60)
314
+
315
+ if (minutes > 0) {
316
+ return `${minutes}m ${seconds}s remaining`
317
+ }
318
+ return `${seconds}s remaining`
319
+ }, [timeRemaining])
320
+
321
+ // Generate CSS classes
322
+ const cssClasses = [
323
+ 'linear-progress',
324
+ `linear-progress--${size}`,
325
+ `linear-progress--${variant}`,
326
+ `linear-progress--${color}`,
327
+ striped && 'linear-progress--striped',
328
+ rounded && 'linear-progress--rounded',
329
+ disabled && 'linear-progress--disabled',
330
+ isComplete && 'linear-progress--complete',
331
+ animated && 'linear-progress--animated',
332
+ className
333
+ ].filter(Boolean).join(' ')
334
+
335
+ // Accessibility attributes
336
+ const ariaAttributes = {
337
+ 'aria-label': ariaLabel || (variant === 'determinate' ? `Progress: ${displayValue}` : 'Loading'),
338
+ 'aria-valuenow': variant === 'determinate' ? animatedValue : undefined,
339
+ 'aria-valuemin': variant === 'determinate' ? 0 : undefined,
340
+ 'aria-valuemax': variant === 'determinate' ? max : undefined,
341
+ 'aria-valuetext': variant === 'determinate' ? displayValue : 'Loading',
342
+ role: 'progressbar'
343
+ }
344
+
345
+ return (
346
+ <div className={cssClasses} style={style} {...restProps}>
347
+ {/* Header with label and value */}
348
+ {(showLabel || showValue || label || timeRemaining) && (
349
+ <div className="linear-progress__header">
350
+ <div className="linear-progress__label">
351
+ {label || (showLabel && variant === 'determinate' ? 'Progress' : '')}
352
+ </div>
353
+ <div className="linear-progress__info">
354
+ {showValue && variant === 'determinate' && (
355
+ <span className="linear-progress__value">
356
+ {displayValue}
357
+ </span>
358
+ )}
359
+ {timeRemaining && (
360
+ <span className="linear-progress__time">
361
+ {displayTimeRemaining}
362
+ </span>
363
+ )}
364
+ </div>
365
+ </div>
366
+ )}
367
+
368
+ {/* Progress track */}
369
+ <div className="linear-progress__track" {...ariaAttributes}>
370
+ {/* Buffer bar (if provided) */}
371
+ {buffer !== undefined && (
372
+ <div
373
+ className="linear-progress__buffer"
374
+ style={{ width: `${bufferPercent}%` }}
375
+ />
376
+ )}
377
+
378
+ {/* Progress bar */}
379
+ <div
380
+ className="linear-progress__bar"
381
+ style={{
382
+ width: variant === 'determinate' ? `${progressPercent}%` : undefined
383
+ }}
384
+ >
385
+ {/* Indeterminate animation elements */}
386
+ {variant === 'indeterminate' && (
387
+ <>
388
+ <div className="linear-progress__bar-primary" />
389
+ <div className="linear-progress__bar-secondary" />
390
+ </>
391
+ )}
392
+ </div>
393
+ </div>
394
+ </div>
395
+ )
396
+ }
397
+
398
+ /**
399
+ * Step Progress component for multi-step processes
400
+ */
401
+ export const StepProgress = (props) => {
402
+ const {
403
+ steps = [],
404
+ currentStep = 0,
405
+ variant = 'horizontal',
406
+ color = 'primary',
407
+ size = 'medium',
408
+ showLabels = true,
409
+ showNumbers = true,
410
+ allowClickNavigation = false,
411
+ animated = true,
412
+ className,
413
+ onStepClick,
414
+ ...restProps
415
+ } = props
416
+
417
+ const handleStepClick = useCallback((stepIndex, step) => {
418
+ if (allowClickNavigation && onStepClick && stepIndex <= currentStep) {
419
+ onStepClick(stepIndex, step)
420
+ }
421
+ }, [allowClickNavigation, onStepClick, currentStep])
422
+
423
+ // Generate CSS classes
424
+ const cssClasses = [
425
+ 'step-progress',
426
+ `step-progress--${variant}`,
427
+ `step-progress--${size}`,
428
+ `step-progress--${color}`,
429
+ animated && 'step-progress--animated',
430
+ className
431
+ ].filter(Boolean).join(' ')
22
432
 
23
433
  return (
24
- <div className="linear-progress">
25
- <progress value={progress} max={max}/>
434
+ <div className={cssClasses} {...restProps}>
435
+ {steps.map((step, index) => {
436
+ const isCompleted = index < currentStep
437
+ const isCurrent = index === currentStep
438
+ const isClickable = allowClickNavigation && index <= currentStep
439
+
440
+ const stepClasses = [
441
+ 'step-progress__step',
442
+ isCompleted && 'step-progress__step--completed',
443
+ isCurrent && 'step-progress__step--current',
444
+ isClickable && 'step-progress__step--clickable',
445
+ step.error && 'step-progress__step--error'
446
+ ].filter(Boolean).join(' ')
447
+
448
+ return (
449
+ <div key={step.id || index} className={stepClasses}>
450
+ {/* Connector line (except for last step) */}
451
+ {index < steps.length - 1 && (
452
+ <div className="step-progress__connector" />
453
+ )}
454
+
455
+ {/* Step circle */}
456
+ <div
457
+ className="step-progress__circle"
458
+ onClick={() => handleStepClick(index, step)}
459
+ role={isClickable ? 'button' : undefined}
460
+ tabIndex={isClickable ? 0 : -1}
461
+ aria-label={`Step ${index + 1}: ${step.label}`}
462
+ >
463
+ {step.error ? (
464
+ <Icon icon="error" size="small" />
465
+ ) : isCompleted ? (
466
+ <Icon icon="check" size="small" />
467
+ ) : showNumbers ? (
468
+ <span className="step-progress__number">
469
+ {index + 1}
470
+ </span>
471
+ ) : (
472
+ step.icon && <Icon icon={step.icon} size="small" />
473
+ )}
474
+ </div>
475
+
476
+ {/* Step label */}
477
+ {showLabels && (
478
+ <div className="step-progress__label">
479
+ <div className="step-progress__title">
480
+ {step.label}
481
+ </div>
482
+ {step.description && (
483
+ <div className="step-progress__description">
484
+ {step.description}
485
+ </div>
486
+ )}
487
+ </div>
488
+ )}
489
+ </div>
490
+ )
491
+ })}
26
492
  </div>
27
493
  )
28
- }
494
+ }
495
+
496
+ /**
497
+ * Radial Progress component for dashboard-style displays
498
+ */
499
+ export const RadialProgress = (props) => {
500
+ const {
501
+ value = 0,
502
+ max = 100,
503
+ size = 120,
504
+ thickness = 8,
505
+ color = 'primary',
506
+ backgroundColor = 'rgba(0,0,0,0.1)',
507
+ showValue = true,
508
+ showLabel = false,
509
+ label,
510
+ icon,
511
+ animated = true,
512
+ gradient = false,
513
+ className,
514
+ formatValue,
515
+ children,
516
+ ...restProps
517
+ } = props
518
+
519
+ const [animatedValue, setAnimatedValue] = useState(0)
520
+
521
+ // Animate value changes
522
+ useEffect(() => {
523
+ if (animated) {
524
+ const startValue = animatedValue
525
+ const endValue = Math.min(Math.max(value, 0), max)
526
+ const duration = 800
527
+ const startTime = Date.now()
528
+
529
+ const animate = () => {
530
+ const elapsed = Date.now() - startTime
531
+ const progress = Math.min(elapsed / duration, 1)
532
+ const easeOutCubic = 1 - Math.pow(1 - progress, 3)
533
+ const currentValue = startValue + (endValue - startValue) * easeOutCubic
534
+
535
+ setAnimatedValue(currentValue)
536
+
537
+ if (progress < 1) {
538
+ requestAnimationFrame(animate)
539
+ }
540
+ }
541
+
542
+ requestAnimationFrame(animate)
543
+ } else {
544
+ setAnimatedValue(value)
545
+ }
546
+ }, [value, max, animated, animatedValue])
547
+
548
+ // Calculate circle properties
549
+ const radius = (size - thickness) / 2
550
+ const circumference = 2 * Math.PI * radius
551
+ const strokeDasharray = circumference
552
+ const strokeDashoffset = circumference - (animatedValue / max) * circumference
553
+ const percentage = Math.round((animatedValue / max) * 100)
554
+
555
+ // Format display value
556
+ const displayValue = formatValue ? formatValue(animatedValue, max) : `${percentage}%`
557
+
558
+ // Generate CSS classes
559
+ const cssClasses = [
560
+ 'radial-progress',
561
+ `radial-progress--${color}`,
562
+ gradient && 'radial-progress--gradient',
563
+ animated && 'radial-progress--animated',
564
+ className
565
+ ].filter(Boolean).join(' ')
566
+
567
+ return (
568
+ <div
569
+ className={cssClasses}
570
+ style={{ width: size, height: size }}
571
+ {...restProps}
572
+ >
573
+ <svg
574
+ className="radial-progress__svg"
575
+ width={size}
576
+ height={size}
577
+ viewBox={`0 0 ${size} ${size}`}
578
+ >
579
+ {/* Gradient definition */}
580
+ {gradient && (
581
+ <defs>
582
+ <linearGradient id={`gradient-${color}`} x1="0%" y1="0%" x2="100%" y2="100%">
583
+ <stop offset="0%" stopColor="var(--primary-color-light)" />
584
+ <stop offset="100%" stopColor="var(--primary-color-dark)" />
585
+ </linearGradient>
586
+ </defs>
587
+ )}
588
+
589
+ {/* Background circle */}
590
+ <circle
591
+ className="radial-progress__background"
592
+ cx={size / 2}
593
+ cy={size / 2}
594
+ r={radius}
595
+ fill="none"
596
+ stroke={backgroundColor}
597
+ strokeWidth={thickness}
598
+ />
599
+
600
+ {/* Progress circle */}
601
+ <circle
602
+ className="radial-progress__progress"
603
+ cx={size / 2}
604
+ cy={size / 2}
605
+ r={radius}
606
+ fill="none"
607
+ stroke={gradient ? `url(#gradient-${color})` : undefined}
608
+ strokeWidth={thickness}
609
+ strokeDasharray={strokeDasharray}
610
+ strokeDashoffset={strokeDashoffset}
611
+ strokeLinecap="round"
612
+ transform={`rotate(-90 ${size / 2} ${size / 2})`}
613
+ />
614
+ </svg>
615
+
616
+ {/* Content overlay */}
617
+ <div className="radial-progress__content">
618
+ {icon && (
619
+ <Icon
620
+ icon={icon}
621
+ size="large"
622
+ className="radial-progress__icon"
623
+ />
624
+ )}
625
+
626
+ {showValue && (
627
+ <div className="radial-progress__value">
628
+ {displayValue}
629
+ </div>
630
+ )}
631
+
632
+ {(showLabel || label) && (
633
+ <div className="radial-progress__label">
634
+ {label || 'Progress'}
635
+ </div>
636
+ )}
637
+
638
+ {children}
639
+ </div>
640
+ </div>
641
+ )
642
+ }
643
+
644
+ /**
645
+ * Multi Progress component for showing multiple progress bars
646
+ */
647
+ export const MultiProgress = (props) => {
648
+ const {
649
+ items = [],
650
+ showLabels = true,
651
+ showValues = true,
652
+ size = 'medium',
653
+ animated = true,
654
+ className,
655
+ ...restProps
656
+ } = props
657
+
658
+ const cssClasses = [
659
+ 'multi-progress',
660
+ `multi-progress--${size}`,
661
+ animated && 'multi-progress--animated',
662
+ className
663
+ ].filter(Boolean).join(' ')
664
+
665
+ return (
666
+ <div className={cssClasses} {...restProps}>
667
+ {items.map((item, index) => (
668
+ <div key={item.id || index} className="multi-progress__item">
669
+ {showLabels && (
670
+ <div className="multi-progress__header">
671
+ <span className="multi-progress__label">
672
+ {item.label}
673
+ </span>
674
+ {showValues && (
675
+ <span className="multi-progress__value">
676
+ {item.formatValue ?
677
+ item.formatValue(item.value, item.max) :
678
+ `${Math.round((item.value / item.max) * 100)}%`
679
+ }
680
+ </span>
681
+ )}
682
+ </div>
683
+ )}
684
+ <LinearProgress
685
+ value={item.value}
686
+ max={item.max}
687
+ color={item.color || 'primary'}
688
+ size={size}
689
+ animated={animated}
690
+ className="multi-progress__bar"
691
+ />
692
+ </div>
693
+ ))}
694
+ </div>
695
+ )
696
+ }
697
+
698
+ // PropTypes for all components
699
+ CircularProgress.propTypes = {
700
+ /** Current progress value */
701
+ value: PropTypes.number,
702
+ /** Maximum value */
703
+ max: PropTypes.number,
704
+ /** Size variant */
705
+ size: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
706
+ /** Stroke thickness */
707
+ thickness: PropTypes.number,
708
+ /** Progress variant */
709
+ variant: PropTypes.oneOf(['determinate', 'indeterminate']),
710
+ /** Color theme */
711
+ color: PropTypes.oneOf(['primary', 'secondary', 'success', 'warning', 'error']),
712
+ /** Show percentage value */
713
+ showValue: PropTypes.bool,
714
+ /** Show label below */
715
+ showLabel: PropTypes.bool,
716
+ /** Custom label text */
717
+ label: PropTypes.string,
718
+ /** Icon to display */
719
+ icon: PropTypes.string,
720
+ /** Enable animations */
721
+ animated: PropTypes.bool,
722
+ /** Disabled state */
723
+ disabled: PropTypes.bool,
724
+ /** Additional CSS classes */
725
+ className: PropTypes.string,
726
+ /** Inline styles */
727
+ style: PropTypes.object,
728
+ /** Child elements */
729
+ children: PropTypes.node,
730
+ /** Callback when progress completes */
731
+ onComplete: PropTypes.func,
732
+ /** Custom value formatter */
733
+ formatValue: PropTypes.func,
734
+ /** ARIA label */
735
+ ariaLabel: PropTypes.string
736
+ }
737
+
738
+ CircularProgress.defaultProps = {
739
+ value: 0,
740
+ max: 100,
741
+ size: 'medium',
742
+ thickness: 4,
743
+ variant: 'indeterminate',
744
+ color: 'primary',
745
+ showValue: false,
746
+ showLabel: false,
747
+ animated: true,
748
+ disabled: false
749
+ }
750
+
751
+ LinearProgress.propTypes = {
752
+ /** Current progress value */
753
+ value: PropTypes.number,
754
+ /** Maximum value */
755
+ max: PropTypes.number,
756
+ /** Progress variant */
757
+ variant: PropTypes.oneOf(['determinate', 'indeterminate']),
758
+ /** Color theme */
759
+ color: PropTypes.oneOf(['primary', 'secondary', 'success', 'warning', 'error']),
760
+ /** Size variant */
761
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
762
+ /** Show percentage value */
763
+ showValue: PropTypes.bool,
764
+ /** Show label */
765
+ showLabel: PropTypes.bool,
766
+ /** Custom label text */
767
+ label: PropTypes.string,
768
+ /** Buffer value for buffered progress */
769
+ buffer: PropTypes.number,
770
+ /** Enable animations */
771
+ animated: PropTypes.bool,
772
+ /** Show striped pattern */
773
+ striped: PropTypes.bool,
774
+ /** Disabled state */
775
+ disabled: PropTypes.bool,
776
+ /** Rounded corners */
777
+ rounded: PropTypes.bool,
778
+ /** Additional CSS classes */
779
+ className: PropTypes.string,
780
+ /** Inline styles */
781
+ style: PropTypes.object,
782
+ /** Callback when progress completes */
783
+ onComplete: PropTypes.func,
784
+ /** Custom value formatter */
785
+ formatValue: PropTypes.func,
786
+ /** ARIA label */
787
+ ariaLabel: PropTypes.string,
788
+ /** Estimated time to completion */
789
+ estimatedTime: PropTypes.number,
790
+ /** Progress speed (units per second) */
791
+ speed: PropTypes.number
792
+ }
793
+
794
+ LinearProgress.defaultProps = {
795
+ value: 0,
796
+ max: 100,
797
+ variant: 'determinate',
798
+ color: 'primary',
799
+ size: 'medium',
800
+ showValue: false,
801
+ showLabel: false,
802
+ animated: true,
803
+ striped: false,
804
+ disabled: false,
805
+ rounded: true
806
+ }
807
+
808
+ StepProgress.propTypes = {
809
+ /** Array of step objects */
810
+ steps: PropTypes.arrayOf(PropTypes.shape({
811
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
812
+ label: PropTypes.string.isRequired,
813
+ description: PropTypes.string,
814
+ icon: PropTypes.string,
815
+ error: PropTypes.bool
816
+ })).isRequired,
817
+ /** Current active step index */
818
+ currentStep: PropTypes.number,
819
+ /** Layout variant */
820
+ variant: PropTypes.oneOf(['horizontal', 'vertical']),
821
+ /** Color theme */
822
+ color: PropTypes.oneOf(['primary', 'secondary', 'success', 'warning', 'error']),
823
+ /** Size variant */
824
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
825
+ /** Show step labels */
826
+ showLabels: PropTypes.bool,
827
+ /** Show step numbers */
828
+ showNumbers: PropTypes.bool,
829
+ /** Allow clicking to navigate */
830
+ allowClickNavigation: PropTypes.bool,
831
+ /** Enable animations */
832
+ animated: PropTypes.bool,
833
+ /** Additional CSS classes */
834
+ className: PropTypes.string,
835
+ /** Callback when step is clicked */
836
+ onStepClick: PropTypes.func
837
+ }
838
+
839
+ StepProgress.defaultProps = {
840
+ currentStep: 0,
841
+ variant: 'horizontal',
842
+ color: 'primary',
843
+ size: 'medium',
844
+ showLabels: true,
845
+ showNumbers: true,
846
+ allowClickNavigation: false,
847
+ animated: true
848
+ }
849
+
850
+ RadialProgress.propTypes = {
851
+ /** Current progress value */
852
+ value: PropTypes.number,
853
+ /** Maximum value */
854
+ max: PropTypes.number,
855
+ /** Circle size in pixels */
856
+ size: PropTypes.number,
857
+ /** Stroke thickness */
858
+ thickness: PropTypes.number,
859
+ /** Color theme */
860
+ color: PropTypes.oneOf(['primary', 'secondary', 'success', 'warning', 'error']),
861
+ /** Background color */
862
+ backgroundColor: PropTypes.string,
863
+ /** Show percentage value */
864
+ showValue: PropTypes.bool,
865
+ /** Show label */
866
+ showLabel: PropTypes.bool,
867
+ /** Custom label text */
868
+ label: PropTypes.string,
869
+ /** Icon to display */
870
+ icon: PropTypes.string,
871
+ /** Enable animations */
872
+ animated: PropTypes.bool,
873
+ /** Use gradient colors */
874
+ gradient: PropTypes.bool,
875
+ /** Additional CSS classes */
876
+ className: PropTypes.string,
877
+ /** Custom value formatter */
878
+ formatValue: PropTypes.func,
879
+ /** Child elements */
880
+ children: PropTypes.node
881
+ }
882
+
883
+ RadialProgress.defaultProps = {
884
+ value: 0,
885
+ max: 100,
886
+ size: 120,
887
+ thickness: 8,
888
+ color: 'primary',
889
+ backgroundColor: 'rgba(0,0,0,0.1)',
890
+ showValue: true,
891
+ showLabel: false,
892
+ animated: true,
893
+ gradient: false
894
+ }
895
+
896
+ MultiProgress.propTypes = {
897
+ /** Array of progress items */
898
+ items: PropTypes.arrayOf(PropTypes.shape({
899
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
900
+ label: PropTypes.string.isRequired,
901
+ value: PropTypes.number.isRequired,
902
+ max: PropTypes.number,
903
+ color: PropTypes.string,
904
+ formatValue: PropTypes.func
905
+ })).isRequired,
906
+ /** Show item labels */
907
+ showLabels: PropTypes.bool,
908
+ /** Show item values */
909
+ showValues: PropTypes.bool,
910
+ /** Size variant */
911
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
912
+ /** Enable animations */
913
+ animated: PropTypes.bool,
914
+ /** Additional CSS classes */
915
+ className: PropTypes.string
916
+ }
917
+
918
+ MultiProgress.defaultProps = {
919
+ showLabels: true,
920
+ showValues: true,
921
+ size: 'medium',
922
+ animated: true
923
+ }
924
+
925
+ export default { CircularProgress, LinearProgress, StepProgress, RadialProgress, MultiProgress }