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,101 +1,606 @@
1
- import React, { useState } from 'react'
1
+ import React, { useState, useCallback, useRef, useEffect } from 'react'
2
+ import PropTypes from 'prop-types'
2
3
  import { Icon } from './icon';
3
4
  import { Text } from './text';
4
5
  import { DropDown } from './textfield';
5
6
  import './tokenfield.css'
6
7
 
7
8
  /**
8
- * Token Field
9
+ * Enhanced Token Field component with improved functionality while maintaining 100% compatibility
9
10
  */
10
- export const TokenField = ({ id, label, tokens = [], readOnly, options, predictive=true, onChange }) => {
11
+ export const TokenField = (props) => {
12
+ const {
13
+ // Original props (100% compatible)
14
+ id,
15
+ label,
16
+ tokens = [],
17
+ readOnly,
18
+ options,
19
+ predictive = true,
20
+ onChange,
21
+ // New enhanced props (all optional for compatibility)
22
+ disabled = false,
23
+ required = false,
24
+ placeholder = "Add token...",
25
+ maxTokens,
26
+ minTokens = 0,
27
+ allowDuplicates = true,
28
+ validateToken,
29
+ tokenSeparators = [',', ';', '\n'],
30
+ size = 'medium',
31
+ variant = 'default',
32
+ error,
33
+ helperText,
34
+ onFocus,
35
+ onBlur,
36
+ onTokenAdd,
37
+ onTokenRemove,
38
+ onValidationError,
39
+ clearable = false,
40
+ onClear,
41
+ searchable = false,
42
+ sortable = false,
43
+ className,
44
+ style,
45
+ ...restProps
46
+ } = props
11
47
 
12
- const [value, setValue] = useState()
48
+ const [value, setValue] = useState('')
49
+ const [isFocused, setIsFocused] = useState(false)
50
+ const [validationErrors, setValidationErrors] = useState({})
51
+ const inputRef = useRef(null)
52
+ const containerRef = useRef(null)
53
+
54
+ // Validate props
55
+ useEffect(() => {
56
+ if (!Array.isArray(tokens)) {
57
+ console.warn('TokenField: tokens prop must be an array')
58
+ }
59
+ if (maxTokens && tokens.length > maxTokens) {
60
+ console.warn(`TokenField: tokens count (${tokens.length}) exceeds maxTokens (${maxTokens})`)
61
+ }
62
+ if (minTokens && tokens.length < minTokens) {
63
+ console.warn(`TokenField: tokens count (${tokens.length}) is below minTokens (${minTokens})`)
64
+ }
65
+ }, [tokens, maxTokens, minTokens])
66
+
67
+ // Enhanced remove function (maintaining original behavior)
68
+ const remove = useCallback((index) => {
69
+ if (disabled || readOnly) return
13
70
 
14
- function remove(index) {
15
71
  const next = tokens.slice()
16
- next.splice(index, 1)
72
+ const removedToken = next.splice(index, 1)[0]
73
+
74
+ // Check minimum tokens
75
+ if (minTokens && next.length < minTokens) {
76
+ console.warn(`TokenField: Cannot remove token. Minimum ${minTokens} tokens required.`)
77
+ return
78
+ }
79
+
17
80
  if (onChange) onChange(id, next)
18
- }
81
+ if (onTokenRemove) onTokenRemove(removedToken, index)
82
+ }, [disabled, readOnly, tokens, minTokens, id, onChange, onTokenRemove])
19
83
 
20
- function change(event) {
21
- const value = event.target.value
22
- setValue(value)
23
- }
84
+ // Enhanced change function (maintaining original behavior)
85
+ const change = useCallback((event) => {
86
+ if (disabled || readOnly) return
87
+
88
+ const newValue = event.target.value
89
+ setValue(newValue)
90
+ }, [disabled, readOnly])
91
+
92
+ // Validate token
93
+ const validateTokenValue = useCallback((tokenValue) => {
94
+ if (!tokenValue || (typeof tokenValue === 'string' && tokenValue.trim() === '')) {
95
+ return { isValid: false, error: 'Token cannot be empty' }
96
+ }
97
+
98
+ // Check duplicates
99
+ if (!allowDuplicates && tokens.includes(tokenValue)) {
100
+ return { isValid: false, error: 'Duplicate token not allowed' }
101
+ }
102
+
103
+ // Check max tokens
104
+ if (maxTokens && tokens.length >= maxTokens) {
105
+ return { isValid: false, error: `Maximum ${maxTokens} tokens allowed` }
106
+ }
107
+
108
+ // Custom validation
109
+ if (validateToken) {
110
+ const customValidation = validateToken(tokenValue, tokens)
111
+ if (customValidation && !customValidation.isValid) {
112
+ return customValidation
113
+ }
114
+ }
115
+
116
+ return { isValid: true }
117
+ }, [allowDuplicates, tokens, maxTokens, validateToken])
118
+
119
+ // Add token helper
120
+ const addToken = useCallback((tokenValue) => {
121
+ if (disabled || readOnly) return false
24
122
 
25
- function changeDropDown(fid, value) {
26
-
123
+ const validation = validateTokenValue(tokenValue)
124
+ if (!validation.isValid) {
125
+ setValidationErrors(prev => ({ ...prev, [tokenValue]: validation.error }))
126
+ if (onValidationError) onValidationError(validation.error, tokenValue)
127
+ return false
128
+ }
129
+
130
+ const next = Array.isArray(tokens) ? tokens.concat(tokenValue) : [tokenValue]
131
+ if (onChange) onChange(id, next)
132
+ if (onTokenAdd) onTokenAdd(tokenValue, tokens.length)
133
+
134
+ // Clear validation error
135
+ setValidationErrors(prev => {
136
+ const newErrors = { ...prev }
137
+ delete newErrors[tokenValue]
138
+ return newErrors
139
+ })
140
+
141
+ return true
142
+ }, [disabled, readOnly, validateTokenValue, tokens, id, onChange, onTokenAdd, onValidationError])
143
+
144
+ // Enhanced changeDropDown function (maintaining original behavior)
145
+ const changeDropDown = useCallback((fid, value) => {
27
146
  const isNumericValue = !isNaN(value)
28
147
  const valueIsNotEmpty = (value && value.length > 0) || isNumericValue
29
148
 
30
149
  if (valueIsNotEmpty) {
31
- const next = Array.isArray(tokens) ? tokens.concat(value) : [value]
32
- if (onChange) onChange(id, next)
150
+ addToken(value)
33
151
  }
34
-
152
+
35
153
  setValue('')
36
- }
154
+ }, [addToken])
155
+
156
+ // Enhanced onEnter function (maintaining original behavior + improvements)
157
+ const onEnter = useCallback((event) => {
158
+ if (disabled || readOnly) return
37
159
 
38
- function onEnter(event) {
39
- if (event.key === 'Enter') {
160
+ // Handle separators
161
+ if (tokenSeparators.includes(event.key) || event.key === 'Enter') {
40
162
  event.preventDefault()
41
163
  event.stopPropagation()
42
- const token = event.target.value
164
+
165
+ const token = event.target.value.trim()
43
166
  if (token && token.length > 0) {
44
- const next = Array.isArray(tokens) ? tokens.concat(token) : [token]
45
- if (onChange) onChange(id, next)
46
- setValue('')
167
+ if (addToken(token)) {
168
+ setValue('')
169
+ }
47
170
  }
171
+ return
48
172
  }
49
173
 
174
+ // Handle backspace to remove last token (maintaining original behavior)
50
175
  if (value === '' && tokens.length > 0 && event.key === 'Backspace') {
51
- const next = tokens.slice(0, -1)
52
- setTokens(next)
176
+ event.preventDefault()
177
+ remove(tokens.length - 1)
178
+ return
179
+ }
180
+
181
+ // Handle paste with multiple tokens
182
+ if (event.key === 'v' && (event.ctrlKey || event.metaKey)) {
183
+ // Let the paste event handle this
184
+ setTimeout(() => {
185
+ const pastedValue = event.target.value
186
+ const newTokens = pastedValue.split(new RegExp(`[${tokenSeparators.join('')}]`))
187
+ .map(token => token.trim())
188
+ .filter(token => token.length > 0)
189
+
190
+ if (newTokens.length > 1) {
191
+ event.target.value = ''
192
+ setValue('')
193
+ newTokens.forEach(token => addToken(token))
194
+ }
195
+ }, 0)
196
+ }
197
+ }, [disabled, readOnly, tokenSeparators, value, tokens, addToken, remove])
198
+
199
+ // Handle focus
200
+ const handleFocus = useCallback((event) => {
201
+ setIsFocused(true)
202
+ if (onFocus) onFocus(event)
203
+ }, [onFocus])
204
+
205
+ // Handle blur
206
+ const handleBlur = useCallback((event) => {
207
+ setIsFocused(false)
208
+
209
+ // Add token on blur if there's a value
210
+ const token = event.target.value.trim()
211
+ if (token && token.length > 0) {
212
+ if (addToken(token)) {
213
+ setValue('')
214
+ }
215
+ }
216
+
217
+ if (onBlur) onBlur(event)
218
+ }, [onBlur, addToken])
219
+
220
+ // Handle clear all
221
+ const handleClear = useCallback(() => {
222
+ if (disabled || readOnly || minTokens > 0) return
223
+
224
+ if (onChange) onChange(id, [])
225
+ if (onClear) onClear()
226
+ setValue('')
227
+ }, [disabled, readOnly, minTokens, id, onChange, onClear])
228
+
229
+ // Filter and sort options
230
+ const getFilteredOptions = useCallback(() => {
231
+ if (!options) return []
232
+
233
+ const tks = tokens || []
234
+ const usedValues = tks.map(token => token)
235
+ let filteredOptions = options.filter(option => !usedValues.includes(option.value))
236
+
237
+ if (searchable && value) {
238
+ filteredOptions = filteredOptions.filter(option =>
239
+ option.label?.toLowerCase().includes(value.toLowerCase()) ||
240
+ option.value?.toString().toLowerCase().includes(value.toLowerCase())
241
+ )
242
+ }
243
+
244
+ if (sortable) {
245
+ filteredOptions.sort((a, b) => {
246
+ if (!a.label || !b.label) return 0
247
+ try {
248
+ return a.label.localeCompare(b.label)
249
+ } catch (error) {
250
+ console.log('Error sorting options', error, a, b)
251
+ return 0
252
+ }
253
+ })
53
254
  }
255
+
256
+ return filteredOptions
257
+ }, [options, tokens, searchable, value, sortable])
258
+
259
+ // Generate CSS classes
260
+ const cssClasses = [
261
+ 'tokenField',
262
+ `tokenField--${size}`,
263
+ `tokenField--${variant}`,
264
+ disabled && 'tokenField--disabled',
265
+ readOnly && 'tokenField--readonly',
266
+ required && 'tokenField--required',
267
+ isFocused && 'tokenField--focused',
268
+ error && 'tokenField--error',
269
+ className
270
+ ].filter(Boolean).join(' ')
271
+
272
+ // Generate accessibility attributes
273
+ const ariaAttributes = {
274
+ 'aria-label': label || 'Token field',
275
+ 'aria-disabled': disabled,
276
+ 'aria-readonly': readOnly,
277
+ 'aria-required': required,
278
+ 'aria-invalid': !!error,
279
+ 'aria-describedby': helperText ? `${id}-helper` : undefined,
280
+ role: 'group'
54
281
  }
55
282
 
56
283
  const tks = Array.isArray(tokens) ? tokens : []
57
- const sortedOptions = options ? options.toSorted((a, b) => {
58
- if (!a.label || !b.label) return 0
59
- try {
60
- return a.label.localeCompare(b.label)
61
- } catch (error) {
62
- console.log('Error sorting options', error, a, b)
63
- return 0
64
- }
65
- }) : null
284
+ const sortedOptions = getFilteredOptions()
66
285
 
67
286
  return (
68
- <div className='tokenField'>
69
- <label>{label}</label>
70
-
71
- {tks.map((text, index) => {
72
- let text2 = text
73
- if (options) {
74
- const lbl = options.find(opt => opt.value == text)
75
- if (lbl) text2 = lbl.label
76
- }
77
- return <Token text={text2} onDelete={() => remove(index)} />
78
- })}
287
+ <div
288
+ className={cssClasses}
289
+ style={style}
290
+ ref={containerRef}
291
+ {...ariaAttributes}
292
+ {...restProps}
293
+ >
294
+ {/* Label (maintaining original structure) */}
295
+ <label htmlFor={id}>
296
+ {label}
297
+ {required && <span className="tokenField__required">*</span>}
298
+ </label>
79
299
 
80
- {options ? (
81
- <DropDown onChange={changeDropDown} options={sortedOptions} predictive={predictive} verbose={false} />
82
- ) : (
83
- <input type='text' value={value} onChange={change} onKeyDown={onEnter} readOnly={readOnly} />)
84
- }
300
+ {/* Tokens container */}
301
+ <div className="tokenField__tokens">
302
+ {tks.map((text, index) => {
303
+ let text2 = text
304
+ if (options) {
305
+ const lbl = options.find(opt => opt.value == text)
306
+ if (lbl) text2 = lbl.label
307
+ }
308
+
309
+ const hasError = validationErrors[text]
310
+
311
+ return (
312
+ <Token
313
+ key={`${text}-${index}`}
314
+ text={text2}
315
+ onDelete={() => remove(index)}
316
+ disabled={disabled || readOnly}
317
+ error={hasError}
318
+ size={size}
319
+ variant={variant}
320
+ />
321
+ )
322
+ })}
323
+
324
+ {/* Input area */}
325
+ <div className="tokenField__input-container">
326
+ {options ? (
327
+ <DropDown
328
+ id={id}
329
+ onChange={changeDropDown}
330
+ options={sortedOptions}
331
+ predictive={predictive}
332
+ verbose={false}
333
+ disabled={disabled}
334
+ readOnly={readOnly}
335
+ placeholder={placeholder}
336
+ onFocus={handleFocus}
337
+ onBlur={handleBlur}
338
+ />
339
+ ) : (
340
+ <input
341
+ ref={inputRef}
342
+ id={id}
343
+ type='text'
344
+ value={value}
345
+ onChange={change}
346
+ onKeyDown={onEnter}
347
+ onFocus={handleFocus}
348
+ onBlur={handleBlur}
349
+ readOnly={readOnly}
350
+ disabled={disabled}
351
+ placeholder={placeholder}
352
+ className="tokenField__input"
353
+ aria-label={`Add ${label || 'token'}`}
354
+ />
355
+ )}
356
+
357
+ {/* Clear all button */}
358
+ {clearable && tks.length > 0 && minTokens === 0 && (
359
+ <button
360
+ type="button"
361
+ onClick={handleClear}
362
+ disabled={disabled || readOnly}
363
+ className="tokenField__clear"
364
+ aria-label="Clear all tokens"
365
+ >
366
+ <Icon icon="clear" size="small" />
367
+ </button>
368
+ )}
369
+ </div>
370
+ </div>
371
+
372
+ {/* Token count and limits */}
373
+ {(maxTokens || minTokens > 0) && (
374
+ <div className="tokenField__count">
375
+ <Text size="sm" color="muted">
376
+ {tks.length}
377
+ {maxTokens && ` / ${maxTokens}`}
378
+ {maxTokens ? ' tokens' : ` of ${tokens.length} tokens`}
379
+ </Text>
380
+ {minTokens > 0 && tks.length < minTokens && (
381
+ <Text size="sm" color="error">
382
+ Minimum {minTokens} required
383
+ </Text>
384
+ )}
385
+ </div>
386
+ )}
387
+
388
+ {/* Helper text or error */}
389
+ {(helperText || error) && (
390
+ <div
391
+ id={`${id}-helper`}
392
+ className={`tokenField__helper ${error ? 'tokenField__helper--error' : ''}`}
393
+ >
394
+ <Text size="sm" color={error ? 'error' : 'muted'}>
395
+ {error || helperText}
396
+ </Text>
397
+ </div>
398
+ )}
85
399
  </div>
86
400
  )
87
401
  }
88
402
 
89
403
  /**
90
- * Token
404
+ * Enhanced Token component with improved functionality while maintaining 100% compatibility
91
405
  */
92
- const Token = ({ text, onDelete }) => {
406
+ const Token = (props) => {
407
+ const {
408
+ // Original props (100% compatible)
409
+ text,
410
+ onDelete,
411
+ // New enhanced props (all optional for compatibility)
412
+ disabled = false,
413
+ error = false,
414
+ size = 'medium',
415
+ variant = 'default',
416
+ icon,
417
+ className,
418
+ style,
419
+ ...restProps
420
+ } = props
421
+
422
+ // Handle delete
423
+ const handleDelete = useCallback((event) => {
424
+ if (disabled) return
425
+ event.preventDefault()
426
+ event.stopPropagation()
427
+ if (onDelete) onDelete()
428
+ }, [disabled, onDelete])
429
+
430
+ // Handle keyboard interaction
431
+ const handleKeyDown = useCallback((event) => {
432
+ if (disabled) return
433
+
434
+ switch (event.key) {
435
+ case 'Delete':
436
+ case 'Backspace':
437
+ event.preventDefault()
438
+ if (onDelete) onDelete()
439
+ break
440
+ default:
441
+ break
442
+ }
443
+ }, [disabled, onDelete])
444
+
445
+ // Generate CSS classes
446
+ const cssClasses = [
447
+ 'token',
448
+ `token--${size}`,
449
+ `token--${variant}`,
450
+ disabled && 'token--disabled',
451
+ error && 'token--error',
452
+ className
453
+ ].filter(Boolean).join(' ')
454
+
93
455
  return (
94
- <div className='token'>
95
- <Text use='caption' tag='div'>
456
+ <div
457
+ className={cssClasses}
458
+ style={style}
459
+ tabIndex={disabled ? -1 : 0}
460
+ onKeyDown={handleKeyDown}
461
+ role="button"
462
+ aria-label={`Token: ${text}. Press Delete to remove.`}
463
+ aria-disabled={disabled}
464
+ {...restProps}
465
+ >
466
+ {/* Icon (optional) */}
467
+ {icon && (
468
+ <Icon
469
+ icon={icon}
470
+ size={size === 'small' ? 'small' : 'medium'}
471
+ className="token__icon"
472
+ />
473
+ )}
474
+
475
+ {/* Text content (maintaining original structure) */}
476
+ <Text use='caption' tag='div' className="token__text">
96
477
  {text}
97
478
  </Text>
98
- <Icon icon='close' clickable action={onDelete} size="small" />
479
+
480
+ {/* Delete button (maintaining original structure) */}
481
+ <Icon
482
+ icon='close'
483
+ clickable
484
+ action={handleDelete}
485
+ size="small"
486
+ className="token__delete"
487
+ disabled={disabled}
488
+ aria-label="Remove token"
489
+ />
99
490
  </div>
100
491
  )
101
- }
492
+ }
493
+
494
+ // PropTypes for TokenField
495
+ TokenField.propTypes = {
496
+ /** Field ID */
497
+ id: PropTypes.string,
498
+ /** Field label */
499
+ label: PropTypes.string,
500
+ /** Array of tokens */
501
+ tokens: PropTypes.array,
502
+ /** Read-only state */
503
+ readOnly: PropTypes.bool,
504
+ /** Options for dropdown */
505
+ options: PropTypes.arrayOf(PropTypes.shape({
506
+ label: PropTypes.string,
507
+ value: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
508
+ })),
509
+ /** Enable predictive input */
510
+ predictive: PropTypes.bool,
511
+ /** Change callback */
512
+ onChange: PropTypes.func,
513
+ /** Disabled state */
514
+ disabled: PropTypes.bool,
515
+ /** Required field */
516
+ required: PropTypes.bool,
517
+ /** Input placeholder */
518
+ placeholder: PropTypes.string,
519
+ /** Maximum tokens allowed */
520
+ maxTokens: PropTypes.number,
521
+ /** Minimum tokens required */
522
+ minTokens: PropTypes.number,
523
+ /** Allow duplicate tokens */
524
+ allowDuplicates: PropTypes.bool,
525
+ /** Token validation function */
526
+ validateToken: PropTypes.func,
527
+ /** Token separator characters */
528
+ tokenSeparators: PropTypes.arrayOf(PropTypes.string),
529
+ /** Field size */
530
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
531
+ /** Visual variant */
532
+ variant: PropTypes.oneOf(['default', 'outlined', 'filled']),
533
+ /** Error message */
534
+ error: PropTypes.string,
535
+ /** Helper text */
536
+ helperText: PropTypes.string,
537
+ /** Focus callback */
538
+ onFocus: PropTypes.func,
539
+ /** Blur callback */
540
+ onBlur: PropTypes.func,
541
+ /** Token add callback */
542
+ onTokenAdd: PropTypes.func,
543
+ /** Token remove callback */
544
+ onTokenRemove: PropTypes.func,
545
+ /** Validation error callback */
546
+ onValidationError: PropTypes.func,
547
+ /** Show clear button */
548
+ clearable: PropTypes.bool,
549
+ /** Clear callback */
550
+ onClear: PropTypes.func,
551
+ /** Enable search in options */
552
+ searchable: PropTypes.bool,
553
+ /** Sort options alphabetically */
554
+ sortable: PropTypes.bool,
555
+ /** Additional CSS classes */
556
+ className: PropTypes.string,
557
+ /** Inline styles */
558
+ style: PropTypes.object
559
+ }
560
+
561
+ TokenField.defaultProps = {
562
+ tokens: [],
563
+ predictive: true,
564
+ disabled: false,
565
+ required: false,
566
+ placeholder: "Add token...",
567
+ minTokens: 0,
568
+ allowDuplicates: true,
569
+ tokenSeparators: [',', ';', '\n'],
570
+ size: 'medium',
571
+ variant: 'default',
572
+ clearable: false,
573
+ searchable: false,
574
+ sortable: false
575
+ }
576
+
577
+ // PropTypes for Token
578
+ Token.propTypes = {
579
+ /** Token text */
580
+ text: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired,
581
+ /** Delete callback */
582
+ onDelete: PropTypes.func,
583
+ /** Disabled state */
584
+ disabled: PropTypes.bool,
585
+ /** Error state */
586
+ error: PropTypes.bool,
587
+ /** Token size */
588
+ size: PropTypes.oneOf(['small', 'medium', 'large']),
589
+ /** Visual variant */
590
+ variant: PropTypes.oneOf(['default', 'outlined', 'filled']),
591
+ /** Optional icon */
592
+ icon: PropTypes.string,
593
+ /** Additional CSS classes */
594
+ className: PropTypes.string,
595
+ /** Inline styles */
596
+ style: PropTypes.object
597
+ }
598
+
599
+ Token.defaultProps = {
600
+ disabled: false,
601
+ error: false,
602
+ size: 'medium',
603
+ variant: 'default'
604
+ }
605
+
606
+ export default TokenField