ywana-core8 0.1.75 → 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.
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 +7900 -1615
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.css +6094 -1122
  23. package/dist/index.css.map +1 -1
  24. package/dist/index.modern.js +7929 -1645
  25. package/dist/index.modern.js.map +1 -1
  26. package/dist/index.umd.js +7900 -1615
  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 +1 -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 +288 -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 +842 -0
  106. package/src/html/textfield2.example.js +499 -0
  107. package/src/html/textfield2.js +1130 -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 +228 -0
  118. package/src/html/tree.example.js +475 -0
  119. package/src/html/tree.js +712 -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
@@ -0,0 +1,378 @@
1
+ import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react'
2
+ import { Fragment } from 'react'
3
+ import PropTypes from 'prop-types'
4
+ import { Icon } from './icon'
5
+ import { Text, TEXTFORMATS } from './text'
6
+ import './tab.css'
7
+
8
+ /**
9
+ * Enhanced Tabs component with improved functionality while maintaining 100% compatibility
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
33
+
34
+ const tabsRef = useRef(null)
35
+
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
64
+
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
+ }
74
+ }
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) => {
82
+ return React.cloneElement(child, {
83
+ selected: index === selected || selected === child.props.id,
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
92
+ })
93
+ })
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
+
153
+ return (
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" />}
163
+ {tabs}
164
+ {fillRight && <div className="tab-filler" />}
165
+ </div>
166
+ )
167
+ }
168
+
169
+ /**
170
+ * Enhanced Tab component with improved functionality while maintaining 100% compatibility
171
+ */
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
195
+
196
+ const tabRef = useRef(null)
197
+
198
+ // Handle selection (maintaining original behavior)
199
+ const handleSelect = useCallback((event) => {
200
+ if (disabled) return
201
+
202
+ event.preventDefault()
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}`
255
+ }
256
+
257
+ // Render label with proper formatting (maintaining original logic)
258
+ const labelTxt = label ? <Text format={TEXTFORMATS.STRING}>{label}</Text> : null
259
+
260
+ return (
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
+ )}
298
+ </div>
299
+ )
300
+ }
301
+
302
+ /**
303
+ * Enhanced Stack component with improved functionality while maintaining 100% compatibility
304
+ */
305
+ export const Stack = (props) => {
306
+ const {
307
+ selected = 0,
308
+ children,
309
+ // New enhanced props (all optional for compatibility)
310
+ lazy = false,
311
+ keepMounted = false,
312
+ animated = true,
313
+ className,
314
+ style,
315
+ ...restProps
316
+ } = props
317
+
318
+ const [mountedTabs, setMountedTabs] = useState(new Set([selected]))
319
+
320
+ // Track which tabs have been mounted for lazy loading
321
+ useEffect(() => {
322
+ if (lazy || keepMounted) {
323
+ setMountedTabs(prev => new Set([...prev, selected]))
324
+ }
325
+ }, [selected, lazy, keepMounted])
326
+
327
+ const notNullChildren = React.Children.toArray(children).filter(child => child !== null)
328
+
329
+ // Generate CSS classes
330
+ const cssClasses = [
331
+ 'stack',
332
+ animated && 'stack--animated',
333
+ className
334
+ ].filter(Boolean).join(' ')
335
+
336
+ if (lazy) {
337
+ // Lazy loading: only render mounted tabs
338
+ return (
339
+ <div className={cssClasses} style={style} {...restProps}>
340
+ {notNullChildren.map((child, index) => {
341
+ const isSelected = index === selected
342
+ const shouldRender = mountedTabs.has(index) || isSelected
343
+
344
+ if (!shouldRender) return null
345
+
346
+ return (
347
+ <div
348
+ key={index}
349
+ className={`stack__panel ${isSelected ? 'stack__panel--active' : 'stack__panel--hidden'}`}
350
+ role="tabpanel"
351
+ aria-labelledby={`tab-${index}`}
352
+ id={`tabpanel-${index}`}
353
+ hidden={!isSelected}
354
+ >
355
+ {child}
356
+ </div>
357
+ )
358
+ })}
359
+ </div>
360
+ )
361
+ }
362
+
363
+ // Original behavior: render selected child only
364
+ const child = notNullChildren.filter((child, index) => index === selected)[0]
365
+
366
+ return (
367
+ <div
368
+ className={cssClasses}
369
+ style={style}
370
+ role="tabpanel"
371
+ aria-labelledby={`tab-${selected}`}
372
+ id={`tabpanel-${selected}`}
373
+ {...restProps}
374
+ >
375
+ {child}
376
+ </div>
377
+ )
378
+ }