polpo 0.1.13 → 0.1.16
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/.turbo/cache/eslint/.cache_16wwsjl +1 -0
- package/.turbo/cache/eslint/.cache_rpznjo +1 -0
- package/.turbo/turbo-build$colon$code.log +49 -0
- package/.turbo/turbo-build$colon$css.log +2 -0
- package/.turbo/turbo-build.log +43 -0
- package/.turbo/turbo-lint.log +2 -0
- package/dist/components.cjs +1 -3655
- package/dist/components.css +2 -2173
- package/dist/components.js +1 -3576
- package/dist/helpers.cjs +1 -311
- package/dist/helpers.d.cts +4 -1
- package/dist/helpers.d.ts +4 -1
- package/dist/helpers.js +1 -276
- package/dist/hooks.cjs +1 -1065
- package/dist/hooks.js +1 -998
- package/dist/layouts.cjs +1 -146
- package/dist/layouts.css +2 -5
- package/dist/layouts.js +1 -119
- package/dist/types.cjs +1 -19
- package/dist/types.js +0 -1
- package/eslint.config.js +4 -0
- package/package.json +16 -11
- package/postcss.config.js +3 -0
- package/src/components/accordion/accordion-item.tsx +123 -0
- package/src/components/accordion/accordion.component.tsx +80 -0
- package/src/components/accordion/accordion.styles.css +41 -0
- package/src/components/accordion/index.ts +2 -0
- package/src/components/button/button.component.tsx +50 -0
- package/src/components/button/button.styles.css +317 -0
- package/src/components/button/index.ts +1 -0
- package/src/components/cards/flip-card/flip-card.component.tsx +66 -0
- package/src/components/cards/flip-card/flip-card.styles.css +50 -0
- package/src/components/cards/flip-card/index.ts +1 -0
- package/src/components/cards/hover-card/hover-card.component.tsx +76 -0
- package/src/components/cards/hover-card/hover-card.styles.css +13 -0
- package/src/components/cards/hover-card/index.ts +1 -0
- package/src/components/cards/index.ts +3 -0
- package/src/components/cards/slide-card/index.ts +1 -0
- package/src/components/cards/slide-card/slide-card.component.tsx +27 -0
- package/src/components/component.types.ts +31 -0
- package/src/components/cursor/cursor.component.tsx +49 -0
- package/src/components/cursor/cursor.styles.css +73 -0
- package/src/components/cursor/index.ts +1 -0
- package/src/components/form/checkbox/checkbox.component.tsx +95 -0
- package/src/components/form/checkbox/checkbox.styles.css +162 -0
- package/src/components/form/checkbox/index.ts +1 -0
- package/src/components/form/controller/controller.component.tsx +42 -0
- package/src/components/form/controller/index.ts +1 -0
- package/src/components/form/date-picker/date-picker.component.tsx +66 -0
- package/src/components/form/date-picker/index.ts +1 -0
- package/src/components/form/field/field.component.tsx +84 -0
- package/src/components/form/field/field.styles.css +98 -0
- package/src/components/form/field/field.types.ts +34 -0
- package/src/components/form/field/index.ts +2 -0
- package/src/components/form/form.types.ts +37 -0
- package/src/components/form/index.ts +14 -0
- package/src/components/form/input/index.ts +1 -0
- package/src/components/form/input/input.component.tsx +75 -0
- package/src/components/form/input-color/index.ts +1 -0
- package/src/components/form/input-color/input-color.component.tsx +170 -0
- package/src/components/form/input-color/input-color.styles.css +92 -0
- package/src/components/form/input-file/index.ts +1 -0
- package/src/components/form/input-file/input-file.component.tsx +221 -0
- package/src/components/form/input-file/input-file.styles.css +143 -0
- package/src/components/form/input-number/index.ts +1 -0
- package/src/components/form/input-number/input-number.component.tsx +144 -0
- package/src/components/form/input-password/index.ts +1 -0
- package/src/components/form/input-password/input-password.component.tsx +77 -0
- package/src/components/form/radio/index.ts +1 -0
- package/src/components/form/radio/radio.component.tsx +92 -0
- package/src/components/form/radio/radio.styles.css +117 -0
- package/src/components/form/select/index.ts +1 -0
- package/src/components/form/select/option.tsx +101 -0
- package/src/components/form/select/options.tsx +165 -0
- package/src/components/form/select/select.component.tsx +317 -0
- package/src/components/form/select/select.styles.css +115 -0
- package/src/components/form/select/select.types.ts +97 -0
- package/src/components/form/slider/index.ts +1 -0
- package/src/components/form/slider/slider.component.tsx +117 -0
- package/src/components/form/slider/slider.styles.css +94 -0
- package/src/components/form/switch/index.ts +1 -0
- package/src/components/form/switch/switch.component.tsx +166 -0
- package/src/components/form/switch/switch.styles.css +165 -0
- package/src/components/form/textarea/index.ts +1 -0
- package/src/components/form/textarea/textarea.component.tsx +80 -0
- package/src/components/form/textarea/textarea.styles.css +7 -0
- package/src/components/image/image.tsx +7 -0
- package/src/components/image/index.ts +1 -0
- package/src/components/index.ts +15 -0
- package/src/components/infinity-scroll/index.ts +1 -0
- package/src/components/infinity-scroll/infinity-scroll.component.tsx +51 -0
- package/src/components/infinity-scroll/infinity-scroll.styles.css +25 -0
- package/src/components/line/index.ts +1 -0
- package/src/components/line/line.component.tsx +77 -0
- package/src/components/line/line.styles.css +61 -0
- package/src/components/modals/action-modal/action-modal.component.tsx +150 -0
- package/src/components/modals/action-modal/action-modal.styles.css +115 -0
- package/src/components/modals/action-modal/index.ts +1 -0
- package/src/components/modals/aside-modal/aside-modal.component.tsx +63 -0
- package/src/components/modals/aside-modal/aside-modal.styles.css +86 -0
- package/src/components/modals/aside-modal/index.ts +1 -0
- package/src/components/modals/confirmation-modal/confirmation-modal.component.tsx +43 -0
- package/src/components/modals/confirmation-modal/confirmation-modal.styles.css +17 -0
- package/src/components/modals/confirmation-modal/index.ts +1 -0
- package/src/components/modals/index.ts +6 -0
- package/src/components/modals/menu/index.ts +1 -0
- package/src/components/modals/menu/menu.component.tsx +194 -0
- package/src/components/modals/menu/menu.styles.css +101 -0
- package/src/components/modals/modal/index.ts +2 -0
- package/src/components/modals/modal/modal.backdrop.tsx +67 -0
- package/src/components/modals/modal/modal.component.tsx +104 -0
- package/src/components/modals/modal/modal.styles.css +70 -0
- package/src/components/modals/portal/index.ts +1 -0
- package/src/components/modals/portal/portal.component.tsx +19 -0
- package/src/components/ripple/index.ts +1 -0
- package/src/components/ripple/ripple.component.tsx +70 -0
- package/src/components/ripple/ripple.styles.css +33 -0
- package/src/components/smart-table/index.ts +2 -0
- package/src/components/smart-table/smart-table.column.tsx +62 -0
- package/src/components/smart-table/smart-table.component.tsx +116 -0
- package/src/components/smart-table/smart-table.helpers.tsx +58 -0
- package/src/components/smart-table/smart-table.hooks.ts +27 -0
- package/src/components/smart-table/smart-table.row.tsx +28 -0
- package/src/components/smart-table/smart-table.styles.css +102 -0
- package/src/components/smart-table/smart-table.types.ts +42 -0
- package/src/components/tabs/index.ts +1 -0
- package/src/components/tabs/tabs-list.tsx +120 -0
- package/src/components/tabs/tabs.styles.css +164 -0
- package/src/components/tabs/tabs.tsx +119 -0
- package/src/components/tag/index.ts +1 -0
- package/src/components/tag/tag.component.tsx +34 -0
- package/src/components/tag/tag.styles.css +50 -0
- package/src/components/tooltips/click-to-copy/click-to-copy.component.tsx +41 -0
- package/src/components/tooltips/click-to-copy/index.ts +1 -0
- package/src/components/tooltips/index.ts +2 -0
- package/src/components/tooltips/tooltip/index.ts +1 -0
- package/src/components/tooltips/tooltip/tooltip.component.tsx +64 -0
- package/src/components/tooltips/tooltip/tooltip.styles.css +91 -0
- package/src/components/typography/index.ts +1 -0
- package/src/components/typography/typography.component.tsx +81 -0
- package/src/components/typography/typography.constants.ts +53 -0
- package/src/components/typography/typography.styles.css +122 -0
- package/src/helpers/cn.ts +6 -0
- package/src/helpers/format-bytes.ts +11 -0
- package/src/helpers/format-dates.ts +47 -0
- package/src/helpers/get-modal-position-relative-to-screen.ts +86 -0
- package/src/helpers/get-modal-position.ts +211 -0
- package/src/helpers/index.ts +6 -0
- package/src/helpers/text/index.ts +1 -0
- package/src/helpers/text/to-capitalize.ts +17 -0
- package/src/hooks/index.ts +30 -0
- package/src/hooks/use-async.ts +88 -0
- package/src/hooks/use-classnames.ts +13 -0
- package/src/hooks/use-click-outside.ts +32 -0
- package/src/hooks/use-constant.ts +3 -0
- package/src/hooks/use-cookie.ts +124 -0
- package/src/hooks/use-debounce-state.ts +13 -0
- package/src/hooks/use-debounce.ts +15 -0
- package/src/hooks/use-dimensions.ts +19 -0
- package/src/hooks/use-dom-container.ts +35 -0
- package/src/hooks/use-event-listener.ts +71 -0
- package/src/hooks/use-file-reader.ts +69 -0
- package/src/hooks/use-geolocation.ts +63 -0
- package/src/hooks/use-hover.ts +17 -0
- package/src/hooks/use-in-view.ts +18 -0
- package/src/hooks/use-input-handlers.ts +52 -0
- package/src/hooks/use-intersection-observer.ts +19 -0
- package/src/hooks/use-media-query.ts +25 -0
- package/src/hooks/use-modal-in-container.ts +85 -0
- package/src/hooks/use-modal-transition.ts +64 -0
- package/src/hooks/use-modal.ts +21 -0
- package/src/hooks/use-mouse-position.ts +64 -0
- package/src/hooks/use-online-status.ts +12 -0
- package/src/hooks/use-render-count.ts +11 -0
- package/src/hooks/use-resize-observer.ts +18 -0
- package/src/hooks/use-safe-dispatch.ts +22 -0
- package/src/hooks/use-scroll.ts +31 -0
- package/src/hooks/use-state-history.ts +22 -0
- package/src/hooks/use-toggle-values.ts +14 -0
- package/src/hooks/use-toggle.ts +11 -0
- package/src/hooks/use-viewport.ts +38 -0
- package/src/index.ts +5 -0
- package/src/layouts/flex/flex.component.tsx +75 -0
- package/src/layouts/flex/index.ts +1 -0
- package/src/layouts/grid/grid.component.tsx +89 -0
- package/src/layouts/grid/grid.styles.css +5 -0
- package/src/layouts/grid/index.ts +1 -0
- package/src/layouts/index.ts +2 -0
- package/src/types/generics.ts +68 -0
- package/src/types/index.ts +1 -0
- package/tsconfig.json +10 -0
- package/tsup.config.cjs +36 -0
- package/dist/components.cjs.map +0 -1
- package/dist/components.css.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/helpers.cjs.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/hooks.cjs.map +0 -1
- package/dist/hooks.js.map +0 -1
- package/dist/layouts.cjs.map +0 -1
- package/dist/layouts.css.map +0 -1
- package/dist/layouts.js.map +0 -1
- package/dist/types.cjs.map +0 -1
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { MdArrowDownward, MdArrowUpward } from 'react-icons/md';
|
|
3
|
+
|
|
4
|
+
import { useClassNames } from '../../hooks';
|
|
5
|
+
import { Grid } from '../../layouts';
|
|
6
|
+
import { Ripple } from '../ripple';
|
|
7
|
+
import { Typography } from '../typography';
|
|
8
|
+
|
|
9
|
+
import { ColumnData, Order, RowDataObject } from './smart-table.types';
|
|
10
|
+
|
|
11
|
+
type SmartTableColumnProps<RowData extends RowDataObject> = ColumnData<RowData> & {
|
|
12
|
+
toggleSortField: (field: ColumnData<RowData>['sortBy']) => void;
|
|
13
|
+
sortField: ColumnData<RowData>['sortBy'] | null;
|
|
14
|
+
orderField: Order | null;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const SmartTableColumn = <RowData extends RowDataObject>({
|
|
18
|
+
header,
|
|
19
|
+
sortBy,
|
|
20
|
+
icon: Icon,
|
|
21
|
+
width,
|
|
22
|
+
field,
|
|
23
|
+
toggleSortField,
|
|
24
|
+
sortField,
|
|
25
|
+
orderField,
|
|
26
|
+
hidden,
|
|
27
|
+
}: SmartTableColumnProps<RowData>) => {
|
|
28
|
+
const sortByKey = sortBy !== undefined ? sortBy : field;
|
|
29
|
+
|
|
30
|
+
const columnClassName = useClassNames({
|
|
31
|
+
'sort-on-click': Boolean(sortByKey),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const columnOnClick = sortByKey ? () => toggleSortField(sortByKey) : undefined;
|
|
35
|
+
|
|
36
|
+
const sortIcon = useMemo(() => {
|
|
37
|
+
if (!sortByKey) return null;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<span className='sort-icon'>
|
|
41
|
+
{Boolean(sortField) &&
|
|
42
|
+
sortField === sortByKey &&
|
|
43
|
+
(orderField === 'asc' ? <MdArrowUpward /> : <MdArrowDownward />)}
|
|
44
|
+
</span>
|
|
45
|
+
);
|
|
46
|
+
}, [sortByKey, sortField, orderField]);
|
|
47
|
+
|
|
48
|
+
if (hidden) return null;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<th style={{ width }} className={columnClassName} onClick={columnOnClick}>
|
|
52
|
+
<Grid flow='column' gap='0.5em' jc='space-between' ai='center'>
|
|
53
|
+
{Icon ? <Icon /> : null}
|
|
54
|
+
<Typography variant='label' noPadding>
|
|
55
|
+
{header}
|
|
56
|
+
</Typography>
|
|
57
|
+
{sortIcon}
|
|
58
|
+
</Grid>
|
|
59
|
+
<Ripple color='var(--color-primary-700)' />
|
|
60
|
+
</th>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useMemo, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useClassNames } from '../../hooks';
|
|
4
|
+
import { KeyValuesOf } from '../../types';
|
|
5
|
+
import { Checkbox } from '../form';
|
|
6
|
+
|
|
7
|
+
import { SmartTableColumn } from './smart-table.column';
|
|
8
|
+
import { sortData } from './smart-table.helpers';
|
|
9
|
+
import { useSort } from './smart-table.hooks';
|
|
10
|
+
import { SmartTableRow } from './smart-table.row';
|
|
11
|
+
import './smart-table.styles.css';
|
|
12
|
+
import { ColumnData, RowDataObject } from './smart-table.types';
|
|
13
|
+
|
|
14
|
+
type SmartTableProps<RowData extends RowDataObject> = {
|
|
15
|
+
columns: Array<ColumnData<RowData>>;
|
|
16
|
+
data: Array<RowData>;
|
|
17
|
+
width?: 'scroll' | 'content';
|
|
18
|
+
selectable?: boolean;
|
|
19
|
+
rowId: KeyValuesOf<RowData, React.Key>;
|
|
20
|
+
className?: string;
|
|
21
|
+
tableClassName?: string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const SmartTable = <RowData extends RowDataObject>({
|
|
25
|
+
columns: initialColumns,
|
|
26
|
+
data,
|
|
27
|
+
width,
|
|
28
|
+
selectable = false,
|
|
29
|
+
rowId,
|
|
30
|
+
className = '',
|
|
31
|
+
tableClassName: _tableClassName = '',
|
|
32
|
+
}: SmartTableProps<RowData>) => {
|
|
33
|
+
const [selected, setSelected] = useState<{ [key: string]: RowData }>({});
|
|
34
|
+
|
|
35
|
+
const tableClassName = useClassNames({
|
|
36
|
+
'smart-table': true,
|
|
37
|
+
[_tableClassName]: Boolean(_tableClassName),
|
|
38
|
+
'layout-fixed': width === 'content',
|
|
39
|
+
'layout-scrollable': width === 'scroll',
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const tableContainerClassName = useClassNames({
|
|
43
|
+
'smart-table-container': true,
|
|
44
|
+
[className]: Boolean(className),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const { sortBy, order, toggleSortField } = useSort<RowData>();
|
|
48
|
+
|
|
49
|
+
const columns = useMemo(() => {
|
|
50
|
+
let finalColumns = [...initialColumns];
|
|
51
|
+
|
|
52
|
+
if (selectable) {
|
|
53
|
+
finalColumns = [
|
|
54
|
+
{
|
|
55
|
+
header: `${Object.values(selected).length}`,
|
|
56
|
+
render: (data, rowKey) => (
|
|
57
|
+
<Checkbox
|
|
58
|
+
name={`selected-${rowKey}`}
|
|
59
|
+
value={`${rowKey}` in selected}
|
|
60
|
+
setValue={isSelected => {
|
|
61
|
+
setSelected(prev => {
|
|
62
|
+
const copy = { ...prev };
|
|
63
|
+
|
|
64
|
+
if (isSelected) {
|
|
65
|
+
copy[`${rowKey}`] = data;
|
|
66
|
+
} else {
|
|
67
|
+
delete copy[`${rowKey}`];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return copy;
|
|
71
|
+
});
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
),
|
|
75
|
+
},
|
|
76
|
+
...finalColumns,
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return finalColumns;
|
|
81
|
+
}, [initialColumns, selectable, selected]);
|
|
82
|
+
|
|
83
|
+
const rows = useMemo(() => sortData<RowData>(data, sortBy, order), [data, sortBy, order]);
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<section className={tableContainerClassName}>
|
|
87
|
+
<table className={tableClassName}>
|
|
88
|
+
<thead>
|
|
89
|
+
<tr>
|
|
90
|
+
{columns.map((column, key) => (
|
|
91
|
+
<SmartTableColumn
|
|
92
|
+
key={key}
|
|
93
|
+
toggleSortField={toggleSortField}
|
|
94
|
+
sortField={sortBy}
|
|
95
|
+
orderField={order}
|
|
96
|
+
{...column}
|
|
97
|
+
/>
|
|
98
|
+
))}
|
|
99
|
+
</tr>
|
|
100
|
+
</thead>
|
|
101
|
+
<tbody>
|
|
102
|
+
{rows.map(row => (
|
|
103
|
+
<SmartTableRow
|
|
104
|
+
key={row[rowId] as React.Key}
|
|
105
|
+
rowKey={row[rowId] as React.Key}
|
|
106
|
+
data={row}
|
|
107
|
+
columns={columns}
|
|
108
|
+
isSelected={`${row[rowId] as React.Key}` in selected}
|
|
109
|
+
selectable={selectable}
|
|
110
|
+
/>
|
|
111
|
+
))}
|
|
112
|
+
</tbody>
|
|
113
|
+
</table>
|
|
114
|
+
</section>
|
|
115
|
+
);
|
|
116
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { Typography } from '../typography';
|
|
4
|
+
|
|
5
|
+
import { ColumnData, Order, Primitive, RowDataObject } from './smart-table.types';
|
|
6
|
+
|
|
7
|
+
const renderRowColumn = <RowData extends RowDataObject>(
|
|
8
|
+
data: RowData,
|
|
9
|
+
{ field, render, Component }: ColumnData<RowData>,
|
|
10
|
+
rowKey: React.Key,
|
|
11
|
+
): React.ReactNode => {
|
|
12
|
+
if (field && data[field])
|
|
13
|
+
return (
|
|
14
|
+
<Typography noPadding nowrap>
|
|
15
|
+
{data[field] as Primitive}
|
|
16
|
+
</Typography>
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (render) return render(data, rowKey);
|
|
20
|
+
|
|
21
|
+
if (Component) return <Component data={data} rowKey={rowKey} />;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const renderRow = <RowData extends RowDataObject>(
|
|
25
|
+
data: RowData,
|
|
26
|
+
columns: Array<ColumnData<RowData>>,
|
|
27
|
+
rowKey: React.Key,
|
|
28
|
+
) => columns.map((column, key) => (column.hidden ? null : <td key={key}>{renderRowColumn(data, column, rowKey)}</td>));
|
|
29
|
+
|
|
30
|
+
export const sortData = <RowData extends RowDataObject>(
|
|
31
|
+
data: Array<RowData>,
|
|
32
|
+
sortBy: ColumnData<RowData>['sortBy'] | null,
|
|
33
|
+
order: Order | null,
|
|
34
|
+
) => {
|
|
35
|
+
if (sortBy) {
|
|
36
|
+
return data.toSorted((a, b) => {
|
|
37
|
+
const fieldA = a[sortBy];
|
|
38
|
+
const fieldB = b[sortBy];
|
|
39
|
+
|
|
40
|
+
if (
|
|
41
|
+
['number', 'boolean', 'string'].includes(typeof fieldA) &&
|
|
42
|
+
['number', 'boolean', 'string'].includes(typeof fieldB)
|
|
43
|
+
) {
|
|
44
|
+
if (fieldA === fieldB) return 0;
|
|
45
|
+
|
|
46
|
+
if (order === 'asc') {
|
|
47
|
+
return fieldA > fieldB ? 1 : -1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return fieldA > fieldB ? -1 : 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return 0;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return data;
|
|
58
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { ColumnData, Order, RowDataObject } from './smart-table.types';
|
|
4
|
+
|
|
5
|
+
export const useSort = <RowData extends RowDataObject>() => {
|
|
6
|
+
const [[sortBy, order], setSortField] = useState<[ColumnData<RowData>['sortBy'] | null, Order | null]>([null, null]);
|
|
7
|
+
|
|
8
|
+
const toggleSortField = useCallback((sortByField: ColumnData<RowData>['sortBy']) => {
|
|
9
|
+
setSortField(([sortBy, order]) => {
|
|
10
|
+
if (sortByField !== sortBy) {
|
|
11
|
+
return [sortByField, 'asc'];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (order === 'asc') {
|
|
15
|
+
return [sortByField, 'desc'];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return [null, null];
|
|
19
|
+
});
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
sortBy,
|
|
24
|
+
order,
|
|
25
|
+
toggleSortField,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { useClassNames } from '../../hooks';
|
|
4
|
+
|
|
5
|
+
import { renderRow } from './smart-table.helpers';
|
|
6
|
+
import { ColumnData, RowDataObject } from './smart-table.types';
|
|
7
|
+
|
|
8
|
+
type SmartTableRowProps<RowData extends RowDataObject> = {
|
|
9
|
+
data: RowData;
|
|
10
|
+
columns: Array<ColumnData<RowData>>;
|
|
11
|
+
isSelected: boolean;
|
|
12
|
+
selectable: boolean;
|
|
13
|
+
rowKey: React.Key;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const SmartTableRow = <RowData extends RowDataObject>({
|
|
17
|
+
data,
|
|
18
|
+
columns,
|
|
19
|
+
isSelected = false,
|
|
20
|
+
selectable = false,
|
|
21
|
+
rowKey,
|
|
22
|
+
}: SmartTableRowProps<RowData>) => {
|
|
23
|
+
const rowClassName = useClassNames({
|
|
24
|
+
'row-selected': selectable && isSelected,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
return <tr className={rowClassName}>{renderRow(data, columns, rowKey)}</tr>;
|
|
28
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
@reference "polpo-tailwind-config/styles";
|
|
2
|
+
|
|
3
|
+
.smart-table-container {
|
|
4
|
+
border: 0 solid var(--color-primary-500);
|
|
5
|
+
border-radius: 5px;
|
|
6
|
+
overflow: auto;
|
|
7
|
+
height: 100%;
|
|
8
|
+
width: 100%;
|
|
9
|
+
margin: auto;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.smart-table {
|
|
13
|
+
border-collapse: collapse;
|
|
14
|
+
position: relative;
|
|
15
|
+
width: 100%;
|
|
16
|
+
|
|
17
|
+
&.layout-fixed {
|
|
18
|
+
table-layout: fixed;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&.layout-scrollable {
|
|
22
|
+
table-layout: auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
thead {
|
|
26
|
+
position: sticky;
|
|
27
|
+
z-index: 10;
|
|
28
|
+
top: 0;
|
|
29
|
+
box-shadow:
|
|
30
|
+
0 3px 5px 0 rgba(0, 0, 0, 0.2),
|
|
31
|
+
0 1px 10px 0 rgba(0, 0, 0, 0.12);
|
|
32
|
+
|
|
33
|
+
tr {
|
|
34
|
+
background: var(--color-primary-500);
|
|
35
|
+
color: var(--color-primary-50);
|
|
36
|
+
transition: all 300ms ease;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
tbody {
|
|
41
|
+
tr {
|
|
42
|
+
box-shadow: inset 0 0 0 0 var(--color-primary-500);
|
|
43
|
+
transition: box-shadow 300ms ease;
|
|
44
|
+
|
|
45
|
+
&.row-selected,
|
|
46
|
+
&:hover {
|
|
47
|
+
background: var(--color-background-paper);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
&.row-selected {
|
|
51
|
+
box-shadow: inset 10px 0 0 -5px var(--color-primary-500);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&:not(:last-child) {
|
|
55
|
+
border-bottom: 1px solid var(--color-border);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
th {
|
|
61
|
+
padding: 0.8em 1.2em;
|
|
62
|
+
user-select: none;
|
|
63
|
+
|
|
64
|
+
&.sort-on-click {
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.sort-icon {
|
|
69
|
+
width: 1em;
|
|
70
|
+
display: block;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
svg {
|
|
74
|
+
animation: fadeIn 300ms ease;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
path {
|
|
78
|
+
transition: all 300ms ease;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
&:first-child {
|
|
82
|
+
padding-left: 2em;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
&:last-child {
|
|
86
|
+
padding-right: 2em;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
td {
|
|
91
|
+
transition: all 300ms ease;
|
|
92
|
+
padding: 0.6em 1em;
|
|
93
|
+
|
|
94
|
+
&:first-child {
|
|
95
|
+
padding-left: 2em;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
&:last-child {
|
|
99
|
+
padding-right: 2em;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { IconType } from 'react-icons';
|
|
3
|
+
|
|
4
|
+
import { KeyValuesOf } from '../../types';
|
|
5
|
+
|
|
6
|
+
export type Primitive = string | number | boolean;
|
|
7
|
+
|
|
8
|
+
export type RowDataObject = { [key: string]: Primitive | object };
|
|
9
|
+
|
|
10
|
+
export type Order = 'asc' | 'desc';
|
|
11
|
+
|
|
12
|
+
type ColumnDataCommon<RowData extends RowDataObject> = {
|
|
13
|
+
header: React.ReactNode;
|
|
14
|
+
sortBy?: KeyValuesOf<RowData, Primitive> | null;
|
|
15
|
+
icon?: IconType;
|
|
16
|
+
width?: `${number}px` | `${number}em` | `${number}%`;
|
|
17
|
+
hidden?: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type ColumnDataField<RowData extends RowDataObject> = ColumnDataCommon<RowData> & {
|
|
21
|
+
field: KeyValuesOf<RowData, Primitive>;
|
|
22
|
+
sortBy?: null;
|
|
23
|
+
render?: never;
|
|
24
|
+
Component?: never;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
type ColumnDataRender<RowData extends RowDataObject> = ColumnDataCommon<RowData> & {
|
|
28
|
+
field?: never;
|
|
29
|
+
render: (row: RowData, rowKey: React.Key) => React.ReactNode;
|
|
30
|
+
Component?: never;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type ColumnDataComponent<RowData extends RowDataObject> = ColumnDataCommon<RowData> & {
|
|
34
|
+
field?: never;
|
|
35
|
+
render?: never;
|
|
36
|
+
Component: React.FC<{ data: RowData; rowKey: React.Key }>;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type ColumnData<RowData extends RowDataObject> =
|
|
40
|
+
| ColumnDataField<RowData>
|
|
41
|
+
| ColumnDataRender<RowData>
|
|
42
|
+
| ColumnDataComponent<RowData>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './tabs';
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useClassNames } from '../../hooks';
|
|
4
|
+
|
|
5
|
+
import { Tabs } from './tabs';
|
|
6
|
+
|
|
7
|
+
export enum RadiusVariants {
|
|
8
|
+
None = 'none',
|
|
9
|
+
Small = 'small',
|
|
10
|
+
Medium = 'medium',
|
|
11
|
+
Large = 'large',
|
|
12
|
+
Full = 'full',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export enum SizeVariants {
|
|
16
|
+
Small = 'small',
|
|
17
|
+
Medium = 'medium',
|
|
18
|
+
Large = 'large',
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DefaultRect = {
|
|
22
|
+
top: 0,
|
|
23
|
+
left: 0,
|
|
24
|
+
width: 0,
|
|
25
|
+
height: 0,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export enum TabListVariant {
|
|
29
|
+
SOLID = 'solid',
|
|
30
|
+
GHOST = 'ghost',
|
|
31
|
+
FLAT = 'flat',
|
|
32
|
+
LINE = 'line',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export enum TabListColor {
|
|
36
|
+
PRIMARY = 'primary',
|
|
37
|
+
SECONDARY = 'secondary',
|
|
38
|
+
TERTIARY = 'tertiary',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export enum TabListDirection {
|
|
42
|
+
HORIZONTAL = 'horizontal',
|
|
43
|
+
VERTICAL = 'vertical',
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TabListProps = {
|
|
47
|
+
openTab: string;
|
|
48
|
+
variant?: `${TabListVariant}`;
|
|
49
|
+
children?: React.ReactNode;
|
|
50
|
+
size?: `${SizeVariants}`;
|
|
51
|
+
color?: `${TabListColor}`;
|
|
52
|
+
radius?: `${RadiusVariants}`;
|
|
53
|
+
direction?: `${TabListDirection}`;
|
|
54
|
+
className?: string;
|
|
55
|
+
style?: React.CSSProperties;
|
|
56
|
+
tabs?: Array<{
|
|
57
|
+
id: string;
|
|
58
|
+
label: React.ReactNode;
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const TabsList = ({
|
|
63
|
+
tabs = [],
|
|
64
|
+
variant = TabListVariant.SOLID,
|
|
65
|
+
radius = RadiusVariants.Full,
|
|
66
|
+
direction = TabListDirection.HORIZONTAL,
|
|
67
|
+
color,
|
|
68
|
+
children,
|
|
69
|
+
openTab,
|
|
70
|
+
size = SizeVariants.Medium,
|
|
71
|
+
className = '',
|
|
72
|
+
style = {},
|
|
73
|
+
}: TabListProps) => {
|
|
74
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
75
|
+
const selectedTabRef = useRef<HTMLSpanElement>(null);
|
|
76
|
+
const [isSelectorActive, setIsSelectorActive] = useState(false);
|
|
77
|
+
const [selector, setSelector] = useState(DefaultRect);
|
|
78
|
+
const containerClassNames = useClassNames({
|
|
79
|
+
'tab-list': true,
|
|
80
|
+
[`variant-${variant}`]: Boolean(variant),
|
|
81
|
+
[`size-${size}`]: Boolean(size),
|
|
82
|
+
[`radius-${radius}`]: Boolean(radius),
|
|
83
|
+
[`color-${color}`]: Boolean(color),
|
|
84
|
+
'vertical-direction': direction === TabListDirection.VERTICAL,
|
|
85
|
+
[className]: Boolean(className),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
const selectedRect = selectedTabRef.current?.getBoundingClientRect() ?? DefaultRect;
|
|
90
|
+
const containerRect = containerRef.current?.getBoundingClientRect() ?? DefaultRect;
|
|
91
|
+
|
|
92
|
+
const width = selectedRect.width;
|
|
93
|
+
const height = variant === TabListVariant.LINE ? 2 : selectedRect.height;
|
|
94
|
+
const left = selectedRect.left - containerRect.left;
|
|
95
|
+
const top =
|
|
96
|
+
variant === TabListVariant.LINE
|
|
97
|
+
? selectedRect.top + selectedRect.height - containerRect.top
|
|
98
|
+
: selectedRect.top - containerRect.top;
|
|
99
|
+
|
|
100
|
+
setSelector({ left, top, width, height });
|
|
101
|
+
|
|
102
|
+
if (!isSelectorActive) {
|
|
103
|
+
setTimeout(() => {
|
|
104
|
+
setIsSelectorActive(true);
|
|
105
|
+
}, 100);
|
|
106
|
+
}
|
|
107
|
+
}, [isSelectorActive, variant, openTab]);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<section className={containerClassNames} ref={containerRef} style={style}>
|
|
111
|
+
{Boolean(variant) && <span className={`tabs-selector ${isSelectorActive ? 'active' : ''}`} style={selector} />}
|
|
112
|
+
{tabs.map(({ id, label }) => (
|
|
113
|
+
<Tabs.Tab key={id} id={id} ref={id === openTab ? selectedTabRef : undefined}>
|
|
114
|
+
{label}
|
|
115
|
+
</Tabs.Tab>
|
|
116
|
+
))}
|
|
117
|
+
{children}
|
|
118
|
+
</section>
|
|
119
|
+
);
|
|
120
|
+
};
|