panelui-native 0.38.0 → 0.40.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.
- package/README.md +2 -1
- package/lib/module/components/heatmap-chart/index.js +113 -6
- package/lib/module/components/heatmap-chart/index.js.map +1 -1
- package/lib/module/components/pagination/index.js +519 -0
- package/lib/module/components/pagination/index.js.map +1 -0
- package/lib/module/components/ring-chart/index.js +183 -45
- package/lib/module/components/ring-chart/index.js.map +1 -1
- package/lib/module/components/steps/index.js +8 -6
- package/lib/module/components/steps/index.js.map +1 -1
- package/lib/module/components/table/index.js +100 -26
- package/lib/module/components/table/index.js.map +1 -1
- package/lib/module/components/timeline/index.js +8 -7
- package/lib/module/components/timeline/index.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/components/heatmap-chart/index.d.ts +51 -3
- package/lib/typescript/src/components/heatmap-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/pagination/index.d.ts +285 -0
- package/lib/typescript/src/components/pagination/index.d.ts.map +1 -0
- package/lib/typescript/src/components/ring-chart/index.d.ts +75 -8
- package/lib/typescript/src/components/ring-chart/index.d.ts.map +1 -1
- package/lib/typescript/src/components/steps/index.d.ts +8 -6
- package/lib/typescript/src/components/steps/index.d.ts.map +1 -1
- package/lib/typescript/src/components/table/index.d.ts +59 -17
- package/lib/typescript/src/components/table/index.d.ts.map +1 -1
- package/lib/typescript/src/components/timeline/index.d.ts +8 -7
- package/lib/typescript/src/components/timeline/index.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/heatmap-chart/index.tsx +147 -5
- package/src/components/pagination/index.tsx +591 -0
- package/src/components/ring-chart/index.tsx +269 -47
- package/src/components/steps/index.tsx +8 -6
- package/src/components/table/index.tsx +146 -30
- package/src/components/timeline/index.tsx +8 -7
- package/src/index.ts +15 -0
|
@@ -4,28 +4,37 @@
|
|
|
4
4
|
* React Native has no table layout: no `<table>`, no column model, nothing that
|
|
5
5
|
* makes the third cell of one row the same width as the third cell of the next.
|
|
6
6
|
* A table here is therefore a stack of flex rows, and the columns exist only
|
|
7
|
-
* because every row divides its width the same way.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* because every row divides its width the same way.
|
|
8
|
+
*
|
|
9
|
+
* `columns` is the column model that closes that gap. Give the root one entry
|
|
10
|
+
* per column and every `Table.Head` and `Table.Cell` takes its `flex`, `width`
|
|
11
|
+
* and `align` from the entry at its own position in the row — so a column is
|
|
12
|
+
* described once, at the top, and a row is just its contents. Without it the
|
|
13
|
+
* sizing has to be repeated on every head and every cell, and the column drifts
|
|
14
|
+
* the moment two of them disagree.
|
|
15
|
+
*
|
|
16
|
+
* ```tsx
|
|
17
|
+
* <Table columns={[{ flex: 2 }, { align: 'end' }]}>
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* Props set on a head or a cell still win, for the one row that has to differ.
|
|
10
21
|
*
|
|
11
22
|
* Everything else it does own: the hairlines between rows and the missing one
|
|
12
23
|
* under the last, the muted header and footer, the striping, the alignment of a
|
|
13
24
|
* numeric column, and the sort arrow that turns over rather than swapping.
|
|
14
25
|
*
|
|
15
26
|
* ```tsx
|
|
16
|
-
* <Table variant="outline">
|
|
27
|
+
* <Table variant="outline" columns={[{ flex: 2 }, { align: 'end' }]}>
|
|
17
28
|
* <Table.Header>
|
|
18
29
|
* <Table.Row>
|
|
19
|
-
* <Table.Head
|
|
20
|
-
* <Table.Head
|
|
21
|
-
* Amount
|
|
22
|
-
* </Table.Head>
|
|
30
|
+
* <Table.Head>Invoice</Table.Head>
|
|
31
|
+
* <Table.Head sortDirection="desc" onPress={toggleSort}>Amount</Table.Head>
|
|
23
32
|
* </Table.Row>
|
|
24
33
|
* </Table.Header>
|
|
25
34
|
* <Table.Body>
|
|
26
35
|
* <Table.Row>
|
|
27
|
-
* <Table.Cell
|
|
28
|
-
* <Table.Cell
|
|
36
|
+
* <Table.Cell>INV-001</Table.Cell>
|
|
37
|
+
* <Table.Cell>$250.00</Table.Cell>
|
|
29
38
|
* </Table.Row>
|
|
30
39
|
* </Table.Body>
|
|
31
40
|
* </Table>
|
|
@@ -125,15 +134,82 @@ const alignment: Record<CellAlign, string> = {
|
|
|
125
134
|
end: 'justify-end',
|
|
126
135
|
};
|
|
127
136
|
|
|
137
|
+
export interface TableColumn {
|
|
138
|
+
/**
|
|
139
|
+
* Share of the leftover width, relative to the other columns. Defaults to 1,
|
|
140
|
+
* so columns divide the row evenly.
|
|
141
|
+
*/
|
|
142
|
+
flex?: number;
|
|
143
|
+
/**
|
|
144
|
+
* Fixed width in pixels, for a column that must not move — an icon, a state
|
|
145
|
+
* dot. Wins over `flex`.
|
|
146
|
+
*/
|
|
147
|
+
width?: number;
|
|
148
|
+
/**
|
|
149
|
+
* Which edge the column's content sits against. Use `end` for numbers: a
|
|
150
|
+
* money column reads as a column only when the digits line up.
|
|
151
|
+
*/
|
|
152
|
+
align?: CellAlign;
|
|
153
|
+
}
|
|
154
|
+
|
|
128
155
|
/**
|
|
129
|
-
* Density and
|
|
130
|
-
* from here rather than being told again per row and per cell.
|
|
156
|
+
* Density, striping and the column model are set once on the root; every part
|
|
157
|
+
* below reads them from here rather than being told again per row and per cell.
|
|
131
158
|
*/
|
|
132
|
-
const TableContext = createContext<{
|
|
159
|
+
const TableContext = createContext<{
|
|
160
|
+
size: TableSize;
|
|
161
|
+
striped: boolean;
|
|
162
|
+
columns?: TableColumn[];
|
|
163
|
+
}>({
|
|
133
164
|
size: 'default',
|
|
134
165
|
striped: false,
|
|
135
166
|
});
|
|
136
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Which column a head or a cell is in — its position among its row's children,
|
|
170
|
+
* counted by `Table.Row`. Only meaningful under a `columns` root, and the
|
|
171
|
+
* counting is skipped entirely without one, so a table that sizes its cells by
|
|
172
|
+
* hand pays nothing for a model it is not using.
|
|
173
|
+
*/
|
|
174
|
+
const TableColumnContext = createContext<number>(-1);
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolves a head's or a cell's sizing against the column it sits in. What the
|
|
178
|
+
* part was given always wins: the model is the default for the column, not a
|
|
179
|
+
* rule about it, so the one row that has to differ still can.
|
|
180
|
+
*/
|
|
181
|
+
function useColumn(own: TableColumn): Required<Pick<TableColumn, 'align'>> & TableColumn {
|
|
182
|
+
const { columns } = useContext(TableContext);
|
|
183
|
+
const index = useContext(TableColumnContext);
|
|
184
|
+
const column = index >= 0 ? columns?.[index] : undefined;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
flex: own.flex ?? column?.flex,
|
|
188
|
+
width: own.width ?? column?.width,
|
|
189
|
+
align: own.align ?? column?.align ?? 'start',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Numbers a row's children so each one knows its column. Runs only when the
|
|
195
|
+
* root carries a `columns` model — otherwise the children are handed straight
|
|
196
|
+
* through, with no extra provider per cell on every row of the table.
|
|
197
|
+
*/
|
|
198
|
+
function useRowCells(children: ReactNode, enabled: boolean): ReactNode {
|
|
199
|
+
return useMemo(() => {
|
|
200
|
+
const cells = textChildren(children);
|
|
201
|
+
if (!enabled) return cells;
|
|
202
|
+
|
|
203
|
+
return Children.map(cells, (child, index) =>
|
|
204
|
+
isValidElement(child) ? (
|
|
205
|
+
<TableColumnContext.Provider value={index}>{child}</TableColumnContext.Provider>
|
|
206
|
+
) : (
|
|
207
|
+
child
|
|
208
|
+
)
|
|
209
|
+
);
|
|
210
|
+
}, [children, enabled]);
|
|
211
|
+
}
|
|
212
|
+
|
|
137
213
|
/**
|
|
138
214
|
* Which band of the table a row is in, and where in that band. A row needs all
|
|
139
215
|
* three: the section decides its text and its borders, the index decides
|
|
@@ -162,13 +238,25 @@ export interface TableProps extends ViewProps, VariantProps<typeof tableVariants
|
|
|
162
238
|
* for a short table, where the stripes are louder than the data.
|
|
163
239
|
*/
|
|
164
240
|
striped?: boolean;
|
|
241
|
+
/**
|
|
242
|
+
* The column model: one entry per column, in order. Every `Table.Head` and
|
|
243
|
+
* `Table.Cell` takes its `flex`, `width` and `align` from the entry at its
|
|
244
|
+
* own position in the row, so a column is described once instead of on every
|
|
245
|
+
* row. Anything set on a head or a cell still wins.
|
|
246
|
+
*
|
|
247
|
+
* Declare it outside render — a new array each frame renumbers every cell.
|
|
248
|
+
*/
|
|
249
|
+
columns?: TableColumn[];
|
|
165
250
|
children?: ReactNode;
|
|
166
251
|
}
|
|
167
252
|
|
|
168
253
|
const TableRoot = forwardRef<View, TableProps>(
|
|
169
|
-
(
|
|
254
|
+
(
|
|
255
|
+
{ className, variant, size = 'default', striped = false, columns, children, ...props },
|
|
256
|
+
ref
|
|
257
|
+
) => {
|
|
170
258
|
const { root } = tableVariants({ variant, size });
|
|
171
|
-
const context = useMemo(() => ({ size, striped }), [size, striped]);
|
|
259
|
+
const context = useMemo(() => ({ size, striped, columns }), [size, striped, columns]);
|
|
172
260
|
|
|
173
261
|
return (
|
|
174
262
|
<TableContext.Provider value={context}>
|
|
@@ -296,9 +384,10 @@ export interface TableRowProps
|
|
|
296
384
|
*/
|
|
297
385
|
const TableRow = forwardRef<View, TableRowProps>(
|
|
298
386
|
({ className, selected, disabled, index, last, children, onPress, ...props }, ref) => {
|
|
299
|
-
const { size, striped } = useContext(TableContext);
|
|
387
|
+
const { size, striped, columns } = useContext(TableContext);
|
|
300
388
|
const row = useContext(TableRowContext);
|
|
301
389
|
const { row: rowClass } = tableVariants({ size });
|
|
390
|
+
const cells = useRowCells(children, !!columns);
|
|
302
391
|
|
|
303
392
|
const position = index ?? row.index;
|
|
304
393
|
const isLast = last ?? row.last;
|
|
@@ -330,7 +419,7 @@ const TableRow = forwardRef<View, TableRowProps>(
|
|
|
330
419
|
className={classes}
|
|
331
420
|
{...(props as ViewProps)}
|
|
332
421
|
>
|
|
333
|
-
{
|
|
422
|
+
{cells}
|
|
334
423
|
</View>
|
|
335
424
|
);
|
|
336
425
|
}
|
|
@@ -347,7 +436,7 @@ const TableRow = forwardRef<View, TableRowProps>(
|
|
|
347
436
|
className={classes}
|
|
348
437
|
{...props}
|
|
349
438
|
>
|
|
350
|
-
{
|
|
439
|
+
{cells}
|
|
351
440
|
</AnimatedPressable>
|
|
352
441
|
);
|
|
353
442
|
}
|
|
@@ -368,13 +457,14 @@ export interface TableHeadProps extends Omit<ViewProps, 'children'> {
|
|
|
368
457
|
className?: string;
|
|
369
458
|
/**
|
|
370
459
|
* Share of the leftover width, relative to the other cells in the row.
|
|
371
|
-
* Defaults to 1, so columns divide the row evenly.
|
|
372
|
-
* the cells beneath it.
|
|
460
|
+
* Defaults to 1, so columns divide the row evenly. Without a `columns` model
|
|
461
|
+
* on the root it must match the `flex` on the cells beneath it.
|
|
373
462
|
*/
|
|
374
463
|
flex?: number;
|
|
375
464
|
/**
|
|
376
465
|
* Fixed width in pixels, for a column that must not move — an icon, a state
|
|
377
|
-
* dot.
|
|
466
|
+
* dot. Without a `columns` model on the root it must match the `width` on the
|
|
467
|
+
* cells beneath it.
|
|
378
468
|
*/
|
|
379
469
|
width?: number;
|
|
380
470
|
/**
|
|
@@ -415,12 +505,12 @@ const TableHead = forwardRef<View, TableHeadProps>(
|
|
|
415
505
|
{
|
|
416
506
|
className,
|
|
417
507
|
labelClassName,
|
|
418
|
-
align
|
|
508
|
+
align: ownAlign,
|
|
419
509
|
sortable,
|
|
420
510
|
sortDirection,
|
|
421
511
|
onPress,
|
|
422
|
-
flex,
|
|
423
|
-
width,
|
|
512
|
+
flex: ownFlex,
|
|
513
|
+
width: ownWidth,
|
|
424
514
|
children,
|
|
425
515
|
style,
|
|
426
516
|
...props
|
|
@@ -428,6 +518,11 @@ const TableHead = forwardRef<View, TableHeadProps>(
|
|
|
428
518
|
ref
|
|
429
519
|
) => {
|
|
430
520
|
const { size } = useContext(TableContext);
|
|
521
|
+
const { flex, width, align } = useColumn({
|
|
522
|
+
flex: ownFlex,
|
|
523
|
+
width: ownWidth,
|
|
524
|
+
align: ownAlign,
|
|
525
|
+
});
|
|
431
526
|
const { head, headLabel } = tableVariants({ size });
|
|
432
527
|
const arrowColor = useCSSVariable('--color-muted-foreground');
|
|
433
528
|
const activeArrowColor = useCSSVariable('--color-foreground');
|
|
@@ -528,15 +623,19 @@ export interface TableCellProps extends ViewProps {
|
|
|
528
623
|
className?: string;
|
|
529
624
|
/**
|
|
530
625
|
* Share of the leftover width, relative to the other cells in the row.
|
|
531
|
-
* Defaults to 1.
|
|
626
|
+
* Defaults to 1. Without a `columns` model on the root it must match the
|
|
627
|
+
* `flex` on the head above it.
|
|
532
628
|
*/
|
|
533
629
|
flex?: number;
|
|
534
630
|
/**
|
|
535
|
-
* Fixed width in pixels, for a column that must not move.
|
|
536
|
-
* `width` on the head above it.
|
|
631
|
+
* Fixed width in pixels, for a column that must not move. Without a `columns`
|
|
632
|
+
* model on the root it must match the `width` on the head above it.
|
|
537
633
|
*/
|
|
538
634
|
width?: number;
|
|
539
|
-
/**
|
|
635
|
+
/**
|
|
636
|
+
* Which edge the cell's content sits against. Without a `columns` model on
|
|
637
|
+
* the root, match the head above it.
|
|
638
|
+
*/
|
|
540
639
|
align?: CellAlign;
|
|
541
640
|
/** Styles the cell's text. */
|
|
542
641
|
labelClassName?: string;
|
|
@@ -550,10 +649,24 @@ export interface TableCellProps extends ViewProps {
|
|
|
550
649
|
*/
|
|
551
650
|
const TableCell = forwardRef<View, TableCellProps>(
|
|
552
651
|
(
|
|
553
|
-
{
|
|
652
|
+
{
|
|
653
|
+
className,
|
|
654
|
+
labelClassName,
|
|
655
|
+
align: ownAlign,
|
|
656
|
+
flex: ownFlex,
|
|
657
|
+
width: ownWidth,
|
|
658
|
+
children,
|
|
659
|
+
style,
|
|
660
|
+
...props
|
|
661
|
+
},
|
|
554
662
|
ref
|
|
555
663
|
) => {
|
|
556
664
|
const { size } = useContext(TableContext);
|
|
665
|
+
const { flex, width, align } = useColumn({
|
|
666
|
+
flex: ownFlex,
|
|
667
|
+
width: ownWidth,
|
|
668
|
+
align: ownAlign,
|
|
669
|
+
});
|
|
557
670
|
const { cell, cellLabel } = tableVariants({ size });
|
|
558
671
|
|
|
559
672
|
return (
|
|
@@ -635,6 +748,8 @@ export interface TableFrameProps extends Omit<ViewProps, 'children'> {
|
|
|
635
748
|
size?: TableSize;
|
|
636
749
|
/** Tint every other body row, as on `Table`. */
|
|
637
750
|
striped?: boolean;
|
|
751
|
+
/** The column model, as on `Table`. */
|
|
752
|
+
columns?: TableColumn[];
|
|
638
753
|
children?: ReactNode;
|
|
639
754
|
}
|
|
640
755
|
|
|
@@ -674,12 +789,13 @@ const TableFrame = forwardRef<View, TableFrameProps>(
|
|
|
674
789
|
description,
|
|
675
790
|
size = 'default',
|
|
676
791
|
striped = false,
|
|
792
|
+
columns,
|
|
677
793
|
children,
|
|
678
794
|
...props
|
|
679
795
|
},
|
|
680
796
|
ref
|
|
681
797
|
) => {
|
|
682
|
-
const context = useMemo(() => ({ size, striped }), [size, striped]);
|
|
798
|
+
const context = useMemo(() => ({ size, striped, columns }), [size, striped, columns]);
|
|
683
799
|
|
|
684
800
|
const { heading, body } = useMemo(() => {
|
|
685
801
|
let found: ReactElement<TableHeaderProps> | null = null;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Timeline — a vertical sequence of events.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* One `value` on the root says how far the sequence has got, and every item
|
|
5
|
+
* resolves its own state from its `step` against it — completed below, active
|
|
6
|
+
* at, inactive above. Stating it once is the point: a timeline whose items each
|
|
7
|
+
* carried their own state would let you write two active items, or none.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* `
|
|
9
|
+
* State is resolved in JS and handed down through context into `tv()` variants
|
|
10
|
+
* rather than being read off the element, because React Native has no
|
|
11
|
+
* attribute selectors for a stylesheet to key off. `Steps` resolves its states
|
|
12
|
+
* the same way, so the two read as siblings.
|
|
12
13
|
*
|
|
13
14
|
* Vertical only — a horizontal timeline gives each item roughly a fifth of a
|
|
14
15
|
* phone's width, which is not enough for a date and a title.
|
package/src/index.ts
CHANGED
|
@@ -267,6 +267,7 @@ export {
|
|
|
267
267
|
type TableCellProps,
|
|
268
268
|
type TableCaptionProps,
|
|
269
269
|
type TableEmptyProps,
|
|
270
|
+
type TableColumn,
|
|
270
271
|
type TableSortDirection,
|
|
271
272
|
} from './components/table';
|
|
272
273
|
export {
|
|
@@ -391,6 +392,20 @@ export {
|
|
|
391
392
|
type PanelsideSwipeFrom,
|
|
392
393
|
type UsePanelsideResult,
|
|
393
394
|
} from './components/panelside';
|
|
395
|
+
export {
|
|
396
|
+
Pagination,
|
|
397
|
+
paginationRange,
|
|
398
|
+
type PaginationProps,
|
|
399
|
+
type PaginationItemProps,
|
|
400
|
+
type PaginationPreviousProps,
|
|
401
|
+
type PaginationNextProps,
|
|
402
|
+
type PaginationEllipsisProps,
|
|
403
|
+
type PaginationSummaryProps,
|
|
404
|
+
type PaginationStatusProps,
|
|
405
|
+
type PaginationVariant,
|
|
406
|
+
type PaginationSize,
|
|
407
|
+
type PaginationItemValue,
|
|
408
|
+
} from './components/pagination';
|
|
394
409
|
export { Textarea, type TextareaProps } from './components/textarea';
|
|
395
410
|
export {
|
|
396
411
|
Item,
|