@swan-io/lake 18.1.0 → 18.1.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/package.json +1 -1
- package/src/components/Accordion.d.ts +2 -1
- package/src/components/Accordion.js +19 -4
- package/src/components/LakeLabel.js +2 -1
- package/src/components/LakeSelect.js +2 -2
- package/src/components/MultiSelect.js +2 -1
- package/src/components/PlainListView.js +4 -4
- package/src/components/Tile.js +5 -3
- package/src/components/Toggle.d.ts +2 -1
- package/src/components/Toggle.js +2 -2
- package/src/components/VirtualizedList.js +9 -7
- package/src/utils/a11y.d.ts +1 -0
- package/src/utils/a11y.js +3 -0
package/package.json
CHANGED
|
@@ -3,8 +3,9 @@ import { ViewProps } from "react-native";
|
|
|
3
3
|
type Props = {
|
|
4
4
|
children: ReactNode;
|
|
5
5
|
trigger: ReactNode;
|
|
6
|
+
triggerEnd?: ReactNode;
|
|
6
7
|
style?: ViewProps["style"];
|
|
7
8
|
contentContainerStyle?: ViewProps["style"];
|
|
8
9
|
};
|
|
9
|
-
export declare const Accordion: ({ children, trigger, style, contentContainerStyle }: Props) => import("react").JSX.Element;
|
|
10
|
+
export declare const Accordion: ({ children, trigger, triggerEnd, style, contentContainerStyle, }: Props) => import("react").JSX.Element;
|
|
10
11
|
export {};
|
|
@@ -3,17 +3,29 @@ import { useId } from "react";
|
|
|
3
3
|
import { Pressable, StyleSheet, View } from "react-native";
|
|
4
4
|
import { backgroundColor, colors, spacings } from "../constants/design";
|
|
5
5
|
import { useBoolean } from "../hooks/useBoolean";
|
|
6
|
+
import { isNotNullish } from "../utils/nullish";
|
|
6
7
|
import { Icon } from "./Icon";
|
|
7
8
|
import { LakeText } from "./LakeText";
|
|
8
9
|
import { Space } from "./Space";
|
|
9
10
|
const styles = StyleSheet.create({
|
|
11
|
+
header: {
|
|
12
|
+
flexDirection: "row",
|
|
13
|
+
alignItems: "stretch",
|
|
14
|
+
},
|
|
10
15
|
trigger: {
|
|
16
|
+
flexGrow: 1,
|
|
17
|
+
flexShrink: 1,
|
|
11
18
|
flexDirection: "row",
|
|
12
19
|
alignItems: "center",
|
|
13
20
|
backgroundColor: backgroundColor.accented,
|
|
14
21
|
paddingVertical: spacings[12],
|
|
15
22
|
paddingHorizontal: spacings[20],
|
|
16
23
|
},
|
|
24
|
+
triggerEnd: {
|
|
25
|
+
justifyContent: "center",
|
|
26
|
+
backgroundColor: backgroundColor.accented,
|
|
27
|
+
paddingRight: spacings[20],
|
|
28
|
+
},
|
|
17
29
|
arrow: {
|
|
18
30
|
transitionProperty: "transform",
|
|
19
31
|
transitionDuration: "200ms",
|
|
@@ -24,11 +36,13 @@ const styles = StyleSheet.create({
|
|
|
24
36
|
contentContainer: {
|
|
25
37
|
display: "grid",
|
|
26
38
|
gridTemplateRows: "0fr",
|
|
27
|
-
transitionProperty: "grid-template-rows",
|
|
39
|
+
transitionProperty: "grid-template-rows, visibility",
|
|
28
40
|
transitionDuration: "300ms",
|
|
41
|
+
visibility: "hidden",
|
|
29
42
|
},
|
|
30
43
|
contentContainerDisplayed: {
|
|
31
44
|
gridTemplateRows: "1fr",
|
|
45
|
+
visibility: "visible",
|
|
32
46
|
},
|
|
33
47
|
contentInner: {
|
|
34
48
|
overflow: "hidden",
|
|
@@ -38,8 +52,9 @@ const styles = StyleSheet.create({
|
|
|
38
52
|
paddingHorizontal: spacings[20],
|
|
39
53
|
},
|
|
40
54
|
});
|
|
41
|
-
export const Accordion = ({ children, trigger, style, contentContainerStyle }) => {
|
|
42
|
-
const
|
|
55
|
+
export const Accordion = ({ children, trigger, triggerEnd, style, contentContainerStyle, }) => {
|
|
56
|
+
const triggerId = useId();
|
|
57
|
+
const contentId = useId();
|
|
43
58
|
const [isOpen, { toggle }] = useBoolean(false);
|
|
44
|
-
return (_jsxs(View, { children: [_jsxs(Pressable, { "aria-controls":
|
|
59
|
+
return (_jsxs(View, { children: [_jsxs(View, { style: styles.header, children: [_jsxs(Pressable, { id: triggerId, "aria-controls": contentId, role: "button", "aria-expanded": isOpen, onPress: toggle, style: [styles.trigger, style], children: [_jsx(Icon, { name: "chevron-right-filled", size: 12, color: colors.gray[500], style: [styles.arrow, isOpen && styles.arrowOpen] }), _jsx(Space, { width: 20 }), typeof trigger === "string" ? (_jsx(LakeText, { variant: "smallMedium", color: colors.gray[900], children: trigger })) : (trigger)] }), isNotNullish(triggerEnd) && _jsx(View, { style: styles.triggerEnd, children: triggerEnd })] }), _jsx(View, { id: contentId, "aria-labelledby": triggerId, role: "region", style: [styles.contentContainer, isOpen && styles.contentContainerDisplayed], children: _jsx(View, { style: styles.contentInner, children: _jsx(View, { style: [styles.content, contentContainerStyle], children: children }) }) })] }));
|
|
45
60
|
};
|
|
@@ -4,6 +4,7 @@ import { StyleSheet, unstable_createElement, View, } from "react-native";
|
|
|
4
4
|
import { match } from "ts-pattern";
|
|
5
5
|
import { commonStyles } from "../constants/commonStyles";
|
|
6
6
|
import { colors, fonts, spacings, texts } from "../constants/design";
|
|
7
|
+
import { getLabelId } from "../utils/a11y";
|
|
7
8
|
import { isNotNullish } from "../utils/nullish";
|
|
8
9
|
import { Box } from "./Box";
|
|
9
10
|
import { LakeText } from "./LakeText";
|
|
@@ -44,7 +45,7 @@ export const LakeLabel = ({ label, optionalLabel, description, extra, readOnly =
|
|
|
44
45
|
target === null || target === void 0 ? void 0 : target.focus();
|
|
45
46
|
}, [id]);
|
|
46
47
|
const renderActions = () => isNotNullish(actions) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), actionsAlign === "content" && isFormLabel ? (_jsx(View, { style: styles.topActions, children: actions })) : (actions)] }));
|
|
47
|
-
return (_jsxs(Box, { style: [styles.container, style], direction: "row", alignItems: "center", children: [_jsxs(View, { style: commonStyles.fill, children: [_jsxs(Box, { direction: "row", justifyContent: "spaceBetween", alignItems: "center", children: [_jsxs(Box, { direction: "row", alignItems: "center", shrink: 1, children: [isFormLabel ? (_jsxs(Box, { shrink: 1, children: [_jsxs(Label, { onClick: onClick, htmlFor: id, style: [styles.label, readOnly && { color: readOnlyColor }], children: [label, optionalLabel != null && (_jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: ` - ${optionalLabel}` }))] }), description != null && (_jsxs(_Fragment, { children: [_jsx(LakeText, { variant: "smallRegular", children: description }), _jsx(Space, { height: 8 })] }))] })) : (_jsxs(LakeText, { variant: "medium", color: readOnlyColor, children: [label, optionalLabel != null && (_jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: ` - ${optionalLabel}` }))] })), isNotNullish(extra) && extra()] }), isNotNullish(help) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), help] }))] }), _jsx(Space, { height: match(type)
|
|
48
|
+
return (_jsxs(Box, { style: [styles.container, style], direction: "row", alignItems: "center", children: [_jsxs(View, { style: commonStyles.fill, children: [_jsxs(Box, { direction: "row", justifyContent: "spaceBetween", alignItems: "center", children: [_jsxs(Box, { direction: "row", alignItems: "center", shrink: 1, children: [isFormLabel ? (_jsxs(Box, { shrink: 1, children: [_jsxs(Label, { id: getLabelId(id), onClick: onClick, htmlFor: id, style: [styles.label, readOnly && { color: readOnlyColor }], children: [label, optionalLabel != null && (_jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: ` - ${optionalLabel}` }))] }), description != null && (_jsxs(_Fragment, { children: [_jsx(LakeText, { variant: "smallRegular", children: description }), _jsx(Space, { height: 8 })] }))] })) : (_jsxs(LakeText, { variant: "medium", color: readOnlyColor, children: [label, optionalLabel != null && (_jsx(LakeText, { color: colors.gray[400], style: styles.optionalLabel, children: ` - ${optionalLabel}` }))] })), isNotNullish(extra) && extra()] }), isNotNullish(help) && (_jsxs(_Fragment, { children: [_jsx(Space, { width: 16 }), help] }))] }), _jsx(Space, { height: match(type)
|
|
48
49
|
.returnType()
|
|
49
50
|
.with("formSmall", "viewSmall", () => 4)
|
|
50
51
|
.with("form", "view", () => 8)
|
|
@@ -6,7 +6,7 @@ import { commonStyles } from "../constants/commonStyles";
|
|
|
6
6
|
import { backgroundColor, colors, invariantColors, radii, shadows, spacings, texts, } from "../constants/design";
|
|
7
7
|
import { useBoolean } from "../hooks/useBoolean";
|
|
8
8
|
import { useMergeRefs } from "../hooks/useMergeRefs";
|
|
9
|
-
import { getFocusableElements } from "../utils/a11y";
|
|
9
|
+
import { getFocusableElements, getLabelId } from "../utils/a11y";
|
|
10
10
|
import { setHighlightApi } from "../utils/highlights";
|
|
11
11
|
import { isNotNullish, isNullishOrEmpty } from "../utils/nullish";
|
|
12
12
|
import { Box } from "./Box";
|
|
@@ -217,7 +217,7 @@ export const LakeSelect = ({ ref, title, items, valueStyle, size, color = "curre
|
|
|
217
217
|
}, [filter, hasSearch]);
|
|
218
218
|
const ListHeaderComponent = useMemo(() => (_jsxs(Box, { direction: "row", alignItems: "center", style: styles.filterContainer, children: [_jsx(TextInput, { autoComplete: "off", inputMode: "search", multiline: false, rows: 1, onChangeText: filterValue => setFilter(filterValue), placeholder: searchPlaceholder, value: filter, onFocus: setFilterFocused.on, onBlur: setFilterFocused.off, style: [styles.filterInput, filterFocused && styles.filterFocused] }), _jsx(Icon, { name: "search-filled", color: colors[color].primary, size: 20, style: styles.searchIcon })] })), [filter, filterFocused, setFilterFocused, searchPlaceholder, color]);
|
|
219
219
|
const ListEmptyComponent = (_jsxs(Box, { justifyContent: "center", alignItems: "center", style: styles.emptyList, children: [_jsx(Icon, { name: "clipboard-search-regular", size: 24, color: colors.gray.primary }), _jsx(Space, { height: 8 }), _jsx(Text, { style: styles.emptyListText, children: t("common.noResult") })] }));
|
|
220
|
-
return (_jsxs(View, { style: commonStyles.fill, children: [_jsx(Pressable, { id: id, ref: mergedRef, "aria-haspopup": "listbox", role: "button", "aria-expanded": visible, disabled: readOnly || disabled, style: ({ focused, hovered, pressed }) => [
|
|
220
|
+
return (_jsxs(View, { style: commonStyles.fill, children: [_jsx(Pressable, { id: id, ref: mergedRef, "aria-labelledby": id != null ? `${getLabelId(id)} ${id}` : undefined, "aria-haspopup": "listbox", role: "button", "aria-expanded": visible, disabled: readOnly || disabled, style: ({ focused, hovered, pressed }) => [
|
|
221
221
|
mode === "normal" ? styles.normal : styles.borderless,
|
|
222
222
|
size === "small" && styles.small,
|
|
223
223
|
(disabled || readOnly) && styles.inputDisabled,
|
|
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { Pressable, StyleSheet, Text, TextInput, View, } from "react-native";
|
|
5
5
|
import { backgroundColor, colors, radii, shadows, texts } from "../constants/design";
|
|
6
6
|
import { useBoolean } from "../hooks/useBoolean";
|
|
7
|
+
import { getLabelId } from "../utils/a11y";
|
|
7
8
|
import { groupBy } from "../utils/array";
|
|
8
9
|
import { isNotNullish, isNotNullishOrEmpty } from "../utils/nullish";
|
|
9
10
|
import { safeSplitAround } from "../utils/string";
|
|
@@ -196,7 +197,7 @@ export const MultiSelect = ({ color = "gray", disabled = false, emptyResultText,
|
|
|
196
197
|
}, [filteredItems]);
|
|
197
198
|
const ListHeaderComponent = useMemo(() => (_jsxs(Box, { direction: "row", alignItems: "center", style: styles.filterContainer, children: [_jsx(TextInput, { autoComplete: "off", inputMode: "search", multiline: false, rows: 1, onChangeText: filterValue => setFilter(filterValue), placeholder: filterPlaceholder, value: filter, onFocus: setFilterFocused.on, onBlur: setFilterFocused.off, style: [styles.filterInput, filterFocused && styles.filterFocused] }), _jsx(Icon, { name: "search-filled", color: colors[color].primary, size: 20, style: styles.searchIcon })] })), [filter, filterFocused, setFilterFocused, filterPlaceholder, color]);
|
|
198
199
|
const ListEmptyComponent = useMemo(() => (_jsxs(Box, { justifyContent: "center", alignItems: "center", style: styles.emptyList, children: [_jsx(Icon, { name: "clipboard-search-regular", size: 24, color: colors.gray.primary }), isNotNullishOrEmpty(emptyResultText) && (_jsxs(_Fragment, { children: [_jsx(Space, { height: 8 }), _jsx(Text, { style: styles.emptyListText, children: emptyResultText })] }))] })), [emptyResultText]);
|
|
199
|
-
return (_jsxs(View, { style: style, children: [_jsxs(Pressable, { id: id, ref: inputRef, role: "button", "aria-haspopup": "listbox", "aria-expanded": visible, disabled: disabled, onPress: open, style: ({ hovered, focused }) => [
|
|
200
|
+
return (_jsxs(View, { style: style, children: [_jsxs(Pressable, { id: id, ref: inputRef, "aria-labelledby": id != null ? `${getLabelId(id)} ${id}` : undefined, role: "button", "aria-haspopup": "listbox", "aria-expanded": visible, disabled: disabled, onPress: open, style: ({ hovered, focused }) => [
|
|
200
201
|
styles.base,
|
|
201
202
|
hovered && styles.hovered,
|
|
202
203
|
(focused || visible) && styles.focused,
|
|
@@ -166,13 +166,13 @@ export const PlainListView = ({ data: originalData, keyExtractor, rowHeight, gro
|
|
|
166
166
|
onActiveRowChange === null || onActiveRowChange === void 0 ? void 0 : onActiveRowChange(element);
|
|
167
167
|
}
|
|
168
168
|
}, [activeRowId, onActiveRowChange]);
|
|
169
|
-
const listWrapper = useMemo(() => withoutScroll ? (_jsx(View, { style: styles.contentContainer })) : (_jsx(ScrollView, { scrollEventThrottle: 32, contentContainerStyle: [
|
|
169
|
+
const listWrapper = useMemo(() => withoutScroll ? (_jsx(View, { role: "table", "aria-busy": isLoading, style: styles.contentContainer })) : (_jsx(ScrollView, { role: "table", "aria-busy": isLoading, tabIndex: isNullish(getRowLink) ? 0 : undefined, scrollEventThrottle: 32, contentContainerStyle: [
|
|
170
170
|
styles.contentContainer,
|
|
171
171
|
{
|
|
172
172
|
minHeight: totalHeight + (isLoading ? loading.count * rowHeight : 0),
|
|
173
173
|
},
|
|
174
|
-
] })), [isLoading, loading === null || loading === void 0 ? void 0 : loading.count, rowHeight, totalHeight, withoutScroll]);
|
|
175
|
-
return (_jsx(ResponsiveContainer, {
|
|
174
|
+
] })), [isLoading, loading === null || loading === void 0 ? void 0 : loading.count, rowHeight, totalHeight, withoutScroll, getRowLink]);
|
|
175
|
+
return (_jsx(ResponsiveContainer, { style: withoutScroll ? (isEmpty ? commonStyles.fill : undefined) : styles.root, breakpoint: breakpoint, children: ({ large }) => {
|
|
176
176
|
const displayColumns = large ? columns : smallColumns;
|
|
177
177
|
return (_jsx(_Fragment, { children: cloneElement(listWrapper, {
|
|
178
178
|
children: (_jsxs(_Fragment, { children: [!isEmpty && large ? (_jsx(View, { role: "row", style: [
|
|
@@ -216,7 +216,7 @@ export const PlainListView = ({ data: originalData, keyExtractor, rowHeight, gro
|
|
|
216
216
|
children: (_jsx(Row, { id: key, rowHeight: rowHeight, columns: displayColumns, item: item, index: index, extraInfo: extraInfo, isActive: isActive, isHovered: isHovered, large: large, style: rowStyle, onMouseEnter: setHoveredRow, onMouseLeave: removeHoveredRow })),
|
|
217
217
|
});
|
|
218
218
|
})] }, groupName));
|
|
219
|
-
}) }), _jsx(View, { children: _jsx(View, {
|
|
219
|
+
}) }), _jsx(View, { children: _jsx(View, { style: styles.loadingPlaceholder, children: isLoading ? (_jsx(PlainListViewPlaceholder, { count: loading.count, rowHeight: rowHeight })) : null }) }), isEmpty && isNotNullish(renderEmptyList) && !isLoading ? (_jsx(View, { style: styles.emptyListContainer, children: renderEmptyList() })) : null, _jsx(View, { style: [styles.scrollTracker, { height: onEndReachedThresholdPx }], ref: scrollTrackerRef })] })),
|
|
220
220
|
}) }));
|
|
221
221
|
} }));
|
|
222
222
|
};
|
package/src/components/Tile.js
CHANGED
|
@@ -87,11 +87,13 @@ const styles = StyleSheet.create({
|
|
|
87
87
|
collapsibleContent: {
|
|
88
88
|
display: "grid",
|
|
89
89
|
gridTemplateRows: "0fr",
|
|
90
|
-
transitionProperty: "grid-template-rows",
|
|
90
|
+
transitionProperty: "grid-template-rows, visibility",
|
|
91
91
|
transitionDuration: "300ms",
|
|
92
|
+
visibility: "hidden",
|
|
92
93
|
},
|
|
93
94
|
collapsibleContentOpen: {
|
|
94
95
|
gridTemplateRows: "1fr",
|
|
96
|
+
visibility: "visible",
|
|
95
97
|
},
|
|
96
98
|
collapsibleInner: {
|
|
97
99
|
overflow: "hidden",
|
|
@@ -103,7 +105,7 @@ export const Tile = ({ icon, title, description, headerEnd, footer, flexBasis, c
|
|
|
103
105
|
const hasHeader = isNotNullish(title) || isNotNullish(headerEnd) || isNotNullish(description) || collapsible;
|
|
104
106
|
const header = (_jsxs(_Fragment, { children: [_jsxs(View, { style: styles.header, children: [_jsxs(View, { style: styles.headerTitleIcon, children: [isNotNullish(icon) && (_jsxs(_Fragment, { children: [icon, _jsx(Space, { height: 12 })] })), isNotNullish(title) && (_jsx(Text, { style: styles.title, role: "heading", "aria-level": 3, children: title }))] }), collapsible ? (_jsx(Icon, { name: "chevron-down-filled", size: 16, color: colors.gray[500], style: [styles.chevron, expanded && styles.chevronOpen] })) : isNotNullish(headerEnd) ? (_jsx(View, { children: headerEnd })) : null] }), isNotNullish(description) && (_jsxs(_Fragment, { children: [isNotNullish(title) && _jsx(Space, { height: 12 }), _jsx(View, { style: styles.descriptionContainer, children: _jsx(LakeText, { numberOfLines: numberOfLines, style: styles.description, children: description }) })] }))] }));
|
|
105
107
|
const content = (_jsxs(_Fragment, { children: [children, isNotNullish(footer) && (_jsx(View, { style: [styles.footer, { marginTop: paddingVertical, marginBottom: -paddingVertical }], children: footer }))] }));
|
|
106
|
-
return (_jsx(View, {
|
|
108
|
+
return (_jsx(View, { style: [
|
|
107
109
|
styles.container,
|
|
108
110
|
selected === false && styles.unselectedContainer,
|
|
109
111
|
{
|
|
@@ -117,7 +119,7 @@ export const Tile = ({ icon, title, description, headerEnd, footer, flexBasis, c
|
|
|
117
119
|
selected === false && hovered && styles.unselectedHoveredContainer,
|
|
118
120
|
disabled && styles.disabled,
|
|
119
121
|
style,
|
|
120
|
-
], children: collapsible ? (_jsxs(_Fragment, { children: [hasHeader && (_jsx(Pressable, { role: "button", "aria-expanded": expanded, "aria-controls": contentId, onPress: toggle, style: styles.headerContainer, children: header })), _jsx(View, { id: contentId,
|
|
122
|
+
], children: collapsible ? (_jsxs(_Fragment, { children: [hasHeader && (_jsx(Pressable, { role: "button", "aria-expanded": expanded, "aria-controls": contentId, onPress: toggle, style: styles.headerContainer, children: header })), _jsx(View, { id: contentId, style: [styles.collapsibleContent, expanded && styles.collapsibleContentOpen], children: _jsxs(View, { style: styles.collapsibleInner, children: [isNotNullish(children) && hasHeader && _jsx(Space, { height: 24 }), content] }) })] })) : (_jsxs(_Fragment, { children: [hasHeader && (_jsxs(View, { style: styles.headerContainer, children: [header, isNotNullish(children) && _jsx(Space, { height: 24 })] })), content] })) }));
|
|
121
123
|
};
|
|
122
124
|
export const TileFullWidthContent = ({ children, flexGrow, fitToBottom = false, }) => {
|
|
123
125
|
return (_jsx(View, { style: [styles.fullWidthContent, fitToBottom && styles.fitToBottom, { flexGrow }], children: children }));
|
|
@@ -4,6 +4,7 @@ type Props = {
|
|
|
4
4
|
labelOff: string;
|
|
5
5
|
labelOn: string;
|
|
6
6
|
onToggle: (value: boolean) => void;
|
|
7
|
+
ariaLabel?: string;
|
|
7
8
|
};
|
|
8
|
-
export declare const Toggle: ({ compact, value, labelOff, labelOn, onToggle }: Props) => import("react").JSX.Element;
|
|
9
|
+
export declare const Toggle: ({ compact, value, labelOff, labelOn, onToggle, ariaLabel, }: Props) => import("react").JSX.Element;
|
|
9
10
|
export {};
|
package/src/components/Toggle.js
CHANGED
|
@@ -50,7 +50,7 @@ const getItemWidth = (node) => match(node)
|
|
|
50
50
|
.returnType()
|
|
51
51
|
.with(P.instanceOf(HTMLElement), element => element.offsetWidth)
|
|
52
52
|
.otherwise(() => 0);
|
|
53
|
-
export const Toggle = ({ compact = false, value, labelOff, labelOn, onToggle }) => {
|
|
53
|
+
export const Toggle = ({ compact = false, value, labelOff, labelOn, onToggle, ariaLabel, }) => {
|
|
54
54
|
const [itemsWidth, setItemsWidth] = useState();
|
|
55
55
|
const onViewRef = useRef(null);
|
|
56
56
|
const offViewRef = useRef(null);
|
|
@@ -67,7 +67,7 @@ export const Toggle = ({ compact = false, value, labelOff, labelOn, onToggle })
|
|
|
67
67
|
const onPress = useCallback(() => {
|
|
68
68
|
onToggle(!value);
|
|
69
69
|
}, [onToggle, value]);
|
|
70
|
-
return (_jsxs(Pressable, { role: "switch", onPress: onPress, "aria-checked": value, style: styles.base, children: [_jsx(View, { role: "presentation", style: [
|
|
70
|
+
return (_jsxs(Pressable, { role: "switch", onPress: onPress, "aria-checked": value, "aria-label": ariaLabel, style: styles.base, children: [_jsx(View, { role: "presentation", style: [
|
|
71
71
|
styles.handle,
|
|
72
72
|
itemsWidth == null
|
|
73
73
|
? styles.hidden
|
|
@@ -261,13 +261,13 @@ const ResizeHandle = ({ id, end = false, width, onResize, scrollViewRef }) => {
|
|
|
261
261
|
};
|
|
262
262
|
}
|
|
263
263
|
}, [id, end]);
|
|
264
|
-
return (_jsx(Pressable, { ref: ref,
|
|
264
|
+
return (_jsx(Pressable, { ref: ref, "aria-hidden": true, tabIndex: -1, style: [
|
|
265
265
|
styles.resizeHandleZone,
|
|
266
266
|
end ? styles.resizeHandleZoneEnd : styles.resizeHandleZoneStart,
|
|
267
267
|
], children: ({ hovered }) => (_jsx(View, { style: [styles.resizeHandle, (hovered || isDragging) && styles.resizeHandleActive] })) }));
|
|
268
268
|
};
|
|
269
269
|
export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns, stickedToEndColumns, headerHeight, rowHeight, renderThreshold = 1000, onEndReached, onEndReachedThreshold = 200, loading, extraInfo, keyExtractor, marginHorizontal, renderEmptyList, onColumnResize, getRowLink, }) => {
|
|
270
|
-
// Used for unique IDs generation (usefull for header IDs
|
|
270
|
+
// Used for unique IDs generation (usefull for header IDs)
|
|
271
271
|
const viewId = useId();
|
|
272
272
|
const scrollViewRef = useRef(null);
|
|
273
273
|
const scrollTrackerRef = useRef(null);
|
|
@@ -481,7 +481,9 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
|
|
|
481
481
|
.getOr(false)) {
|
|
482
482
|
return (_jsx(ScrollView, { style: styles.container, contentContainerStyle: styles.emptyListContentContainer, children: renderEmptyList() }));
|
|
483
483
|
}
|
|
484
|
-
return (_jsxs(ScrollView, { ref: scrollViewRef, role: "table",
|
|
484
|
+
return (_jsxs(ScrollView, { ref: scrollViewRef, role: "table", "aria-busy": Option.fromNullable(loading)
|
|
485
|
+
.map(({ isLoading }) => isLoading)
|
|
486
|
+
.getOr(false), tabIndex: getRowLink == null ? 0 : undefined, both: true, style: [styles.container, { marginHorizontal }], onScroll: onScroll, scrollEventThrottle: 32, contentContainerStyle: [
|
|
485
487
|
styles.contentContainer,
|
|
486
488
|
{
|
|
487
489
|
height: containerContainerHeight,
|
|
@@ -490,7 +492,7 @@ export const VirtualizedList = ({ variant, data, stickedToStartColumns, columns,
|
|
|
490
492
|
], children: [header, rowsToRender
|
|
491
493
|
.map(({ startIndex, endIndex, data }) => (_jsxs(View, { style: styles.rowsContainer, ref: rowsContainerRef, children: [data.map((item, index) => (_jsx(VirtualizedRow, { viewId: viewId, item: item, rowHeight: rowHeight, absoluteIndex: startIndex + index, variant: variant, stickedToStartColumnsWidth: stickedToStartColumnsWidth, centerColumnsWidth: centerColumnsWidth, stickedToEndColumnsWidth: stickedToEndColumnsWidth, stickedToStartColumns: stickedToStartColumns, columns: columns, stickedToEndColumns: stickedToEndColumns, extraInfo: extraInfo, getRowLink: getRowLink, horizontalScrollPosition: horizontalScrollPosition !== null && horizontalScrollPosition !== void 0 ? horizontalScrollPosition : "NoScroll", stickedToStartFirstCellLeftPadding: stickedToStartFirstCellLeftPadding, centerFirstCellLeftPadding: centerFirstCellLeftPadding, centerLastCellLeftPadding: centerLastCellLeftPadding, stickedToEndLastCellRightPadding: stickedToEndLastCellRightPadding }, keyExtractor(item, startIndex + index)))), Option.fromNullable(loading)
|
|
492
494
|
.flatMap(({ isLoading, count }) => (isLoading ? Option.Some(count) : Option.None()))
|
|
493
|
-
.map(count => (_jsx(View, {
|
|
495
|
+
.map(count => (_jsx(View, { style: [
|
|
494
496
|
styles.loadingPlaceholder,
|
|
495
497
|
{
|
|
496
498
|
top: endIndex * rowHeight,
|
|
@@ -541,7 +543,7 @@ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedT
|
|
|
541
543
|
], children: columns.map(({ id, width, renderCell }, index) => {
|
|
542
544
|
const columnId = `${viewId}_${id}`;
|
|
543
545
|
const paddingLeft = index === 0 ? stickedToStartFirstCellLeftPadding : 0;
|
|
544
|
-
return (_jsx(View, { style: [styles.cell, { width: width + paddingLeft, paddingLeft }],
|
|
546
|
+
return (_jsx(View, { style: [styles.cell, { width: width + paddingLeft, paddingLeft }], children: renderCell({
|
|
545
547
|
columnId,
|
|
546
548
|
item,
|
|
547
549
|
index: absoluteIndex,
|
|
@@ -567,7 +569,7 @@ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedT
|
|
|
567
569
|
styles.cell,
|
|
568
570
|
grow && styles.grow,
|
|
569
571
|
{ width: width + paddingLeft + paddingRight, paddingLeft, paddingRight },
|
|
570
|
-
],
|
|
572
|
+
], children: renderCell({
|
|
571
573
|
columnId,
|
|
572
574
|
item,
|
|
573
575
|
index: absoluteIndex,
|
|
@@ -589,7 +591,7 @@ const RawVirtualizedRow = ({ viewId, rowHeight, absoluteIndex, variant, stickedT
|
|
|
589
591
|
], children: columns.map(({ id, width, renderCell }, index) => {
|
|
590
592
|
const columnId = `${viewId}_${id}`;
|
|
591
593
|
const paddingRight = index === columns.length - 1 ? stickedToEndLastCellRightPadding : 0;
|
|
592
|
-
return (_jsx(View, { style: [styles.cell, { width: width + paddingRight, paddingRight }],
|
|
594
|
+
return (_jsx(View, { style: [styles.cell, { width: width + paddingRight, paddingRight }], children: renderCell({
|
|
593
595
|
columnId,
|
|
594
596
|
item,
|
|
595
597
|
index: absoluteIndex,
|
package/src/utils/a11y.d.ts
CHANGED
package/src/utils/a11y.js
CHANGED
|
@@ -16,3 +16,6 @@ const FOCUSABLE_SELECTOR = [
|
|
|
16
16
|
export const getFocusableElements = (rootNode = document.documentElement, includeRoot = true) => {
|
|
17
17
|
return (includeRoot && rootNode.matches(FOCUSABLE_SELECTOR) ? [rootNode] : []).concat(Array.from(rootNode.querySelectorAll(FOCUSABLE_SELECTOR)));
|
|
18
18
|
};
|
|
19
|
+
// `<label for>` doesn't apply to elements that aren't labelable (like a `div` with `role="button"`),
|
|
20
|
+
// so LakeLabel also gives its label this id for fields to reference with `aria-labelledby`
|
|
21
|
+
export const getLabelId = (fieldId) => `${fieldId}-label`;
|