@uniflowed/ui 0.0.0-alpha.18 → 0.0.0-alpha.37

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.
package/slider.js CHANGED
@@ -76,8 +76,13 @@ import * as React from "@uniflowed/react";
76
76
  import { createContext, useContext, useMemo, useRef } from "@uniflowed/react";
77
77
  import { useStableCallback } from "@uniflowed/hooks/lifecycle";
78
78
 
79
- import type { Rest } from "./internal/merge-props.js";
80
- import { composeHandlers, composeRefs, withoutComposed } from "./internal/merge-props.js";
79
+ import type { RenderProp, Rest } from "./internal/merge-props.js";
80
+ import {
81
+ composeHandlers,
82
+ composeRefs,
83
+ withProps,
84
+ withoutComposed,
85
+ } from "./internal/merge-props.js";
81
86
  import type { Orientation } from "./internal/roving-focus.js";
82
87
  import { clamp, fraction, isReversed, snap } from "./internal/range.js";
83
88
  import { useControlled } from "./internal/controlled-state.js";
@@ -128,6 +133,7 @@ export component SliderRoot(
128
133
  orientation?: Orientation = "horizontal",
129
134
  disabled?: boolean = false,
130
135
  valueText?: (value: number, index: number) => string,
136
+ render?: RenderProp,
131
137
  ...rest: Rest
132
138
  ) {
133
139
  const [values, setValues] = useControlled(value, defaultValue, onValueChange);
@@ -190,7 +196,7 @@ export component SliderRoot(
190
196
 
191
197
  return (
192
198
  <SliderContext.Provider value={state}>
193
- <div {...rest}>{children}</div>
199
+ {render == null ? <div {...rest}>{children}</div> : render(withProps(rest, { children }))}
194
200
  </SliderContext.Provider>
195
201
  );
196
202
  }
@@ -203,7 +209,7 @@ export component SliderRoot(
203
209
  * wanders off the track — which every drag does — keeps arriving here instead
204
210
  * of being lost to whatever it wandered over.
205
211
  */
206
- export component SliderTrack(children: React.Node, ...rest: Rest) {
212
+ export component SliderTrack(children: React.Node, render?: RenderProp, ...rest: Rest) {
207
213
  const slider = useSlider("Slider.Track");
208
214
  const passed = withoutComposed(rest, ["onPointerDown", "onPointerMove", "onPointerUp", "ref"]);
209
215
  const dragging = useRef<number | null>(null);
@@ -239,35 +245,33 @@ export component SliderTrack(children: React.Node, ...rest: Rest) {
239
245
  slider.setAt(at, target);
240
246
  };
241
247
 
242
- return (
243
- <div
244
- {...passed}
245
- onPointerDown={composeHandlers(rest.onPointerDown, (event: $FlowFixMe) => {
246
- if (slider.disabled) {
247
- return;
248
- }
249
- // Otherwise the press selects the page's text on the way past, which
250
- // makes a drag paint everything blue.
251
- event.preventDefault();
252
- event.currentTarget?.setPointerCapture?.(event.pointerId);
253
- moveTo(event, null);
254
- })}
255
- onPointerMove={composeHandlers(rest.onPointerMove, (event: $FlowFixMe) => {
256
- if (dragging.current != null) {
257
- moveTo(event, dragging.current);
258
- }
259
- })}
260
- onPointerUp={composeHandlers(rest.onPointerUp, (event: $FlowFixMe) => {
261
- dragging.current = null;
262
- event.currentTarget?.releasePointerCapture?.(event.pointerId);
263
- })}
264
- ref={composeRefs(rest.ref, (element) => {
265
- slider.trackRef.current = element;
266
- })}
267
- >
268
- {children}
269
- </div>
270
- );
248
+ const props = withProps(passed, {
249
+ children,
250
+ onPointerDown: composeHandlers(rest.onPointerDown, (event: $FlowFixMe) => {
251
+ if (slider.disabled) {
252
+ return;
253
+ }
254
+ // Otherwise the press selects the page's text on the way past, which
255
+ // makes a drag paint everything blue.
256
+ event.preventDefault();
257
+ event.currentTarget?.setPointerCapture?.(event.pointerId);
258
+ moveTo(event, null);
259
+ }),
260
+ onPointerMove: composeHandlers(rest.onPointerMove, (event: $FlowFixMe) => {
261
+ if (dragging.current != null) {
262
+ moveTo(event, dragging.current);
263
+ }
264
+ }),
265
+ onPointerUp: composeHandlers(rest.onPointerUp, (event: $FlowFixMe) => {
266
+ dragging.current = null;
267
+ event.currentTarget?.releasePointerCapture?.(event.pointerId);
268
+ }),
269
+ ref: composeRefs(rest.ref, (element: HTMLElement | null) => {
270
+ slider.trackRef.current = element;
271
+ }),
272
+ });
273
+
274
+ return render == null ? <div {...props} /> : render(props);
271
275
  }
272
276
 
273
277
  /**
@@ -281,24 +285,22 @@ export component SliderTrack(children: React.Node, ...rest: Rest) {
281
285
  * is told the value by the thumb and telling them again here would be telling
282
286
  * them twice.
283
287
  */
284
- export component SliderRange(...rest: Rest) {
288
+ export component SliderRange(render?: RenderProp, ...rest: Rest) {
285
289
  const slider = useSlider("Slider.Range");
286
290
  const passed = withoutComposed(rest, ["style"]);
287
291
  const ends = [...slider.values].sort((first, second) => first - second);
288
292
  const start = slider.values.length > 1 ? (ends[0] ?? slider.min) : slider.min;
289
293
  const end = ends[ends.length - 1] ?? slider.min;
294
+ const props = withProps(passed, {
295
+ "aria-hidden": "true",
296
+ style: {
297
+ ...(rest.style as $FlowFixMe),
298
+ "--uf-slider-start": fraction(start, slider.min, slider.max),
299
+ "--uf-slider-end": fraction(end, slider.min, slider.max),
300
+ },
301
+ });
290
302
 
291
- return (
292
- <div
293
- {...passed}
294
- aria-hidden="true"
295
- style={{
296
- ...(rest.style as $FlowFixMe),
297
- "--uf-slider-start": fraction(start, slider.min, slider.max),
298
- "--uf-slider-end": fraction(end, slider.min, slider.max),
299
- }}
300
- />
301
- );
303
+ return render == null ? <div {...props} /> : render(props);
302
304
  }
303
305
 
304
306
  /**
@@ -315,55 +317,53 @@ export component SliderRange(...rest: Rest) {
315
317
  * "Maximum" told apart is the whole difference between a control a reader can
316
318
  * operate and two identical "slider"s.
317
319
  */
318
- export component SliderThumb(index?: number = 0, ...rest: Rest) {
320
+ export component SliderThumb(index?: number = 0, render?: RenderProp, ...rest: Rest) {
319
321
  const slider = useSlider("Slider.Thumb");
320
322
  const passed = withoutComposed(rest, ["onKeyDown", "style"]);
321
323
  const value = slider.values[index] ?? slider.min;
322
324
  const [lower, upper] = boundsOf(slider.values, index, slider.min, slider.max);
325
+ const props = withProps(passed, {
326
+ "aria-disabled": slider.disabled ? "true" : undefined,
327
+ "aria-orientation": slider.orientation,
328
+ // The neighbour's value, not the slider's end. A reader told they may
329
+ // set this thumb to 90 while the control refuses at 60 has been told
330
+ // something the control disagrees with.
331
+ "aria-valuemax": upper,
332
+ "aria-valuemin": lower,
333
+ "aria-valuenow": value,
334
+ "aria-valuetext": slider.valueText?.(value, index),
335
+ onKeyDown: composeHandlers(rest.onKeyDown, (event: $FlowFixMe) => {
336
+ if (slider.disabled) {
337
+ return;
338
+ }
339
+ const reversed = isReversed(event.currentTarget, slider.orientation);
340
+ const move = stepFor(event.key, slider.step, slider.largeStep, reversed);
341
+ if (move != null) {
342
+ // Before moving: the arrow keys scroll the page, and a slider that
343
+ // moves the page under the reader as it moves the value is a control
344
+ // they cannot watch.
345
+ event.preventDefault();
346
+ slider.setAt(index, value + move);
347
+ return;
348
+ }
349
+ if (event.key === "Home" || event.key === "End") {
350
+ event.preventDefault();
351
+ // This thumb's own ends, which for the lower thumb of a range is its
352
+ // neighbour rather than the slider's maximum.
353
+ slider.setAt(index, event.key === "Home" ? lower : upper);
354
+ }
355
+ }),
356
+ role: "slider",
357
+ style: {
358
+ ...(rest.style as $FlowFixMe),
359
+ "--uf-slider-fraction": fraction(value, slider.min, slider.max),
360
+ },
361
+ // In the tab sequence, and out of it while disabled — the browser does
362
+ // this for a real control and there is no real control here to do it.
363
+ tabIndex: slider.disabled ? -1 : 0,
364
+ });
323
365
 
324
- return (
325
- <span
326
- {...passed}
327
- aria-disabled={slider.disabled ? "true" : undefined}
328
- aria-orientation={slider.orientation}
329
- // The neighbour's value, not the slider's end. A reader told they may
330
- // set this thumb to 90 while the control refuses at 60 has been told
331
- // something the control disagrees with.
332
- aria-valuemax={upper}
333
- aria-valuemin={lower}
334
- aria-valuenow={value}
335
- aria-valuetext={slider.valueText?.(value, index)}
336
- onKeyDown={composeHandlers(rest.onKeyDown, (event: $FlowFixMe) => {
337
- if (slider.disabled) {
338
- return;
339
- }
340
- const reversed = isReversed(event.currentTarget, slider.orientation);
341
- const move = stepFor(event.key, slider.step, slider.largeStep, reversed);
342
- if (move != null) {
343
- // Before moving: the arrow keys scroll the page, and a slider that
344
- // moves the page under the reader as it moves the value is a control
345
- // they cannot watch.
346
- event.preventDefault();
347
- slider.setAt(index, value + move);
348
- return;
349
- }
350
- if (event.key === "Home" || event.key === "End") {
351
- event.preventDefault();
352
- // This thumb's own ends, which for the lower thumb of a range is its
353
- // neighbour rather than the slider's maximum.
354
- slider.setAt(index, event.key === "Home" ? lower : upper);
355
- }
356
- })}
357
- role="slider"
358
- style={{
359
- ...(rest.style as $FlowFixMe),
360
- "--uf-slider-fraction": fraction(value, slider.min, slider.max),
361
- }}
362
- // In the tab sequence, and out of it while disabled — the browser does
363
- // this for a real control and there is no real control here to do it.
364
- tabIndex={slider.disabled ? -1 : 0}
365
- />
366
- );
366
+ return render == null ? <span {...props} /> : render(props);
367
367
  }
368
368
 
369
369
  /**
package/switch.js CHANGED
@@ -29,47 +29,53 @@
29
29
 
30
30
  import * as React from "@uniflowed/react";
31
31
 
32
- import type { Rest } from "./internal/merge-props.js";
33
- import { composeHandlers, withoutComposed } from "./internal/merge-props.js";
32
+ import type { PartEvent, RenderProp, Rest } from "./internal/merge-props.js";
33
+ import { composeHandlers, withProps, withoutComposed } from "./internal/merge-props.js";
34
34
  import { useControlled } from "./internal/controlled-state.js";
35
35
 
36
- /** A two-state switch: on or off. */
36
+ /**
37
+ * A two-state switch: on or off.
38
+ *
39
+ * `render` is the escape hatch, and on a switch it is what a caller with their
40
+ * own `<Pressable>` or a `<div role="switch">` in a design system reaches for.
41
+ * `type="button"` is the one thing it does not hand over, because that is true
42
+ * of the element rather than of the switch.
43
+ */
37
44
  export component Switch(
38
45
  checked?: boolean,
39
46
  defaultChecked?: boolean = false,
40
47
  onCheckedChange?: (checked: boolean) => void,
41
48
  disabled?: boolean = false,
42
49
  children?: React.Node,
50
+ render?: RenderProp,
43
51
  ...rest: Rest
44
52
  ) {
45
53
  const [on, setOn] = useControlled(checked, defaultChecked, onCheckedChange);
46
- const passed = withoutComposed(rest, ["onClick", "onKeyDown"]);
47
-
48
- return (
49
- <button
50
- {...passed}
51
- aria-checked={on ? "true" : "false"}
52
- disabled={disabled}
53
- onClick={composeHandlers(rest.onClick, () => {
54
- if (!disabled) {
55
- setOn(!on);
56
- }
57
- })}
58
- onKeyDown={composeHandlers(rest.onKeyDown, (event) => {
59
- if (disabled || (event.key !== " " && event.key !== "Enter")) {
60
- return;
61
- }
62
- // Preventing the default is not decoration. It stops `Space` scrolling
63
- // the page — which is what makes a hand-written toggle feel broken even
64
- // when it works — and it stops the browser's own click from arriving
65
- // after this handler and toggling the switch a second time.
66
- event.preventDefault();
54
+ const props = withProps(withoutComposed(rest, ["onClick", "onKeyDown"]), {
55
+ "aria-checked": on ? "true" : "false",
56
+ children,
57
+ disabled,
58
+ onClick: composeHandlers(rest.onClick, () => {
59
+ if (!disabled) {
67
60
  setOn(!on);
68
- })}
69
- role="switch"
70
- type="button"
71
- >
72
- {children}
73
- </button>
74
- );
61
+ }
62
+ }),
63
+ onKeyDown: composeHandlers(rest.onKeyDown, (event: PartEvent) => {
64
+ if (disabled || (event.key !== " " && event.key !== "Enter")) {
65
+ return;
66
+ }
67
+ // Preventing the default is not decoration. It stops `Space` scrolling
68
+ // the page — which is what makes a hand-written toggle feel broken even
69
+ // when it works — and it stops the browser's own click from arriving
70
+ // after this handler and toggling the switch a second time.
71
+ event.preventDefault();
72
+ setOn(!on);
73
+ }),
74
+ role: "switch",
75
+ });
76
+
77
+ if (render != null) {
78
+ return render(props);
79
+ }
80
+ return <button {...props} type="button" />;
75
81
  }
package/table.js CHANGED
@@ -68,8 +68,13 @@ import { createContext, useContext, useEffect, useId, useMemo, useState } from "
68
68
  import { useStableCallback } from "@uniflowed/hooks/lifecycle";
69
69
 
70
70
  import { Checkbox } from "./checkbox.js";
71
- import type { Rest } from "./internal/merge-props.js";
72
- import { composeHandlers, composeRefs, withoutComposed } from "./internal/merge-props.js";
71
+ import type { RenderProp, Rest } from "./internal/merge-props.js";
72
+ import {
73
+ composeHandlers,
74
+ composeRefs,
75
+ withProps,
76
+ withoutComposed,
77
+ } from "./internal/merge-props.js";
73
78
  import { useControlled } from "./internal/controlled-state.js";
74
79
 
75
80
  /** Which column a table is sorted by, and which way. */
@@ -130,6 +135,7 @@ export component TableRoot(
130
135
  rowCount?: number | null = null,
131
136
  rowOffset?: number = 0,
132
137
  announceSort?: (column: string, direction: "ascending" | "descending") => string,
138
+ render?: RenderProp,
133
139
  ...rest: Rest
134
140
  ) {
135
141
  const base = useId();
@@ -184,23 +190,15 @@ export component TableRoot(
184
190
  labels[current.column] ?? current.column,
185
191
  current.direction,
186
192
  );
193
+ const props = withProps(rest, {
194
+ "aria-labelledby": captioned ? `${base}-caption` : undefined,
195
+ "aria-rowcount": rowCount == null ? undefined : rowCount + headerRows,
196
+ children,
197
+ });
187
198
 
188
199
  return (
189
200
  <TableContext.Provider value={state}>
190
- <table
191
- {...rest}
192
- // The caption is a `<table>`'s accessible name in HTML-AAM already;
193
- // naming it again here is what turns "the host will probably do this"
194
- // into something this component promises, and it costs nothing because
195
- // both point at the same words. Only while a caption is rendered, for
196
- // the reason every part of this package repeats.
197
- aria-labelledby={captioned ? `${base}-caption` : undefined}
198
- // Every row in the table, which is the data plus the header — the
199
- // arithmetic a caller should not have to remember.
200
- aria-rowcount={rowCount == null ? undefined : rowCount + headerRows}
201
- >
202
- {children}
203
- </table>
201
+ {render == null ? <table {...props} /> : render(withProps(props, { role: "table" }))}
204
202
  {/*
205
203
  Beside the table rather than inside it, because a `<table>` may only
206
204
  contain a caption, column groups and row groups — and mounted from the
@@ -221,7 +219,7 @@ export component TableRoot(
221
219
  * what a screen reader reads when a reader lands on it. A heading above the
222
220
  * table looks the same and is not the table's name.
223
221
  */
224
- export component TableCaption(children: React.Node, ...rest: Rest) {
222
+ export component TableCaption(children: React.Node, render?: RenderProp, ...rest: Rest) {
225
223
  const table = useTable("Table.Caption");
226
224
  const register = table.registerCaption;
227
225
 
@@ -230,11 +228,11 @@ export component TableCaption(children: React.Node, ...rest: Rest) {
230
228
  return () => register(false);
231
229
  }, [register]);
232
230
 
233
- return (
234
- <caption {...rest} id={table.captionId}>
235
- {children}
236
- </caption>
237
- );
231
+ const props = withProps(rest, { children, id: table.captionId });
232
+ if (render != null) {
233
+ return render(withProps(props, { role: "caption" }));
234
+ }
235
+ return <caption {...props} />;
238
236
  }
239
237
 
240
238
  /**
@@ -243,7 +241,7 @@ export component TableCaption(children: React.Node, ...rest: Rest) {
243
241
  * It tells the root that it exists, because `aria-rowcount` and every row's
244
242
  * `aria-rowindex` count header rows and a table without one counts differently.
245
243
  */
246
- export component TableHeader(children: React.Node, ...rest: Rest) {
244
+ export component TableHeader(children: React.Node, render?: RenderProp, ...rest: Rest) {
247
245
  const table = useTable("Table.Header");
248
246
  const register = table.registerHeader;
249
247
 
@@ -252,18 +250,21 @@ export component TableHeader(children: React.Node, ...rest: Rest) {
252
250
  return () => register(false);
253
251
  }, [register]);
254
252
 
253
+ const props = withProps(rest, { children });
254
+
255
255
  return (
256
256
  <HeaderContext.Provider value={true}>
257
- <thead {...rest}>{children}</thead>
257
+ {render == null ? <thead {...props} /> : render(withProps(props, { role: "rowgroup" }))}
258
258
  </HeaderContext.Provider>
259
259
  );
260
260
  }
261
261
 
262
262
  /** The data rows. */
263
- export component TableBody(children: React.Node, ...rest: Rest) {
263
+ export component TableBody(children: React.Node, render?: RenderProp, ...rest: Rest) {
264
+ const props = withProps(rest, { children });
264
265
  return (
265
266
  <HeaderContext.Provider value={false}>
266
- <tbody {...rest}>{children}</tbody>
267
+ {render == null ? <tbody {...props} /> : render(withProps(props, { role: "rowgroup" }))}
267
268
  </HeaderContext.Provider>
268
269
  );
269
270
  }
@@ -284,7 +285,12 @@ export component TableBody(children: React.Node, ...rest: Rest) {
284
285
  * adding them anyway is a second source of truth that can disagree with the
285
286
  * document.
286
287
  */
287
- export component TableRow(children: React.Node, index?: number | null = null, ...rest: Rest) {
288
+ export component TableRow(
289
+ children: React.Node,
290
+ index?: number | null = null,
291
+ render?: RenderProp,
292
+ ...rest: Rest
293
+ ) {
288
294
  const table = useTable("Table.Row");
289
295
  const header = useContext(HeaderContext);
290
296
  const counted = table.rowCount != null;
@@ -300,11 +306,11 @@ export component TableRow(children: React.Node, index?: number | null = null, ..
300
306
  rowIndex = table.headerRows + table.rowOffset + (index ?? 0) + 1;
301
307
  }
302
308
 
303
- return (
304
- <tr {...rest} aria-rowindex={rowIndex}>
305
- {children}
306
- </tr>
307
- );
309
+ const props = withProps(rest, { "aria-rowindex": rowIndex, children });
310
+ if (render != null) {
311
+ return render(withProps(props, { role: "row" }));
312
+ }
313
+ return <tr {...props} />;
308
314
  }
309
315
 
310
316
  /**
@@ -320,7 +326,12 @@ export component TableRow(children: React.Node, index?: number | null = null, ..
320
326
  * Not `"none"` on the others: eleven headers each announcing "not sorted" is
321
327
  * eleven announcements of nothing, on every pass through the table.
322
328
  */
323
- export component TableHead(children: React.Node, column?: string | null = null, ...rest: Rest) {
329
+ export component TableHead(
330
+ children: React.Node,
331
+ column?: string | null = null,
332
+ render?: RenderProp,
333
+ ...rest: Rest
334
+ ) {
324
335
  const table = useTable("Table.Head");
325
336
  const passed = withoutComposed(rest, column == null ? [] : ["onClick", "ref"]);
326
337
  const sorted = column != null && table.sort?.column === column;
@@ -341,41 +352,49 @@ export component TableHead(children: React.Node, column?: string | null = null,
341
352
  });
342
353
 
343
354
  if (column == null) {
344
- return (
345
- <th {...rest} scope="col">
346
- {children}
347
- </th>
348
- );
355
+ const props = withProps(rest, { children, scope: "col" });
356
+ if (render != null) {
357
+ return render(withProps(props, { role: "columnheader" }));
358
+ }
359
+ return <th {...props} />;
349
360
  }
350
361
 
351
- return (
352
- <th
353
- {...passed}
354
- aria-sort={sorted ? table.sort?.direction : undefined}
355
- ref={composeRefs(rest.ref, setElement)}
356
- scope="col"
362
+ const button = (
363
+ <button
364
+ onClick={composeHandlers(rest.onClick, () => {
365
+ // Two states, not three. A sort that cycles back to "unsorted" gives
366
+ // a reader a third press whose result is a table in an order nobody
367
+ // asked for.
368
+ table.setSort({
369
+ column,
370
+ direction: sorted && table.sort?.direction === "ascending" ? "descending" : "ascending",
371
+ });
372
+ })}
373
+ type="button"
357
374
  >
358
- <button
359
- onClick={composeHandlers(rest.onClick, () => {
360
- // Two states, not three. A sort that cycles back to "unsorted" gives
361
- // a reader a third press whose result is a table in an order nobody
362
- // asked for.
363
- table.setSort({
364
- column,
365
- direction: sorted && table.sort?.direction === "ascending" ? "descending" : "ascending",
366
- });
367
- })}
368
- type="button"
369
- >
370
- {children}
371
- </button>
372
- </th>
375
+ {children}
376
+ </button>
373
377
  );
378
+ const props = withProps(passed, {
379
+ "aria-sort": sorted ? table.sort?.direction : undefined,
380
+ children: button,
381
+ ref: composeRefs(rest.ref, setElement),
382
+ scope: "col",
383
+ });
384
+
385
+ if (render != null) {
386
+ return render(withProps(props, { role: "columnheader" }));
387
+ }
388
+ return <th {...props} />;
374
389
  }
375
390
 
376
391
  /** One cell. */
377
- export component TableCell(children: React.Node, ...rest: Rest) {
378
- return <td {...rest}>{children}</td>;
392
+ export component TableCell(children: React.Node, render?: RenderProp, ...rest: Rest) {
393
+ const props = withProps(rest, { children });
394
+ if (render != null) {
395
+ return render(withProps(props, { role: "cell" }));
396
+ }
397
+ return <td {...props} />;
379
398
  }
380
399
 
381
400
  /**
@@ -386,12 +405,12 @@ export component TableCell(children: React.Node, ...rest: Rest) {
386
405
  * moves down the year column. A table of records usually has one and almost
387
406
  * never marks it.
388
407
  */
389
- export component TableRowHeader(children: React.Node, ...rest: Rest) {
390
- return (
391
- <th {...rest} scope="row">
392
- {children}
393
- </th>
394
- );
408
+ export component TableRowHeader(children: React.Node, render?: RenderProp, ...rest: Rest) {
409
+ const props = withProps(rest, { children, scope: "row" });
410
+ if (render != null) {
411
+ return render(withProps(props, { role: "rowheader" }));
412
+ }
413
+ return <th {...props} />;
395
414
  }
396
415
 
397
416
  /**
@@ -430,6 +449,7 @@ export component TableSelectAll(
430
449
  label?: string = "Select all rows",
431
450
  className?: string,
432
451
  disabled?: boolean = false,
452
+ render?: RenderProp,
433
453
  ) {
434
454
  return (
435
455
  <Checkbox
@@ -439,6 +459,7 @@ export component TableSelectAll(
439
459
  disabled={disabled}
440
460
  indeterminate={checked === "mixed"}
441
461
  onCheckedChange={onCheckedChange}
462
+ render={render}
442
463
  />
443
464
  );
444
465
  }
@@ -461,6 +482,7 @@ export component TableRowSelect(
461
482
  onCheckedChange: (checked: boolean) => void,
462
483
  className?: string,
463
484
  disabled?: boolean = false,
485
+ render?: RenderProp,
464
486
  ) {
465
487
  return (
466
488
  <Checkbox
@@ -469,6 +491,7 @@ export component TableRowSelect(
469
491
  className={className}
470
492
  disabled={disabled}
471
493
  onCheckedChange={onCheckedChange}
494
+ render={render}
472
495
  />
473
496
  );
474
497
  }