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
package/src/html/tab.js CHANGED
@@ -1,57 +1,300 @@
1
- import React, { useState } from 'react'
1
+ import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react'
2
2
  import { Fragment } from 'react'
3
+ import PropTypes from 'prop-types'
3
4
  import { Icon } from './icon'
4
5
  import { Text, TEXTFORMATS } from './text'
5
6
  import './tab.css'
6
7
 
7
8
  /**
8
- * Tabs
9
+ * Enhanced Tabs component with improved functionality while maintaining 100% compatibility
9
10
  */
10
11
  export const Tabs = (props) => {
12
+ const {
13
+ children,
14
+ selected,
15
+ onChange,
16
+ fillLeft = false,
17
+ fillRight = true,
18
+ // New enhanced props (all optional for compatibility)
19
+ orientation = 'horizontal',
20
+ variant = 'standard',
21
+ scrollable = false,
22
+ centered = false,
23
+ disabled = false,
24
+ animated = true,
25
+ persistent = false,
26
+ persistKey,
27
+ beforeChange,
28
+ className,
29
+ style,
30
+ ariaLabel,
31
+ ...restProps
32
+ } = props
11
33
 
12
- const { children, selected, onChange, fillLeft=false, fillRight=true } = props
34
+ const tabsRef = useRef(null)
13
35
 
14
- const notNullChildren = React.Children.toArray(children).filter(child => child !== null)
15
- const tabs = notNullChildren.map((child, index) => {
36
+ // Validate props
37
+ if (children && !React.Children.count(children)) {
38
+ console.warn('Tabs component: children prop should contain Tab components')
39
+ }
40
+
41
+ // Handle persistent tabs (remember selected tab)
42
+ useEffect(() => {
43
+ if (persistent && persistKey && typeof Storage !== 'undefined') {
44
+ const savedTab = localStorage.getItem(`tabs-${persistKey}`)
45
+ if (savedTab !== null && onChange) {
46
+ const parsedTab = isNaN(savedTab) ? savedTab : parseInt(savedTab)
47
+ if (parsedTab !== selected) {
48
+ onChange(parsedTab)
49
+ }
50
+ }
51
+ }
52
+ }, [persistent, persistKey])
53
+
54
+ // Save selected tab to localStorage
55
+ useEffect(() => {
56
+ if (persistent && persistKey && typeof Storage !== 'undefined' && selected !== undefined) {
57
+ localStorage.setItem(`tabs-${persistKey}`, selected.toString())
58
+ }
59
+ }, [selected, persistent, persistKey])
60
+
61
+ // Enhanced select function with validation
62
+ const handleSelect = useCallback(async (id, index) => {
63
+ if (disabled) return
16
64
 
17
- function select(id) {
18
- if (onChange) onChange(id || index)
65
+ // Call beforeChange hook if provided
66
+ if (beforeChange) {
67
+ try {
68
+ const canChange = await beforeChange(id || index, selected)
69
+ if (canChange === false) return
70
+ } catch (error) {
71
+ console.warn('Tabs beforeChange hook error:', error)
72
+ return
73
+ }
19
74
  }
20
75
 
76
+ if (onChange) onChange(id || index)
77
+ }, [disabled, beforeChange, onChange, selected])
78
+
79
+ // Process children (maintaining original logic)
80
+ const notNullChildren = React.Children.toArray(children).filter(child => child !== null)
81
+ const tabs = notNullChildren.map((child, index) => {
21
82
  return React.cloneElement(child, {
22
83
  selected: index === selected || selected === child.props.id,
23
- onSelect: select
84
+ onSelect: (id) => handleSelect(id, index),
85
+ // Pass enhanced props to Tab
86
+ orientation,
87
+ variant,
88
+ disabled: disabled || child.props.disabled,
89
+ animated,
90
+ tabIndex: index,
91
+ totalTabs: notNullChildren.length
24
92
  })
25
93
  })
26
94
 
95
+ // Generate CSS classes
96
+ const cssClasses = [
97
+ 'tabs',
98
+ `tabs--${orientation}`,
99
+ `tabs--${variant}`,
100
+ scrollable && 'tabs--scrollable',
101
+ centered && 'tabs--centered',
102
+ disabled && 'tabs--disabled',
103
+ animated && 'tabs--animated',
104
+ className
105
+ ].filter(Boolean).join(' ')
106
+
107
+ // Accessibility attributes
108
+ const ariaAttributes = {
109
+ 'aria-label': ariaLabel || 'Tabs',
110
+ 'aria-disabled': disabled,
111
+ 'aria-orientation': orientation,
112
+ role: 'tablist'
113
+ }
114
+
115
+ // Handle keyboard navigation
116
+ const handleKeyDown = useCallback((event) => {
117
+ if (disabled) return
118
+
119
+ const currentIndex = typeof selected === 'number' ? selected :
120
+ notNullChildren.findIndex(child => child.props.id === selected)
121
+
122
+ let newIndex = currentIndex
123
+
124
+ switch (event.key) {
125
+ case 'ArrowLeft':
126
+ case 'ArrowUp':
127
+ event.preventDefault()
128
+ newIndex = currentIndex > 0 ? currentIndex - 1 : notNullChildren.length - 1
129
+ break
130
+ case 'ArrowRight':
131
+ case 'ArrowDown':
132
+ event.preventDefault()
133
+ newIndex = currentIndex < notNullChildren.length - 1 ? currentIndex + 1 : 0
134
+ break
135
+ case 'Home':
136
+ event.preventDefault()
137
+ newIndex = 0
138
+ break
139
+ case 'End':
140
+ event.preventDefault()
141
+ newIndex = notNullChildren.length - 1
142
+ break
143
+ default:
144
+ return
145
+ }
146
+
147
+ const targetChild = notNullChildren[newIndex]
148
+ if (targetChild && !targetChild.props.disabled) {
149
+ handleSelect(targetChild.props.id, newIndex)
150
+ }
151
+ }, [disabled, selected, notNullChildren, handleSelect])
152
+
27
153
  return (
28
- <div className="tabs">
29
- { fillLeft ? <div className="tab-filler" /> : null }
154
+ <div
155
+ className={cssClasses}
156
+ style={style}
157
+ ref={tabsRef}
158
+ onKeyDown={handleKeyDown}
159
+ {...ariaAttributes}
160
+ {...restProps}
161
+ >
162
+ {fillLeft && <div className="tab-filler" />}
30
163
  {tabs}
31
- { fillRight ? <div className="tab-filler" /> : null }
164
+ {fillRight && <div className="tab-filler" />}
32
165
  </div>
33
166
  )
34
167
  }
35
168
 
36
169
  /**
37
- * Tab
170
+ * Enhanced Tab component with improved functionality while maintaining 100% compatibility
38
171
  */
39
172
  export const Tab = (props) => {
173
+ const {
174
+ id,
175
+ icon,
176
+ label,
177
+ selected,
178
+ actions,
179
+ onSelect,
180
+ // New enhanced props (all optional for compatibility)
181
+ disabled = false,
182
+ closeable = false,
183
+ onClose,
184
+ badge,
185
+ tooltip,
186
+ orientation = 'horizontal',
187
+ variant = 'standard',
188
+ animated = true,
189
+ tabIndex,
190
+ totalTabs,
191
+ className,
192
+ style,
193
+ ...restProps
194
+ } = props
40
195
 
41
- const { id, icon, label, selected, actions, onSelect } = props
196
+ const tabRef = useRef(null)
42
197
 
43
- function select() {
198
+ // Handle selection (maintaining original behavior)
199
+ const handleSelect = useCallback((event) => {
200
+ if (disabled) return
201
+
202
+ event.preventDefault()
44
203
  if (onSelect) onSelect(id)
204
+ }, [disabled, onSelect, id])
205
+
206
+ // Handle close
207
+ const handleClose = useCallback((event) => {
208
+ event.stopPropagation()
209
+ if (onClose) onClose(id)
210
+ }, [onClose, id])
211
+
212
+ // Handle keyboard interaction
213
+ const handleKeyDown = useCallback((event) => {
214
+ if (disabled) return
215
+
216
+ switch (event.key) {
217
+ case 'Enter':
218
+ case ' ':
219
+ event.preventDefault()
220
+ if (onSelect) onSelect(id)
221
+ break
222
+ case 'Delete':
223
+ case 'Backspace':
224
+ if (closeable && onClose) {
225
+ event.preventDefault()
226
+ onClose(id)
227
+ }
228
+ break
229
+ default:
230
+ break
231
+ }
232
+ }, [disabled, onSelect, id, closeable, onClose])
233
+
234
+ // Generate CSS classes
235
+ const cssClasses = [
236
+ 'tab',
237
+ selected && 'selected',
238
+ disabled && 'tab--disabled',
239
+ closeable && 'tab--closeable',
240
+ animated && 'tab--animated',
241
+ `tab--${orientation}`,
242
+ `tab--${variant}`,
243
+ className
244
+ ].filter(Boolean).join(' ')
245
+
246
+ // Accessibility attributes
247
+ const ariaAttributes = {
248
+ 'aria-selected': selected,
249
+ 'aria-disabled': disabled,
250
+ 'aria-controls': `tabpanel-${id || tabIndex}`,
251
+ 'aria-label': typeof label === 'string' ? label : `Tab ${(tabIndex || 0) + 1}`,
252
+ role: 'tab',
253
+ tabIndex: selected ? 0 : -1,
254
+ id: `tab-${id || tabIndex}`
45
255
  }
46
256
 
47
- const style = selected ? "selected" : ""
257
+ // Render label with proper formatting (maintaining original logic)
48
258
  const labelTxt = label ? <Text format={TEXTFORMATS.STRING}>{label}</Text> : null
49
259
 
50
260
  return (
51
- <div className={`tab ${style}`} onClick={select}>
52
- {icon ? <Icon icon={icon} size="small" /> : null}
53
- {labelTxt}
54
- {actions ? actions : null}
261
+ <div
262
+ className={cssClasses}
263
+ style={style}
264
+ onClick={handleSelect}
265
+ onKeyDown={handleKeyDown}
266
+ ref={tabRef}
267
+ title={tooltip}
268
+ {...ariaAttributes}
269
+ {...restProps}
270
+ >
271
+ {/* Icon (maintaining original structure) */}
272
+ {icon && <Icon icon={icon} size="small" disabled={disabled} />}
273
+
274
+ {/* Label with badge */}
275
+ <div className="tab__content">
276
+ {labelTxt}
277
+ {badge && (
278
+ <span className="tab__badge">
279
+ {typeof badge === 'number' && badge > 99 ? '99+' : badge}
280
+ </span>
281
+ )}
282
+ </div>
283
+
284
+ {/* Actions (maintaining original structure) */}
285
+ {actions && <div className="tab__actions">{actions}</div>}
286
+
287
+ {/* Close button */}
288
+ {closeable && (
289
+ <button
290
+ className="tab__close"
291
+ onClick={handleClose}
292
+ aria-label={`Close ${typeof label === 'string' ? label : 'tab'}`}
293
+ tabIndex={-1}
294
+ >
295
+ <Icon icon="close" size="small" />
296
+ </button>
297
+ )}
55
298
  </div>
56
299
  )
57
300
  }
@@ -65,11 +308,133 @@ export const Tab = (props) => {
65
308
 
66
309
  const notNullChildren = React.Children.toArray(props.children).filter(child => child !== null)
67
310
 
68
- const child = notNullChildren.filter((child, index) => index === selected )[0]
69
-
311
+ // Original behavior: render selected child only (maintaining compatibility)
312
+ const child = notNullChildren.filter((child, index) => index === selected)[0]
313
+
70
314
  return (
71
315
  <Fragment>
72
316
  {child}
73
317
  </Fragment>
74
318
  )
75
- }
319
+ }
320
+
321
+ // PropTypes for Tabs component
322
+ Tabs.propTypes = {
323
+ /** Child Tab components */
324
+ children: PropTypes.node,
325
+ /** Selected tab index or id */
326
+ selected: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
327
+ /** Change callback */
328
+ onChange: PropTypes.func,
329
+ /** Fill space on the left */
330
+ fillLeft: PropTypes.bool,
331
+ /** Fill space on the right */
332
+ fillRight: PropTypes.bool,
333
+ /** Tab orientation */
334
+ orientation: PropTypes.oneOf(['horizontal', 'vertical']),
335
+ /** Tab variant */
336
+ variant: PropTypes.oneOf(['standard', 'scrollable', 'fullWidth']),
337
+ /** Enable scrollable tabs */
338
+ scrollable: PropTypes.bool,
339
+ /** Center tabs */
340
+ centered: PropTypes.bool,
341
+ /** Disabled state */
342
+ disabled: PropTypes.bool,
343
+ /** Enable animations */
344
+ animated: PropTypes.bool,
345
+ /** Persist selected tab in localStorage */
346
+ persistent: PropTypes.bool,
347
+ /** Key for localStorage persistence */
348
+ persistKey: PropTypes.string,
349
+ /** Before change validation hook */
350
+ beforeChange: PropTypes.func,
351
+ /** Additional CSS classes */
352
+ className: PropTypes.string,
353
+ /** Inline styles */
354
+ style: PropTypes.object,
355
+ /** ARIA label */
356
+ ariaLabel: PropTypes.string
357
+ }
358
+
359
+ Tabs.defaultProps = {
360
+ fillLeft: false,
361
+ fillRight: true,
362
+ orientation: 'horizontal',
363
+ variant: 'standard',
364
+ scrollable: false,
365
+ centered: false,
366
+ disabled: false,
367
+ animated: true,
368
+ persistent: false
369
+ }
370
+
371
+ // PropTypes for Tab component
372
+ Tab.propTypes = {
373
+ /** Tab identifier */
374
+ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
375
+ /** Icon name */
376
+ icon: PropTypes.string,
377
+ /** Tab label */
378
+ label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
379
+ /** Selected state */
380
+ selected: PropTypes.bool,
381
+ /** Action elements */
382
+ actions: PropTypes.node,
383
+ /** Selection callback */
384
+ onSelect: PropTypes.func,
385
+ /** Disabled state */
386
+ disabled: PropTypes.bool,
387
+ /** Closeable tab */
388
+ closeable: PropTypes.bool,
389
+ /** Close callback */
390
+ onClose: PropTypes.func,
391
+ /** Badge content */
392
+ badge: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.node]),
393
+ /** Tooltip text */
394
+ tooltip: PropTypes.string,
395
+ /** Tab orientation */
396
+ orientation: PropTypes.oneOf(['horizontal', 'vertical']),
397
+ /** Tab variant */
398
+ variant: PropTypes.oneOf(['standard', 'scrollable', 'fullWidth']),
399
+ /** Enable animations */
400
+ animated: PropTypes.bool,
401
+ /** Additional CSS classes */
402
+ className: PropTypes.string,
403
+ /** Inline styles */
404
+ style: PropTypes.object
405
+ }
406
+
407
+ Tab.defaultProps = {
408
+ disabled: false,
409
+ closeable: false,
410
+ orientation: 'horizontal',
411
+ variant: 'standard',
412
+ animated: true
413
+ }
414
+
415
+ // PropTypes for Stack component
416
+ Stack.propTypes = {
417
+ /** Selected panel index */
418
+ selected: PropTypes.number,
419
+ /** Child panel components */
420
+ children: PropTypes.node,
421
+ /** Enable lazy loading */
422
+ lazy: PropTypes.bool,
423
+ /** Keep mounted panels */
424
+ keepMounted: PropTypes.bool,
425
+ /** Enable animations */
426
+ animated: PropTypes.bool,
427
+ /** Additional CSS classes */
428
+ className: PropTypes.string,
429
+ /** Inline styles */
430
+ style: PropTypes.object
431
+ }
432
+
433
+ Stack.defaultProps = {
434
+ selected: 0,
435
+ lazy: false,
436
+ keepMounted: false,
437
+ animated: true
438
+ }
439
+
440
+ export default Tabs