@vygruppen/spor-react 12.24.0 → 12.24.2
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/turbo-build.log +10 -10
- package/.turbo/turbo-postinstall.log +1 -1
- package/CHANGELOG.md +16 -0
- package/dist/index.cjs +122 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -13
- package/dist/index.d.ts +10 -13
- package/dist/index.mjs +113 -69
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/input/Field.tsx +2 -1
- package/src/input/Textarea.tsx +1 -1
- package/src/table/Table.tsx +50 -34
- package/src/table/index.tsx +2 -0
- package/src/table/sort-utils.ts +31 -34
- package/src/theme/recipes/attached-inputs.ts +7 -7
- package/src/theme/slot-recipes/native-select.ts +5 -0
- package/src/util/externals.tsx +7 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vygruppen/spor-react",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "12.24.
|
|
4
|
+
"version": "12.24.2",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"react-stately": "^3.31.1",
|
|
47
47
|
"react-swipeable": "^7.0.1",
|
|
48
48
|
"usehooks-ts": "^3.1.0",
|
|
49
|
-
"@vygruppen/spor-icon-react": "4.5.1",
|
|
50
49
|
"@vygruppen/spor-design-tokens": "4.3.2",
|
|
50
|
+
"@vygruppen/spor-icon-react": "4.5.2",
|
|
51
51
|
"@vygruppen/spor-loader": "0.7.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
"vitest": "^0.26.3",
|
|
69
69
|
"vitest-axe": "^0.1.0",
|
|
70
70
|
"vitest-canvas-mock": "^0.2.2",
|
|
71
|
-
"@vygruppen/
|
|
72
|
-
"@vygruppen/
|
|
71
|
+
"@vygruppen/tsconfig": "0.1.1",
|
|
72
|
+
"@vygruppen/eslint-config": "2.1.0"
|
|
73
73
|
},
|
|
74
74
|
"peerDependencies": {
|
|
75
75
|
"react": ">=18.0.0 <19.0.0",
|
package/src/input/Field.tsx
CHANGED
|
@@ -113,7 +113,7 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
|
|
113
113
|
id={id}
|
|
114
114
|
>
|
|
115
115
|
{label && !floatingLabel && (
|
|
116
|
-
<Label asChild={labelAsChild}>
|
|
116
|
+
<Label asChild={labelAsChild} aria-hidden>
|
|
117
117
|
{renderLabelWithIndicator(label, labelAsChild)}
|
|
118
118
|
</Label>
|
|
119
119
|
)}
|
|
@@ -124,6 +124,7 @@ export const Field = React.forwardRef<HTMLDivElement, FieldProps>(
|
|
|
124
124
|
<FloatingLabel
|
|
125
125
|
data-float={shouldFloat ? true : undefined}
|
|
126
126
|
asChild={labelAsChild}
|
|
127
|
+
aria-hidden
|
|
127
128
|
>
|
|
128
129
|
{renderLabelWithIndicator(label, labelAsChild)}
|
|
129
130
|
</FloatingLabel>
|
package/src/input/Textarea.tsx
CHANGED
package/src/table/Table.tsx
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
TableBodyProps as ChakraTableBodyProps,
|
|
8
8
|
TableColumnHeaderProps as ChakraTableColumnHeaderProps,
|
|
9
9
|
TableRootProps as ChakraTableProps,
|
|
10
|
-
TableRowProps as ChakraTableRowProps,
|
|
11
10
|
useSlotRecipe,
|
|
12
11
|
} from "@chakra-ui/react";
|
|
13
12
|
import {
|
|
@@ -20,16 +19,17 @@ import {
|
|
|
20
19
|
forwardRef,
|
|
21
20
|
PropsWithChildren,
|
|
22
21
|
useContext,
|
|
23
|
-
|
|
22
|
+
useLayoutEffect,
|
|
23
|
+
useRef,
|
|
24
24
|
useState,
|
|
25
25
|
} from "react";
|
|
26
26
|
|
|
27
27
|
import { tableSlotRecipe } from "../theme/slot-recipes/table";
|
|
28
28
|
import {
|
|
29
|
+
applyDomSort,
|
|
30
|
+
captureRowOrder,
|
|
29
31
|
getColumnIndex,
|
|
30
32
|
getNextSortState,
|
|
31
|
-
getSortKey,
|
|
32
|
-
sortRows,
|
|
33
33
|
type SortState,
|
|
34
34
|
} from "./sort-utils";
|
|
35
35
|
|
|
@@ -38,10 +38,10 @@ type TableVariantProps = RecipeVariantProps<typeof tableSlotRecipe>;
|
|
|
38
38
|
const SortContext = createContext<{
|
|
39
39
|
enabled: boolean;
|
|
40
40
|
sortState: SortState;
|
|
41
|
-
onSort: (
|
|
41
|
+
onSort: (columnIndex: number) => void;
|
|
42
42
|
}>({
|
|
43
43
|
enabled: false,
|
|
44
|
-
sortState: {
|
|
44
|
+
sortState: { direction: "asc", columnIndex: null },
|
|
45
45
|
onSort: () => {},
|
|
46
46
|
});
|
|
47
47
|
|
|
@@ -83,14 +83,13 @@ export const Table = forwardRef<HTMLTableElement, TableProps>(
|
|
|
83
83
|
ref,
|
|
84
84
|
) => {
|
|
85
85
|
const [sortState, setSortState] = useState<SortState>({
|
|
86
|
-
key: null,
|
|
87
86
|
direction: "asc",
|
|
88
87
|
columnIndex: null,
|
|
89
88
|
});
|
|
90
89
|
|
|
91
|
-
const handleSort = (
|
|
90
|
+
const handleSort = (columnIndex: number) => {
|
|
92
91
|
if (!sortable) return;
|
|
93
|
-
setSortState(getNextSortState(sortState,
|
|
92
|
+
setSortState(getNextSortState(sortState, columnIndex));
|
|
94
93
|
};
|
|
95
94
|
|
|
96
95
|
const recipe = useSlotRecipe({ key: "table" });
|
|
@@ -123,22 +122,29 @@ export const TableColumnHeader = forwardRef<
|
|
|
123
122
|
TableColumnHeaderProps
|
|
124
123
|
>(({ children, ...rest }, ref) => {
|
|
125
124
|
const { enabled, sortState, onSort } = useTableSort();
|
|
126
|
-
const
|
|
125
|
+
const [columnIndex, setColumnIndex] = useState<number | null>(null);
|
|
127
126
|
const props = rest as Record<string, unknown>;
|
|
128
|
-
const columnSortable = enabled &&
|
|
129
|
-
const isActive =
|
|
127
|
+
const columnSortable = enabled && !("data-nosort" in props);
|
|
128
|
+
const isActive =
|
|
129
|
+
columnSortable &&
|
|
130
|
+
columnIndex != null &&
|
|
131
|
+
columnIndex === sortState.columnIndex;
|
|
130
132
|
|
|
131
133
|
return (
|
|
132
|
-
<ChakraTable.ColumnHeader
|
|
134
|
+
<ChakraTable.ColumnHeader
|
|
135
|
+
ref={(element: HTMLTableCellElement) => {
|
|
136
|
+
if (element) setColumnIndex(getColumnIndex(element));
|
|
137
|
+
if (typeof ref === "function") ref(element);
|
|
138
|
+
else if (ref) ref.current = element;
|
|
139
|
+
}}
|
|
140
|
+
{...rest}
|
|
141
|
+
>
|
|
133
142
|
<HStack>
|
|
134
143
|
{children}
|
|
135
|
-
{columnSortable && (
|
|
144
|
+
{columnSortable && columnIndex != null && (
|
|
136
145
|
<Button
|
|
137
146
|
variant="ghost"
|
|
138
|
-
onClick={(
|
|
139
|
-
const th = event.currentTarget.closest("th");
|
|
140
|
-
if (th) onSort(key, getColumnIndex(th));
|
|
141
|
-
}}
|
|
147
|
+
onClick={() => onSort(columnIndex)}
|
|
142
148
|
p="0px !important"
|
|
143
149
|
size="xs"
|
|
144
150
|
>
|
|
@@ -162,30 +168,40 @@ export const TableColumnHeader = forwardRef<
|
|
|
162
168
|
});
|
|
163
169
|
TableColumnHeader.displayName = "ColumnHeader";
|
|
164
170
|
|
|
165
|
-
export type TableRowProps = ChakraTableRowProps;
|
|
166
|
-
|
|
167
|
-
export const TableRow = forwardRef<HTMLTableRowElement, TableRowProps>(
|
|
168
|
-
(props, ref) => <ChakraTable.Row ref={ref} {...props} />,
|
|
169
|
-
);
|
|
170
|
-
TableRow.displayName = "TableRow";
|
|
171
|
-
|
|
172
171
|
export type TableBodyProps = ChakraTableBodyProps;
|
|
173
172
|
|
|
174
173
|
export const TableBody = forwardRef<HTMLTableSectionElement, TableBodyProps>(
|
|
175
174
|
({ children, ...rest }, ref) => {
|
|
176
175
|
const { sortState } = useTableSort();
|
|
176
|
+
const tbodyRef = useRef<HTMLTableSectionElement | null>(null);
|
|
177
|
+
const originalOrder = useRef<HTMLTableRowElement[]>([]);
|
|
178
|
+
const previousChildren = useRef(children);
|
|
177
179
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
useLayoutEffect(() => {
|
|
181
|
+
const tbody = tbodyRef.current;
|
|
182
|
+
if (!tbody) return;
|
|
183
|
+
|
|
184
|
+
if (
|
|
185
|
+
previousChildren.current !== children ||
|
|
186
|
+
originalOrder.current.length === 0
|
|
187
|
+
) {
|
|
188
|
+
originalOrder.current = captureRowOrder(tbody);
|
|
189
|
+
previousChildren.current = children;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
applyDomSort(tbody, sortState, originalOrder.current);
|
|
193
|
+
}, [sortState, children]);
|
|
185
194
|
|
|
186
195
|
return (
|
|
187
|
-
<ChakraTable.Body
|
|
188
|
-
{
|
|
196
|
+
<ChakraTable.Body
|
|
197
|
+
ref={(element: HTMLTableSectionElement) => {
|
|
198
|
+
tbodyRef.current = element;
|
|
199
|
+
if (typeof ref === "function") ref(element);
|
|
200
|
+
else if (ref) ref.current = element;
|
|
201
|
+
}}
|
|
202
|
+
{...rest}
|
|
203
|
+
>
|
|
204
|
+
{children}
|
|
189
205
|
</ChakraTable.Body>
|
|
190
206
|
);
|
|
191
207
|
},
|
package/src/table/index.tsx
CHANGED
|
@@ -6,6 +6,7 @@ export type {
|
|
|
6
6
|
TableFooterProps,
|
|
7
7
|
TableHeaderProps,
|
|
8
8
|
TableRootProps,
|
|
9
|
+
TableRowProps,
|
|
9
10
|
} from "@chakra-ui/react";
|
|
10
11
|
export {
|
|
11
12
|
TableCaption,
|
|
@@ -15,4 +16,5 @@ export {
|
|
|
15
16
|
TableFooter,
|
|
16
17
|
TableHeader,
|
|
17
18
|
TableRoot,
|
|
19
|
+
TableRow,
|
|
18
20
|
} from "@chakra-ui/react";
|
package/src/table/sort-utils.ts
CHANGED
|
@@ -1,51 +1,48 @@
|
|
|
1
|
-
import { Children, isValidElement, type ReactNode } from "react";
|
|
2
|
-
|
|
3
1
|
export type SortDirection = "asc" | "desc";
|
|
4
2
|
export type SortState = {
|
|
5
|
-
key: string | null;
|
|
6
3
|
direction: SortDirection;
|
|
7
4
|
columnIndex: number | null;
|
|
8
5
|
};
|
|
9
6
|
|
|
10
7
|
export const getNextSortState = (
|
|
11
8
|
current: SortState,
|
|
12
|
-
key: string,
|
|
13
9
|
columnIndex: number,
|
|
14
10
|
): SortState => {
|
|
15
|
-
if (current.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return {
|
|
11
|
+
if (current.columnIndex !== columnIndex)
|
|
12
|
+
return { columnIndex, direction: "asc" };
|
|
13
|
+
if (current.direction === "asc") return { columnIndex, direction: "desc" };
|
|
14
|
+
return { direction: "asc", columnIndex: null };
|
|
19
15
|
};
|
|
20
16
|
|
|
21
|
-
export const getSortKey = (children: ReactNode) =>
|
|
22
|
-
typeof children === "string" ? children.trim() : null;
|
|
23
|
-
|
|
24
17
|
export const getColumnIndex = (element: HTMLElement) =>
|
|
25
18
|
Array.prototype.indexOf.call(element.parentElement?.children, element);
|
|
26
19
|
|
|
27
|
-
const
|
|
28
|
-
const cell =
|
|
29
|
-
|
|
30
|
-
)
|
|
31
|
-
if (!isValidElement(cell)) return "";
|
|
32
|
-
const props = cell.props as Record<string, unknown>;
|
|
33
|
-
return (
|
|
34
|
-
(typeof props["data-sort"] === "string" && props["data-sort"]) ||
|
|
35
|
-
(typeof props.children === "string" && props.children.trim()) ||
|
|
36
|
-
""
|
|
37
|
-
);
|
|
20
|
+
const getCellSortText = (row: HTMLTableRowElement, columnIndex: number) => {
|
|
21
|
+
const cell = row.cells[columnIndex];
|
|
22
|
+
if (!cell) return "";
|
|
23
|
+
return cell.dataset.sort || cell.textContent?.trim() || "";
|
|
38
24
|
};
|
|
39
25
|
|
|
40
|
-
export const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
) =>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
|
|
26
|
+
export const applyDomSort = (
|
|
27
|
+
tbody: HTMLTableSectionElement,
|
|
28
|
+
sortState: SortState,
|
|
29
|
+
originalRows: HTMLTableRowElement[],
|
|
30
|
+
) => {
|
|
31
|
+
if (sortState.columnIndex == null) {
|
|
32
|
+
for (const row of originalRows) tbody.append(row);
|
|
33
|
+
} else {
|
|
34
|
+
// eslint-disable-next-line unicorn/prefer-spread -- HTMLCollectionOf is not spreadable
|
|
35
|
+
const rows = Array.from(tbody.rows);
|
|
36
|
+
rows.sort((a, b) => {
|
|
37
|
+
const cmp = getCellSortText(a, sortState.columnIndex!).localeCompare(
|
|
38
|
+
getCellSortText(b, sortState.columnIndex!),
|
|
39
|
+
);
|
|
40
|
+
return sortState.direction === "asc" ? cmp : -cmp;
|
|
41
|
+
});
|
|
42
|
+
for (const row of rows) tbody.append(row);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const captureRowOrder = (tbody: HTMLTableSectionElement) =>
|
|
47
|
+
// eslint-disable-next-line unicorn/prefer-spread -- HTMLCollectionOf is not spreadable
|
|
48
|
+
Array.from(tbody.rows);
|
|
@@ -6,7 +6,7 @@ export const attachedInputsRecipe = defineRecipe({
|
|
|
6
6
|
gap: "0.1rem",
|
|
7
7
|
width: "100%",
|
|
8
8
|
"& select": {
|
|
9
|
-
borderEndRadius: 0,
|
|
9
|
+
borderEndRadius: "0 !important",
|
|
10
10
|
},
|
|
11
11
|
|
|
12
12
|
"& > *": {
|
|
@@ -22,13 +22,13 @@ export const attachedInputsRecipe = defineRecipe({
|
|
|
22
22
|
horizontal: {
|
|
23
23
|
flexDirection: "row",
|
|
24
24
|
"& > *:first-of-type:not(:last-of-type) [data-attachable]": {
|
|
25
|
-
borderEndRadius: 0,
|
|
25
|
+
borderEndRadius: "0 !important",
|
|
26
26
|
},
|
|
27
27
|
"& > *:not(:first-of-type):not(:last-of-type) [data-attachable]": {
|
|
28
|
-
borderRadius: 0,
|
|
28
|
+
borderRadius: "0 !important",
|
|
29
29
|
},
|
|
30
30
|
"& > *:not(:first-of-type):last-of-type [data-attachable]": {
|
|
31
|
-
borderStartRadius: 0,
|
|
31
|
+
borderStartRadius: "0 !important",
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
"&[data-with-flip-button]": {
|
|
@@ -45,13 +45,13 @@ export const attachedInputsRecipe = defineRecipe({
|
|
|
45
45
|
vertical: {
|
|
46
46
|
flexDirection: "column",
|
|
47
47
|
"& > *:first-of-type:not(:last-of-type) [data-attachable]": {
|
|
48
|
-
borderBottomRadius: 0,
|
|
48
|
+
borderBottomRadius: "0 !important",
|
|
49
49
|
},
|
|
50
50
|
"& > *:not(:first-of-type):not(:last-of-type) [data-attachable]": {
|
|
51
|
-
borderRadius: 0,
|
|
51
|
+
borderRadius: "0 !important",
|
|
52
52
|
},
|
|
53
53
|
"& > *:not(:first-of-type):last-of-type [data-attachable]": {
|
|
54
|
-
borderTopRadius: 0,
|
|
54
|
+
borderTopRadius: "0 !important",
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
57
|
},
|
|
@@ -11,6 +11,11 @@ export const nativeSelectSlotRecipe = defineSlotRecipe({
|
|
|
11
11
|
width: "100%",
|
|
12
12
|
height: "fit-content",
|
|
13
13
|
position: "relative",
|
|
14
|
+
backgroundColor: "surface",
|
|
15
|
+
"& option, & optgroup": {
|
|
16
|
+
color: "text",
|
|
17
|
+
backgroundColor: "surface",
|
|
18
|
+
},
|
|
14
19
|
},
|
|
15
20
|
field: {
|
|
16
21
|
...inputRecipe.base,
|
package/src/util/externals.tsx
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
/** This file works as a proxy for all Chakra UI exports */
|
|
2
|
-
export type {
|
|
2
|
+
export type {
|
|
3
|
+
PortalProps,
|
|
4
|
+
SystemConfig,
|
|
5
|
+
UseDisclosureProps,
|
|
6
|
+
} from "@chakra-ui/react";
|
|
3
7
|
export {
|
|
4
8
|
AspectRatio,
|
|
5
9
|
ClientOnly,
|
|
6
10
|
createIcon,
|
|
7
11
|
createListCollection,
|
|
8
12
|
defineRecipe,
|
|
13
|
+
defineSlotRecipe,
|
|
9
14
|
defineStyle,
|
|
10
15
|
For,
|
|
11
16
|
FormatByte,
|
|
12
17
|
FormatNumber,
|
|
13
18
|
Icon,
|
|
14
19
|
LocaleProvider,
|
|
20
|
+
mergeConfigs,
|
|
15
21
|
Portal,
|
|
16
22
|
Show,
|
|
17
23
|
chakra as spor,
|