@moontra/moonui 6.3.0 → 6.5.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.
@@ -7,6 +7,32 @@ import { cn } from "../../lib/utils";
7
7
 
8
8
  export type SortDirection = "asc" | "desc" | null;
9
9
 
10
+ /**
11
+ * Tablo düzeyi kontrollü sıralama bağlamı (#321).
12
+ *
13
+ * `Table`'daki `sortColumn`/`sortDirection`/`onSortChange` deklare edilmişti ama
14
+ * destructure'da yoruma alındığı için `...props` içinde kalıp native <table>'a
15
+ * sızıyordu (ölçüldü: `sortcolumn`/`sortdirection` geçersiz DOM attribute +
16
+ * 4 React uyarısı). Artık gerçekten bağlılar.
17
+ *
18
+ * `TableHead`'in mevcut `sorted`/`onSort` prop'ları KORUNUR ve önceliklidir —
19
+ * elle bağlayan tüketici kırılmaz. `columnId` verildiğinde bağlam devreye girer.
20
+ */
21
+ interface TableSortContextValue {
22
+ sortColumn?: string;
23
+ sortDirection?: SortDirection;
24
+ onSortChange?: (column: string, direction: SortDirection) => void;
25
+ }
26
+
27
+ const TableSortContext = React.createContext<TableSortContextValue | null>(null);
28
+
29
+ /** asc → desc → null → asc döngüsü */
30
+ function nextSortDirection(current: SortDirection): SortDirection {
31
+ if (current === "asc") return "desc";
32
+ if (current === "desc") return null;
33
+ return "asc";
34
+ }
35
+
10
36
  const tableVariants = cva(
11
37
  "w-full caption-bottom text-sm",
12
38
  {
@@ -72,18 +98,30 @@ const Table = React.forwardRef<
72
98
  size,
73
99
  loading,
74
100
  emptyContent,
75
- // Kullanılmayan özellikleri yoruma alarak lint uyarılarını önlüyoruz
76
- // sortColumn,
77
- // sortDirection,
78
- // onSortChange,
79
- // selectedRowIds,
80
- // onRowSelectionChange,
81
- // disableRowSelection,
82
- // getRowId,
101
+ // #321: Bunlar eskiden yoruma alınmıştı ve `...props` içinde kalıp native
102
+ // <table>'a sızıyordu (ölçüldü: `sortcolumn`/`sortdirection` geçersiz DOM
103
+ // attribute olarak basılıyor + 4 React uyarısı). Artık destructure ediliyorlar:
104
+ // sıralama üçlüsü GERÇEKTEN bağlandı (aşağıdaki TableSortContext)
105
+ // seçim dörtlüsü yalnız DOM'dan ÇIKARILIYOR — bağlanamazlar, çünkü `Table`
106
+ // veri almıyor (tamamen kompozisyonel) ve `getRowId(row)` bir veri satırı
107
+ // ister. Satır seçimi için veri-güdümlü `DataTable` kullanılmalı.
108
+ sortColumn,
109
+ sortDirection,
110
+ onSortChange,
111
+ selectedRowIds: _selectedRowIds,
112
+ onRowSelectionChange: _onRowSelectionChange,
113
+ disableRowSelection: _disableRowSelection,
114
+ getRowId: _getRowId,
83
115
  ...props
84
116
  }, ref) => {
85
117
  // Apply striped styles to the tbody via a class name
86
118
  const striped = variant === "striped";
119
+
120
+ // #321: sıralama prop'ları alt başlıklara bağlam üzerinden ulaşır
121
+ const sortContextValue = React.useMemo<TableSortContextValue>(
122
+ () => ({ sortColumn, sortDirection, onSortChange }),
123
+ [sortColumn, sortDirection, onSortChange]
124
+ );
87
125
 
88
126
  // Çocukları güvenli bir şekilde işle ve tip kontrollerini doğru yap
89
127
  const childrenWithProps = React.Children.map(props.children, (child) => {
@@ -108,17 +146,19 @@ const Table = React.forwardRef<
108
146
  </div>
109
147
  )}
110
148
 
111
- <table
112
- ref={ref}
113
- className={cn(
114
- tableVariants({ variant, size }),
115
- loading && "opacity-70",
116
- className
117
- )}
118
- {...props}
119
- >
120
- {childrenWithProps}
121
- </table>
149
+ <TableSortContext.Provider value={sortContextValue}>
150
+ <table
151
+ ref={ref}
152
+ className={cn(
153
+ tableVariants({ variant, size }),
154
+ loading && "opacity-70",
155
+ className
156
+ )}
157
+ {...props}
158
+ >
159
+ {childrenWithProps}
160
+ </table>
161
+ </TableSortContext.Provider>
122
162
 
123
163
  </div>
124
164
  );
@@ -207,12 +247,43 @@ interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
207
247
  sorted?: SortDirection;
208
248
  /** Sıralama değiştiğinde çağrılacak fonksiyon */
209
249
  onSort?: () => void;
250
+ /**
251
+ * Tablo düzeyi kontrollü sıralamada bu sütunun kimliği (#321).
252
+ *
253
+ * Verildiğinde `Table`'ın `sortColumn`/`sortDirection`/`onSortChange`
254
+ * prop'larıyla otomatik bağlanır: yön bağlamdan okunur, tıklama/klavye
255
+ * `onSortChange(columnId, sonrakiYön)` çağırır.
256
+ *
257
+ * Elle bağlayan mevcut kullanım (`sorted` + `onSort`) ÖNCELİKLİDİR ve
258
+ * çalışmaya devam eder — bu prop eklemeli, kırıcı değil.
259
+ */
260
+ columnId?: string;
210
261
  /** Text alignment */
211
262
  align?: 'left' | 'center' | 'right';
212
263
  }
213
264
 
214
265
  const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
215
- ({ className, sortable, sorted, onSort, align = 'left', children, ...props }, ref) => {
266
+ ({ className, sortable, sorted, onSort, columnId, align = 'left', children, ...props }, ref) => {
267
+ const sortCtx = React.useContext(TableSortContext);
268
+
269
+ // Elle verilen `sorted`/`onSort` önceliklidir; yoksa bağlamdan türetilir.
270
+ const ctxSorted: SortDirection =
271
+ columnId && sortCtx && sortCtx.sortColumn === columnId
272
+ ? sortCtx.sortDirection ?? null
273
+ : null;
274
+ const effectiveSorted: SortDirection = sorted !== undefined ? sorted : ctxSorted;
275
+
276
+ const handleSort = React.useCallback(() => {
277
+ if (onSort) {
278
+ onSort();
279
+ return;
280
+ }
281
+ if (columnId && sortCtx?.onSortChange) {
282
+ sortCtx.onSortChange(columnId, nextSortDirection(ctxSorted));
283
+ }
284
+ }, [onSort, columnId, sortCtx, ctxSorted]);
285
+
286
+ const isInteractive = Boolean(sortable && (onSort || (columnId && sortCtx?.onSortChange)));
216
287
  // Sıralama için simgeler
217
288
  const renderSortIcon = () => {
218
289
  if (!sortable) return null;
@@ -282,11 +353,41 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
282
353
  sortable && "cursor-pointer hover:text-foreground select-none",
283
354
  className
284
355
  )}
285
- onClick={sortable ? onSort : undefined}
356
+ /* #321: sıralama durumu ekran okuyucuya YANSIMIYORDU (`aria-sort`
357
+ grep'i 0 idi) — yalnız görsel ok ikonu değişiyordu. Sıralanamayan
358
+ sütunda öznitelik hiç basılmaz. */
359
+ aria-sort={
360
+ sortable
361
+ ? effectiveSorted === "asc"
362
+ ? "ascending"
363
+ : effectiveSorted === "desc"
364
+ ? "descending"
365
+ : "none"
366
+ : undefined
367
+ }
368
+ onClick={isInteractive ? handleSort : undefined}
286
369
  {...props}
287
370
  >
288
371
  {sortable || align !== 'left' ? (
289
- <div className={cn("flex items-center", alignmentClasses[align].split(' ')[1])}>
372
+ <div
373
+ className={cn("flex items-center", alignmentClasses[align].split(' ')[1])}
374
+ /* #321: sıralama kontrolü yalnız fareyle çalışıyordu (dosyada
375
+ `tabIndex` grep'i 0, `onKeyDown` yok). Görsel olarak div kalır ama
376
+ role/tabIndex/onKeyDown ile gerçek düğme gibi davranır; erişilebilir
377
+ adı sütun başlığıdır. Pro DataTable'da aynı düzeltme #291'de yapıldı. */
378
+ role={isInteractive ? "button" : undefined}
379
+ tabIndex={isInteractive ? 0 : undefined}
380
+ onKeyDown={
381
+ isInteractive
382
+ ? (e) => {
383
+ if (e.key === "Enter" || e.key === " ") {
384
+ e.preventDefault();
385
+ handleSort();
386
+ }
387
+ }
388
+ : undefined
389
+ }
390
+ >
290
391
  {children}
291
392
  {sortable && renderSortIcon()}
292
393
  </div>
@@ -347,4 +448,5 @@ export {
347
448
  TableCaption,
348
449
  };
349
450
 
350
- export type { TableBodyProps };
451
+ // #321: TableProps/TableHeadProps/TableCellProps export edilmiyordu
452
+ export type { TableProps, TableBodyProps, TableHeadProps, TableCellProps };