uibee 3.1.5 → 3.2.0

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.
@@ -0,0 +1,20 @@
1
+ type SpinnerProps = {
2
+ size?: 'sm' | 'md' | 'lg'
3
+ className?: string
4
+ }
5
+
6
+ const sizes = {
7
+ sm: 'w-4 h-4 border-2',
8
+ md: 'w-6 h-6 border-2',
9
+ lg: 'w-9 h-9 border-[3px]',
10
+ }
11
+
12
+ export default function Spinner({ size = 'md', className = '' }: SpinnerProps) {
13
+ return (
14
+ <div
15
+ role='status'
16
+ aria-label='Loading'
17
+ className={`rounded-full border-login-500 border-t-login animate-spin ${sizes[size]} ${className}`}
18
+ />
19
+ )
20
+ }
@@ -46,18 +46,16 @@ function Cell<T extends Record<string, unknown>>({
46
46
  variant: TableVariant
47
47
  }) {
48
48
  const value = row[col.key]
49
- // Truncate: opt-in, only applied when col.truncate === true (needs an explicit col.width to bite).
50
49
  const shouldTruncate = col.truncate === true
51
50
  const align = col.align ?? 'left'
52
51
 
53
52
  const wrapperAlign =
54
53
  align === 'right' ? 'text-right' :
55
- align === 'center' ? 'text-center' : ''
54
+ align === 'center' ? 'text-center' : ''
56
55
 
57
56
  let content: React.ReactNode
58
57
 
59
58
  if (col.render) {
60
- // Custom renderer: caller is responsible for alignment
61
59
  content = col.render(value, row)
62
60
  } else if (col.highlight) {
63
61
  const colorName = col.highlight[String(value)] ?? col.highlight['default']
@@ -124,8 +122,6 @@ export default function Body<T extends Record<string, unknown>>({
124
122
 
125
123
  useEffect(() => {
126
124
  if (openMenuId === null) return
127
- // The menu is anchored to viewport coords, so close it on any scroll (the scroll
128
- // container is now an ancestor of the table). Capture phase since scroll doesn't bubble.
129
125
  document.addEventListener('scroll', closeMenu, true)
130
126
  return () => document.removeEventListener('scroll', closeMenu, true)
131
127
  }, [closeMenu, openMenuId])
@@ -150,7 +146,11 @@ export default function Body<T extends Record<string, unknown>>({
150
146
  return (
151
147
  <tbody className={VARIANT_TBODY[variant]}>
152
148
  {data.map((row, rowIdx) => {
153
- const id = resolveId(row as Record<string, unknown>, idKey as string | undefined, columns as Column<Record<string, unknown>>[])
149
+ const id = resolveId(
150
+ row as Record<string, unknown>,
151
+ idKey as string | undefined,
152
+ columns as Column<Record<string, unknown>>[],
153
+ )
154
154
  const url = resolveRedirectUrl(redirectPath, row as Record<string, unknown>, id)
155
155
  const isClickable = Boolean(url || onRowClick || hasExpand)
156
156
  const isMenuOpen = openMenuId === id
@@ -195,28 +195,34 @@ export default function Body<T extends Record<string, unknown>>({
195
195
  {selectable && (
196
196
  <td className={`align-middle ${VARIANT_ROW_BORDER[variant]}`}
197
197
  style={{ width: '3rem', minWidth: '3rem' }}>
198
- <div className='flex items-center justify-center'>
199
- <button
200
- type='button'
201
- aria-label={isSelected ? 'Deselect row' : 'Select row'}
202
- aria-checked={isSelected}
203
- role='checkbox'
204
- onClick={(e) => { e.stopPropagation(); toggleSelect(id) }}
205
- className={`
198
+ <div className='flex items-center justify-center'>
199
+ <button
200
+ type='button'
201
+ aria-label={isSelected ? 'Deselect row' : 'Select row'}
202
+ aria-checked={isSelected}
203
+ role='checkbox'
204
+ onClick={(e) => { e.stopPropagation(); toggleSelect(id) }}
205
+ className={`
206
206
  h-4 w-4 rounded border flex items-center justify-center transition-colors cursor-pointer
207
207
  ${isSelected
208
- ? 'bg-login border-login text-white'
209
- : 'border-login-400 bg-transparent hover:border-login-100'
210
- }
208
+ ? 'bg-login border-login text-white'
209
+ : 'border-login-400 bg-transparent hover:border-login-100'
210
+ }
211
211
  `}
212
- >
213
- {isSelected && (
214
- <svg className='h-3 w-3' viewBox='0 0 12 12' fill='none'>
215
- <path d='M2 6l3 3 5-5' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' strokeLinejoin='round' />
216
- </svg>
217
- )}
218
- </button>
219
- </div>
212
+ >
213
+ {isSelected && (
214
+ <svg className='h-3 w-3' viewBox='0 0 12 12' fill='none'>
215
+ <path
216
+ d='M2 6l3 3 5-5'
217
+ stroke='currentColor'
218
+ strokeWidth='1.5'
219
+ strokeLinecap='round'
220
+ strokeLinejoin='round'
221
+ />
222
+ </svg>
223
+ )}
224
+ </button>
225
+ </div>
220
226
  </td>
221
227
  )}
222
228
 
@@ -227,12 +233,12 @@ export default function Body<T extends Record<string, unknown>>({
227
233
  {hasExpand && (
228
234
  <td className={`align-middle ${VARIANT_ROW_BORDER[variant]}`}
229
235
  style={{ width: '2.5rem', minWidth: '2.5rem' }}>
230
- <div className='flex items-center justify-center'>
231
- <ChevronDown className={`
236
+ <div className='flex items-center justify-center'>
237
+ <ChevronDown className={`
232
238
  h-4 w-4 transition-transform duration-200
233
239
  ${isExpanded ? 'rotate-180 text-login' : 'text-login-400'}
234
240
  `} />
235
- </div>
241
+ </div>
236
242
  </td>
237
243
  )}
238
244
 
@@ -258,9 +264,9 @@ export default function Body<T extends Record<string, unknown>>({
258
264
  className={`
259
265
  p-1.5 rounded flex items-center justify-center transition-colors
260
266
  ${isMenuOpen
261
- ? 'bg-login-500 text-login-75'
262
- : 'text-login-300 hover:bg-login-500/60 hover:text-login-75'
263
- }
267
+ ? 'bg-login-500 text-login-75'
268
+ : 'text-login-300 hover:bg-login-500/60 hover:text-login-75'
269
+ }
264
270
  `}
265
271
  >
266
272
  <EllipsisVertical className='h-4 w-4' />
@@ -34,11 +34,9 @@ export const VARIANT_THEAD: Record<TableVariant, string> = {
34
34
 
35
35
  export const VARIANT_HEAD_BG: Record<TableVariant, string> = {
36
36
  original: 'bg-login-700',
37
- modern: 'bg-login-900/95',
37
+ modern: '',
38
38
  }
39
39
 
40
- // Bottom border under the header cells (row dividers live on the cells, not the <tr>,
41
- // because native table rows don't render borders in border-separate mode).
42
40
  export const VARIANT_HEAD_BORDER: Record<TableVariant, string> = {
43
41
  original: 'border-b border-login-600',
44
42
  modern: 'border-b border-login-500/40',
@@ -49,29 +49,35 @@ export default function Header<T extends Record<string, unknown>>({
49
49
  {hasSelect && (
50
50
  <th className={`sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`}
51
51
  style={{ width: '3rem', minWidth: '3rem' }}>
52
- <div className='flex items-center justify-center'>
53
- <button
54
- type='button'
55
- aria-label={allSelected ? 'Deselect all' : 'Select all'}
56
- onClick={onSelectAll}
57
- className={`
52
+ <div className='flex items-center justify-center'>
53
+ <button
54
+ type='button'
55
+ aria-label={allSelected ? 'Deselect all' : 'Select all'}
56
+ onClick={onSelectAll}
57
+ className={`
58
58
  h-4 w-4 rounded border flex items-center justify-center transition-colors cursor-pointer
59
59
  ${allSelected || someSelected
60
- ? 'bg-login border-login text-white'
61
- : 'border-login-400 bg-transparent hover:border-login-200'
62
- }
60
+ ? 'bg-login border-login text-white'
61
+ : 'border-login-400 bg-transparent hover:border-login-200'
62
+ }
63
63
  `}
64
- >
65
- {someSelected && !allSelected && (
66
- <span className='block h-0.5 w-2 bg-white rounded-full' />
67
- )}
68
- {allSelected && (
69
- <svg className='h-3 w-3' viewBox='0 0 12 12' fill='none'>
70
- <path d='M2 6l3 3 5-5' stroke='currentColor' strokeWidth='1.5' strokeLinecap='round' strokeLinejoin='round' />
71
- </svg>
72
- )}
73
- </button>
74
- </div>
64
+ >
65
+ {someSelected && !allSelected && (
66
+ <span className='block h-0.5 w-2 bg-white rounded-full' />
67
+ )}
68
+ {allSelected && (
69
+ <svg className='h-3 w-3' viewBox='0 0 12 12' fill='none'>
70
+ <path
71
+ d='M2 6l3 3 5-5'
72
+ stroke='currentColor'
73
+ strokeWidth='1.5'
74
+ strokeLinecap='round'
75
+ strokeLinejoin='round'
76
+ />
77
+ </svg>
78
+ )}
79
+ </button>
80
+ </div>
75
81
  </th>
76
82
  )}
77
83
 
@@ -84,7 +90,7 @@ export default function Header<T extends Record<string, unknown>>({
84
90
 
85
91
  const alignClass =
86
92
  col.align === 'right' ? 'justify-end' :
87
- col.align === 'center' ? 'justify-center' : ''
93
+ col.align === 'center' ? 'justify-center' : ''
88
94
 
89
95
  return (
90
96
  <th
@@ -126,7 +132,11 @@ export default function Header<T extends Record<string, unknown>>({
126
132
  {hasExpand && (
127
133
  <th className={`sticky top-0 z-10 ${VARIANT_HEAD_BG[variant]} ${VARIANT_HEAD_BORDER[variant]}`}
128
134
  style={{ width: '2.5rem', minWidth: '2.5rem' }}>
129
- <span className={`flex items-center justify-center text-xs font-medium tracking-wider uppercase opacity-50 ${VARIANT_THEAD_TH[variant]}`}>
135
+ <span className={`
136
+ flex items-center justify-center
137
+ text-xs font-medium tracking-wider uppercase opacity-50
138
+ ${VARIANT_THEAD_TH[variant]}
139
+ `}>
130
140
  +
131
141
  </span>
132
142
  </th>