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.
Files changed (204) hide show
  1. package/.turbo/cache/eslint/.cache_16wwsjl +1 -0
  2. package/.turbo/cache/eslint/.cache_rpznjo +1 -0
  3. package/.turbo/turbo-build$colon$code.log +49 -0
  4. package/.turbo/turbo-build$colon$css.log +2 -0
  5. package/.turbo/turbo-build.log +43 -0
  6. package/.turbo/turbo-lint.log +2 -0
  7. package/dist/components.cjs +1 -3655
  8. package/dist/components.css +2 -2173
  9. package/dist/components.js +1 -3576
  10. package/dist/helpers.cjs +1 -311
  11. package/dist/helpers.d.cts +4 -1
  12. package/dist/helpers.d.ts +4 -1
  13. package/dist/helpers.js +1 -276
  14. package/dist/hooks.cjs +1 -1065
  15. package/dist/hooks.js +1 -998
  16. package/dist/layouts.cjs +1 -146
  17. package/dist/layouts.css +2 -5
  18. package/dist/layouts.js +1 -119
  19. package/dist/types.cjs +1 -19
  20. package/dist/types.js +0 -1
  21. package/eslint.config.js +4 -0
  22. package/package.json +16 -11
  23. package/postcss.config.js +3 -0
  24. package/src/components/accordion/accordion-item.tsx +123 -0
  25. package/src/components/accordion/accordion.component.tsx +80 -0
  26. package/src/components/accordion/accordion.styles.css +41 -0
  27. package/src/components/accordion/index.ts +2 -0
  28. package/src/components/button/button.component.tsx +50 -0
  29. package/src/components/button/button.styles.css +317 -0
  30. package/src/components/button/index.ts +1 -0
  31. package/src/components/cards/flip-card/flip-card.component.tsx +66 -0
  32. package/src/components/cards/flip-card/flip-card.styles.css +50 -0
  33. package/src/components/cards/flip-card/index.ts +1 -0
  34. package/src/components/cards/hover-card/hover-card.component.tsx +76 -0
  35. package/src/components/cards/hover-card/hover-card.styles.css +13 -0
  36. package/src/components/cards/hover-card/index.ts +1 -0
  37. package/src/components/cards/index.ts +3 -0
  38. package/src/components/cards/slide-card/index.ts +1 -0
  39. package/src/components/cards/slide-card/slide-card.component.tsx +27 -0
  40. package/src/components/component.types.ts +31 -0
  41. package/src/components/cursor/cursor.component.tsx +49 -0
  42. package/src/components/cursor/cursor.styles.css +73 -0
  43. package/src/components/cursor/index.ts +1 -0
  44. package/src/components/form/checkbox/checkbox.component.tsx +95 -0
  45. package/src/components/form/checkbox/checkbox.styles.css +162 -0
  46. package/src/components/form/checkbox/index.ts +1 -0
  47. package/src/components/form/controller/controller.component.tsx +42 -0
  48. package/src/components/form/controller/index.ts +1 -0
  49. package/src/components/form/date-picker/date-picker.component.tsx +66 -0
  50. package/src/components/form/date-picker/index.ts +1 -0
  51. package/src/components/form/field/field.component.tsx +84 -0
  52. package/src/components/form/field/field.styles.css +98 -0
  53. package/src/components/form/field/field.types.ts +34 -0
  54. package/src/components/form/field/index.ts +2 -0
  55. package/src/components/form/form.types.ts +37 -0
  56. package/src/components/form/index.ts +14 -0
  57. package/src/components/form/input/index.ts +1 -0
  58. package/src/components/form/input/input.component.tsx +75 -0
  59. package/src/components/form/input-color/index.ts +1 -0
  60. package/src/components/form/input-color/input-color.component.tsx +170 -0
  61. package/src/components/form/input-color/input-color.styles.css +92 -0
  62. package/src/components/form/input-file/index.ts +1 -0
  63. package/src/components/form/input-file/input-file.component.tsx +221 -0
  64. package/src/components/form/input-file/input-file.styles.css +143 -0
  65. package/src/components/form/input-number/index.ts +1 -0
  66. package/src/components/form/input-number/input-number.component.tsx +144 -0
  67. package/src/components/form/input-password/index.ts +1 -0
  68. package/src/components/form/input-password/input-password.component.tsx +77 -0
  69. package/src/components/form/radio/index.ts +1 -0
  70. package/src/components/form/radio/radio.component.tsx +92 -0
  71. package/src/components/form/radio/radio.styles.css +117 -0
  72. package/src/components/form/select/index.ts +1 -0
  73. package/src/components/form/select/option.tsx +101 -0
  74. package/src/components/form/select/options.tsx +165 -0
  75. package/src/components/form/select/select.component.tsx +317 -0
  76. package/src/components/form/select/select.styles.css +115 -0
  77. package/src/components/form/select/select.types.ts +97 -0
  78. package/src/components/form/slider/index.ts +1 -0
  79. package/src/components/form/slider/slider.component.tsx +117 -0
  80. package/src/components/form/slider/slider.styles.css +94 -0
  81. package/src/components/form/switch/index.ts +1 -0
  82. package/src/components/form/switch/switch.component.tsx +166 -0
  83. package/src/components/form/switch/switch.styles.css +165 -0
  84. package/src/components/form/textarea/index.ts +1 -0
  85. package/src/components/form/textarea/textarea.component.tsx +80 -0
  86. package/src/components/form/textarea/textarea.styles.css +7 -0
  87. package/src/components/image/image.tsx +7 -0
  88. package/src/components/image/index.ts +1 -0
  89. package/src/components/index.ts +15 -0
  90. package/src/components/infinity-scroll/index.ts +1 -0
  91. package/src/components/infinity-scroll/infinity-scroll.component.tsx +51 -0
  92. package/src/components/infinity-scroll/infinity-scroll.styles.css +25 -0
  93. package/src/components/line/index.ts +1 -0
  94. package/src/components/line/line.component.tsx +77 -0
  95. package/src/components/line/line.styles.css +61 -0
  96. package/src/components/modals/action-modal/action-modal.component.tsx +150 -0
  97. package/src/components/modals/action-modal/action-modal.styles.css +115 -0
  98. package/src/components/modals/action-modal/index.ts +1 -0
  99. package/src/components/modals/aside-modal/aside-modal.component.tsx +63 -0
  100. package/src/components/modals/aside-modal/aside-modal.styles.css +86 -0
  101. package/src/components/modals/aside-modal/index.ts +1 -0
  102. package/src/components/modals/confirmation-modal/confirmation-modal.component.tsx +43 -0
  103. package/src/components/modals/confirmation-modal/confirmation-modal.styles.css +17 -0
  104. package/src/components/modals/confirmation-modal/index.ts +1 -0
  105. package/src/components/modals/index.ts +6 -0
  106. package/src/components/modals/menu/index.ts +1 -0
  107. package/src/components/modals/menu/menu.component.tsx +194 -0
  108. package/src/components/modals/menu/menu.styles.css +101 -0
  109. package/src/components/modals/modal/index.ts +2 -0
  110. package/src/components/modals/modal/modal.backdrop.tsx +67 -0
  111. package/src/components/modals/modal/modal.component.tsx +104 -0
  112. package/src/components/modals/modal/modal.styles.css +70 -0
  113. package/src/components/modals/portal/index.ts +1 -0
  114. package/src/components/modals/portal/portal.component.tsx +19 -0
  115. package/src/components/ripple/index.ts +1 -0
  116. package/src/components/ripple/ripple.component.tsx +70 -0
  117. package/src/components/ripple/ripple.styles.css +33 -0
  118. package/src/components/smart-table/index.ts +2 -0
  119. package/src/components/smart-table/smart-table.column.tsx +62 -0
  120. package/src/components/smart-table/smart-table.component.tsx +116 -0
  121. package/src/components/smart-table/smart-table.helpers.tsx +58 -0
  122. package/src/components/smart-table/smart-table.hooks.ts +27 -0
  123. package/src/components/smart-table/smart-table.row.tsx +28 -0
  124. package/src/components/smart-table/smart-table.styles.css +102 -0
  125. package/src/components/smart-table/smart-table.types.ts +42 -0
  126. package/src/components/tabs/index.ts +1 -0
  127. package/src/components/tabs/tabs-list.tsx +120 -0
  128. package/src/components/tabs/tabs.styles.css +164 -0
  129. package/src/components/tabs/tabs.tsx +119 -0
  130. package/src/components/tag/index.ts +1 -0
  131. package/src/components/tag/tag.component.tsx +34 -0
  132. package/src/components/tag/tag.styles.css +50 -0
  133. package/src/components/tooltips/click-to-copy/click-to-copy.component.tsx +41 -0
  134. package/src/components/tooltips/click-to-copy/index.ts +1 -0
  135. package/src/components/tooltips/index.ts +2 -0
  136. package/src/components/tooltips/tooltip/index.ts +1 -0
  137. package/src/components/tooltips/tooltip/tooltip.component.tsx +64 -0
  138. package/src/components/tooltips/tooltip/tooltip.styles.css +91 -0
  139. package/src/components/typography/index.ts +1 -0
  140. package/src/components/typography/typography.component.tsx +81 -0
  141. package/src/components/typography/typography.constants.ts +53 -0
  142. package/src/components/typography/typography.styles.css +122 -0
  143. package/src/helpers/cn.ts +6 -0
  144. package/src/helpers/format-bytes.ts +11 -0
  145. package/src/helpers/format-dates.ts +47 -0
  146. package/src/helpers/get-modal-position-relative-to-screen.ts +86 -0
  147. package/src/helpers/get-modal-position.ts +211 -0
  148. package/src/helpers/index.ts +6 -0
  149. package/src/helpers/text/index.ts +1 -0
  150. package/src/helpers/text/to-capitalize.ts +17 -0
  151. package/src/hooks/index.ts +30 -0
  152. package/src/hooks/use-async.ts +88 -0
  153. package/src/hooks/use-classnames.ts +13 -0
  154. package/src/hooks/use-click-outside.ts +32 -0
  155. package/src/hooks/use-constant.ts +3 -0
  156. package/src/hooks/use-cookie.ts +124 -0
  157. package/src/hooks/use-debounce-state.ts +13 -0
  158. package/src/hooks/use-debounce.ts +15 -0
  159. package/src/hooks/use-dimensions.ts +19 -0
  160. package/src/hooks/use-dom-container.ts +35 -0
  161. package/src/hooks/use-event-listener.ts +71 -0
  162. package/src/hooks/use-file-reader.ts +69 -0
  163. package/src/hooks/use-geolocation.ts +63 -0
  164. package/src/hooks/use-hover.ts +17 -0
  165. package/src/hooks/use-in-view.ts +18 -0
  166. package/src/hooks/use-input-handlers.ts +52 -0
  167. package/src/hooks/use-intersection-observer.ts +19 -0
  168. package/src/hooks/use-media-query.ts +25 -0
  169. package/src/hooks/use-modal-in-container.ts +85 -0
  170. package/src/hooks/use-modal-transition.ts +64 -0
  171. package/src/hooks/use-modal.ts +21 -0
  172. package/src/hooks/use-mouse-position.ts +64 -0
  173. package/src/hooks/use-online-status.ts +12 -0
  174. package/src/hooks/use-render-count.ts +11 -0
  175. package/src/hooks/use-resize-observer.ts +18 -0
  176. package/src/hooks/use-safe-dispatch.ts +22 -0
  177. package/src/hooks/use-scroll.ts +31 -0
  178. package/src/hooks/use-state-history.ts +22 -0
  179. package/src/hooks/use-toggle-values.ts +14 -0
  180. package/src/hooks/use-toggle.ts +11 -0
  181. package/src/hooks/use-viewport.ts +38 -0
  182. package/src/index.ts +5 -0
  183. package/src/layouts/flex/flex.component.tsx +75 -0
  184. package/src/layouts/flex/index.ts +1 -0
  185. package/src/layouts/grid/grid.component.tsx +89 -0
  186. package/src/layouts/grid/grid.styles.css +5 -0
  187. package/src/layouts/grid/index.ts +1 -0
  188. package/src/layouts/index.ts +2 -0
  189. package/src/types/generics.ts +68 -0
  190. package/src/types/index.ts +1 -0
  191. package/tsconfig.json +10 -0
  192. package/tsup.config.cjs +36 -0
  193. package/dist/components.cjs.map +0 -1
  194. package/dist/components.css.map +0 -1
  195. package/dist/components.js.map +0 -1
  196. package/dist/helpers.cjs.map +0 -1
  197. package/dist/helpers.js.map +0 -1
  198. package/dist/hooks.cjs.map +0 -1
  199. package/dist/hooks.js.map +0 -1
  200. package/dist/layouts.cjs.map +0 -1
  201. package/dist/layouts.css.map +0 -1
  202. package/dist/layouts.js.map +0 -1
  203. package/dist/types.cjs.map +0 -1
  204. 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
+ };