@manuscripts/style-guide 3.5.5 → 3.5.6
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/dist/cjs/components/Button.js +9 -11
- package/dist/cjs/components/Checkbox.js +3 -1
- package/dist/cjs/components/Drawer.js +13 -10
- package/dist/cjs/components/Inspector.js +5 -0
- package/dist/cjs/components/List.js +195 -0
- package/dist/cjs/components/MultiValueInput.js +6 -1
- package/dist/cjs/components/SelectedItemsBox.js +5 -0
- package/dist/cjs/hooks/use-focus-cycle.js +53 -0
- package/dist/cjs/index.js +2 -0
- package/dist/es/components/Button.js +8 -10
- package/dist/es/components/Checkbox.js +3 -1
- package/dist/es/components/Drawer.js +13 -10
- package/dist/es/components/Inspector.js +5 -0
- package/dist/es/components/List.js +187 -0
- package/dist/es/components/MultiValueInput.js +6 -1
- package/dist/es/components/SelectedItemsBox.js +5 -0
- package/dist/es/hooks/use-focus-cycle.js +47 -0
- package/dist/es/index.js +2 -0
- package/dist/types/components/Button.d.ts +1 -0
- package/dist/types/components/Drawer.d.ts +20 -4
- package/dist/types/components/List.d.ts +4 -0
- package/dist/types/hooks/use-focus-cycle.d.ts +4 -0
- package/dist/types/index.d.ts +2 -0
- package/package.json +1 -1
|
@@ -48,7 +48,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
48
48
|
};
|
|
49
49
|
})();
|
|
50
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
-
exports.IconButtonGroup = exports.ButtonGroup = exports.SecondaryIconButton = exports.RoundIconButton = exports.IconButton = exports.ToggleButtonAlt = exports.ToggleButton = exports.IconTextButton = exports.TertiaryButton = exports.LoginButton = exports.PrimaryButton = exports.SecondaryButton = void 0;
|
|
51
|
+
exports.IconButtonGroup = exports.ButtonGroup = exports.SecondaryIconButton = exports.RoundIconButton = exports.IconButton = exports.ToggleButtonAlt = exports.ToggleButton = exports.IconTextButton = exports.TertiaryButton = exports.LoginButton = exports.PrimaryButton = exports.SecondaryButton = exports.outlineStyle = void 0;
|
|
52
52
|
const styled_components_1 = __importStar(require("styled-components"));
|
|
53
53
|
const dangerBtnStyles = (0, styled_components_1.css) `
|
|
54
54
|
${(props) => btnColors(props.theme.colors.button.error.color.default, props.theme.colors.button.error.background.default, props.theme.colors.button.error.border.default, true)}
|
|
@@ -70,6 +70,12 @@ const miniBtnStyles = (0, styled_components_1.css) `
|
|
|
70
70
|
margin: 0 ${(props) => props.theme.grid.unit}px;
|
|
71
71
|
line-height: 1;
|
|
72
72
|
`;
|
|
73
|
+
exports.outlineStyle = (0, styled_components_1.css) `
|
|
74
|
+
&:not([disabled]):focus-visible {
|
|
75
|
+
outline: 3px solid ${(props) => props.theme.colors.outline.focus};
|
|
76
|
+
outline-offset: 4px;
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
73
79
|
const btnStyles = (0, styled_components_1.css) `
|
|
74
80
|
align-items: center;
|
|
75
81
|
background: transparent;
|
|
@@ -104,6 +110,8 @@ const btnStyles = (0, styled_components_1.css) `
|
|
|
104
110
|
:disabled {
|
|
105
111
|
${disabledBtnStyles}
|
|
106
112
|
}
|
|
113
|
+
|
|
114
|
+
${exports.outlineStyle}
|
|
107
115
|
`;
|
|
108
116
|
const btnColors = (color, bg, border, important) => `
|
|
109
117
|
${color && 'color: ' + color}
|
|
@@ -128,11 +136,6 @@ exports.SecondaryButton = (0, styled_components_1.default)(ButtonTemplate) `
|
|
|
128
136
|
&:not([disabled]):active {
|
|
129
137
|
${(props) => btnColors(props.theme.colors.button.secondary.color.active, props.theme.colors.button.secondary.background.active, props.theme.colors.button.secondary.border.active, false)}
|
|
130
138
|
}
|
|
131
|
-
|
|
132
|
-
&:focus-visible {
|
|
133
|
-
outline: 3px solid ${(props) => props.theme.colors.outline.focus};
|
|
134
|
-
outline-offset: 4px;
|
|
135
|
-
}
|
|
136
139
|
`;
|
|
137
140
|
exports.PrimaryButton = (0, styled_components_1.default)(ButtonTemplate) `
|
|
138
141
|
${(props) => btnColors(props.theme.colors.button.primary.color.default, props.theme.colors.button.primary.background.default, props.theme.colors.button.primary.border.default, false)}
|
|
@@ -141,11 +144,6 @@ exports.PrimaryButton = (0, styled_components_1.default)(ButtonTemplate) `
|
|
|
141
144
|
${(props) => btnColors(props.theme.colors.button.primary.color.hover, props.theme.colors.button.primary.background.hover, props.theme.colors.button.primary.border.hover, false)}
|
|
142
145
|
}
|
|
143
146
|
|
|
144
|
-
&:focus-visible {
|
|
145
|
-
outline: 3px solid ${(props) => props.theme.colors.outline.focus} !important;
|
|
146
|
-
outline-offset: 4px;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
147
|
&:not([disabled]):active {
|
|
150
148
|
${(props) => btnColors(props.theme.colors.button.primary.color.active, props.theme.colors.button.primary.background.active, props.theme.colors.button.primary.border.active, false)}
|
|
151
149
|
}
|
|
@@ -58,6 +58,7 @@ exports.CheckboxLabel = styled_components_1.default.label `
|
|
|
58
58
|
border: 1.5px solid ${(props) => props.theme.colors.border.field.default};
|
|
59
59
|
border-radius: 3px;
|
|
60
60
|
margin-right: 10px;
|
|
61
|
+
margin-left: 4px;
|
|
61
62
|
text-align: center;
|
|
62
63
|
background: ${(props) => props.theme.colors.background.primary};
|
|
63
64
|
}
|
|
@@ -74,7 +75,8 @@ exports.CheckboxLabel = styled_components_1.default.label `
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
input:focus-visible + div::before {
|
|
77
|
-
outline:
|
|
78
|
+
outline: 2px solid ${(props) => props.theme.colors.outline.focus};
|
|
79
|
+
outline-offset: 2px;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
input:disabled + div::before {
|
|
@@ -36,6 +36,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.Drawer = exports.DrawerItemMeta = exports.DrawerItemLabel = exports.DrawerLabelContainer = exports.DrawerIcon = exports.DrawerListItem = exports.DrawerItemsList = void 0;
|
|
37
37
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
38
38
|
const styled_components_1 = __importStar(require("styled-components"));
|
|
39
|
+
const Button_1 = require("./Button");
|
|
40
|
+
const List_1 = require("./List");
|
|
39
41
|
const slideIn = (0, styled_components_1.keyframes) `
|
|
40
42
|
from {
|
|
41
43
|
transform: translateX(100%);
|
|
@@ -44,7 +46,7 @@ const slideIn = (0, styled_components_1.keyframes) `
|
|
|
44
46
|
transform: translateX(0);
|
|
45
47
|
}
|
|
46
48
|
`;
|
|
47
|
-
const DrawerContainer = styled_components_1.default.div `
|
|
49
|
+
const DrawerContainer = (0, List_1.withFocusTrap)(styled_components_1.default.div `
|
|
48
50
|
width: ${(props) => props.width || '300px'};
|
|
49
51
|
background: ${(props) => props.theme.colors.background.primary};
|
|
50
52
|
height: 100%;
|
|
@@ -55,9 +57,10 @@ const DrawerContainer = styled_components_1.default.div `
|
|
|
55
57
|
right: 0;
|
|
56
58
|
top: 0;
|
|
57
59
|
z-index: 1;
|
|
58
|
-
|
|
59
|
-
const DrawerBackButton = styled_components_1.default.
|
|
60
|
-
|
|
60
|
+
`);
|
|
61
|
+
const DrawerBackButton = (0, styled_components_1.default)(Button_1.SecondaryButton) `
|
|
62
|
+
margin: 0 12px;
|
|
63
|
+
padding: 8px 4px;
|
|
61
64
|
border: none;
|
|
62
65
|
background: none;
|
|
63
66
|
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
@@ -66,7 +69,7 @@ const DrawerBackButton = styled_components_1.default.button `
|
|
|
66
69
|
color: ${(props) => props.theme.colors.brand.default};
|
|
67
70
|
cursor: pointer;
|
|
68
71
|
display: flex;
|
|
69
|
-
|
|
72
|
+
justify-content: start;
|
|
70
73
|
gap: 4px;
|
|
71
74
|
|
|
72
75
|
&:hover {
|
|
@@ -74,13 +77,13 @@ const DrawerBackButton = styled_components_1.default.button `
|
|
|
74
77
|
opacity: 0.8;
|
|
75
78
|
}
|
|
76
79
|
`;
|
|
77
|
-
exports.DrawerItemsList = styled_components_1.default.ul `
|
|
80
|
+
exports.DrawerItemsList = (0, List_1.withListNavigation)(styled_components_1.default.ul `
|
|
78
81
|
list-style: none;
|
|
79
|
-
padding:
|
|
82
|
+
padding: 8px;
|
|
80
83
|
margin: 0;
|
|
81
84
|
overflow-y: auto;
|
|
82
|
-
|
|
83
|
-
exports.DrawerListItem = styled_components_1.default.li `
|
|
85
|
+
`);
|
|
86
|
+
exports.DrawerListItem = (0, List_1.withNavigableListItem)(styled_components_1.default.li `
|
|
84
87
|
padding: 8px 16px;
|
|
85
88
|
cursor: pointer;
|
|
86
89
|
display: flex;
|
|
@@ -95,7 +98,7 @@ exports.DrawerListItem = styled_components_1.default.li `
|
|
|
95
98
|
&:last-child {
|
|
96
99
|
border-bottom: none;
|
|
97
100
|
}
|
|
98
|
-
|
|
101
|
+
`);
|
|
99
102
|
exports.DrawerIcon = styled_components_1.default.span `
|
|
100
103
|
width: 20px;
|
|
101
104
|
height: 20px;
|
|
@@ -79,6 +79,11 @@ exports.InspectorTab = (0, styled_components_1.default)(react_1.Tab) `
|
|
|
79
79
|
border-color: ${(props) => props.theme.colors.brand.default};
|
|
80
80
|
color: ${(props) => props.theme.colors.brand.default};
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
&[data-headlessui-state~='selected'][data-headlessui-state~='focus'] {
|
|
84
|
+
outline: 2px solid ${(props) => props.theme.colors.outline.focus};
|
|
85
|
+
outline-offset: -2px;
|
|
86
|
+
}
|
|
82
87
|
}
|
|
83
88
|
`;
|
|
84
89
|
exports.InspectorTabPanelHeading = styled_components_1.default.div `
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.withListNavigation = withListNavigation;
|
|
7
|
+
exports.withNavigableListItem = withNavigableListItem;
|
|
8
|
+
exports.withFocusTrap = withFocusTrap;
|
|
9
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
10
|
+
const react_1 = require("react");
|
|
11
|
+
const styled_components_1 = __importDefault(require("styled-components"));
|
|
12
|
+
const Button_1 = require("./Button");
|
|
13
|
+
const use_focus_cycle_1 = require("../hooks/use-focus-cycle");
|
|
14
|
+
function mergeRefs(...refs) {
|
|
15
|
+
return (value) => {
|
|
16
|
+
refs.forEach((ref) => {
|
|
17
|
+
if (typeof ref === 'function') {
|
|
18
|
+
ref(value);
|
|
19
|
+
}
|
|
20
|
+
else if (ref != null) {
|
|
21
|
+
;
|
|
22
|
+
ref.current = value;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function handleTabWithinListItem(e, items) {
|
|
28
|
+
if (e.key !== 'Tab') {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const activeElement = document.activeElement;
|
|
32
|
+
const currentItem = activeElement.closest('[data-list-item]');
|
|
33
|
+
const isOnInternalElement = currentItem &&
|
|
34
|
+
currentItem !== activeElement &&
|
|
35
|
+
currentItem.contains(activeElement);
|
|
36
|
+
if (currentItem && isOnInternalElement) {
|
|
37
|
+
const focusableElements = (0, use_focus_cycle_1.getFocusableElements)(currentItem);
|
|
38
|
+
const currentIndex = focusableElements.indexOf(activeElement);
|
|
39
|
+
const isFirstElement = currentIndex === 0;
|
|
40
|
+
const isLastElement = currentIndex === focusableElements.length - 1;
|
|
41
|
+
if (e.shiftKey && isFirstElement) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
currentItem.focus();
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
else if (!e.shiftKey && isLastElement) {
|
|
47
|
+
const currentIndex = items.indexOf(currentItem);
|
|
48
|
+
const nextItem = items[currentIndex + 1];
|
|
49
|
+
if (nextItem) {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
nextItem.focus();
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
function withListNavigation(Component) {
|
|
59
|
+
return (0, styled_components_1.default)((0, react_1.forwardRef)((props, forwardedRef) => {
|
|
60
|
+
const containerRef = (0, react_1.useRef)(null);
|
|
61
|
+
const tappingOutIndex = (0, react_1.useRef)(0);
|
|
62
|
+
const getListItems = (0, react_1.useCallback)(() => {
|
|
63
|
+
const element = containerRef.current;
|
|
64
|
+
if (!element) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
return Array.from(element.querySelectorAll('[data-list-item]'));
|
|
68
|
+
}, []);
|
|
69
|
+
const getColumnCount = (0, react_1.useCallback)(() => {
|
|
70
|
+
const element = containerRef.current;
|
|
71
|
+
if (!element) {
|
|
72
|
+
return 1;
|
|
73
|
+
}
|
|
74
|
+
const gridStyle = window.getComputedStyle(element);
|
|
75
|
+
return gridStyle
|
|
76
|
+
.getPropertyValue('grid-template-columns')
|
|
77
|
+
.split(' ')
|
|
78
|
+
.filter((val) => val !== '').length;
|
|
79
|
+
}, []);
|
|
80
|
+
const handleKeyDown = (0, react_1.useCallback)((e) => {
|
|
81
|
+
const items = getListItems();
|
|
82
|
+
const handled = handleTabWithinListItem(e, items);
|
|
83
|
+
if (handled) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
87
|
+
const element = containerRef.current;
|
|
88
|
+
const columnCount = getColumnCount();
|
|
89
|
+
if (currentIndex === -1 || !element) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
let newIndex = currentIndex;
|
|
93
|
+
switch (e.key) {
|
|
94
|
+
case 'ArrowUp': {
|
|
95
|
+
e.preventDefault();
|
|
96
|
+
newIndex =
|
|
97
|
+
(currentIndex - columnCount + items.length) % items.length;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case 'ArrowDown': {
|
|
101
|
+
e.preventDefault();
|
|
102
|
+
newIndex = (currentIndex + columnCount) % items.length;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
case 'ArrowLeft': {
|
|
106
|
+
if (columnCount > 1) {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
newIndex = (currentIndex - 1 + items.length) % items.length;
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'ArrowRight': {
|
|
113
|
+
if (columnCount > 1) {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
newIndex = (currentIndex + 1) % items.length;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
case 'Tab': {
|
|
120
|
+
tappingOutIndex.current = currentIndex;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (currentIndex !== newIndex) {
|
|
125
|
+
items[currentIndex].setAttribute('tabindex', '-1');
|
|
126
|
+
items[newIndex].setAttribute('tabindex', '0');
|
|
127
|
+
items[newIndex].focus();
|
|
128
|
+
}
|
|
129
|
+
}, [getListItems, getColumnCount, containerRef]);
|
|
130
|
+
const handelOnBlur = (0, react_1.useCallback)(() => {
|
|
131
|
+
if (tappingOutIndex.current) {
|
|
132
|
+
const items = getListItems();
|
|
133
|
+
items[tappingOutIndex.current].setAttribute('tabindex', '-1');
|
|
134
|
+
items[0].setAttribute('tabindex', '0');
|
|
135
|
+
tappingOutIndex.current = 0;
|
|
136
|
+
}
|
|
137
|
+
}, [getListItems]);
|
|
138
|
+
(0, react_1.useLayoutEffect)(() => {
|
|
139
|
+
const element = containerRef.current;
|
|
140
|
+
if (!element) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
const firstItem = element.querySelector('[data-list-item]');
|
|
144
|
+
if (!firstItem) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (firstItem.tabIndex === 0) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const tabbedElement = element.querySelector('[data-list-item][tabindex="0"]');
|
|
151
|
+
if (tabbedElement) {
|
|
152
|
+
tabbedElement.tabIndex = -1;
|
|
153
|
+
}
|
|
154
|
+
firstItem.tabIndex = 0;
|
|
155
|
+
});
|
|
156
|
+
return ((0, jsx_runtime_1.jsx)(Component, { ref: mergeRefs(containerRef, forwardedRef), ...props, onKeyDown: handleKeyDown, onBlur: handelOnBlur }));
|
|
157
|
+
})) `
|
|
158
|
+
outline: none;
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
function withNavigableListItem(Component) {
|
|
162
|
+
return (0, styled_components_1.default)((0, react_1.forwardRef)((props, forwardedRef) => {
|
|
163
|
+
const listItemRef = (0, react_1.useRef)(null);
|
|
164
|
+
const handleKeyDown = (0, react_1.useCallback)((e) => {
|
|
165
|
+
const listItem = listItemRef.current;
|
|
166
|
+
const element = e.target;
|
|
167
|
+
if (element && listItem && element === listItem && e.key === 'Enter') {
|
|
168
|
+
const interactiveElements = [
|
|
169
|
+
'BUTTON',
|
|
170
|
+
'A',
|
|
171
|
+
'INPUT',
|
|
172
|
+
'TEXTAREA',
|
|
173
|
+
'SELECT',
|
|
174
|
+
];
|
|
175
|
+
if (!interactiveElements.includes(listItem.tagName)) {
|
|
176
|
+
e.preventDefault();
|
|
177
|
+
listItem.click();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}, []);
|
|
181
|
+
return ((0, jsx_runtime_1.jsx)(Component, { tabIndex: -1, "data-list-item": true, ref: mergeRefs(listItemRef, forwardedRef), onKeyDown: handleKeyDown, ...props }));
|
|
182
|
+
})) `
|
|
183
|
+
&:not([disabled]):focus-visible {
|
|
184
|
+
background-color: ${(props) => props.theme.colors.background.fifth};
|
|
185
|
+
}
|
|
186
|
+
${Button_1.outlineStyle}
|
|
187
|
+
`;
|
|
188
|
+
}
|
|
189
|
+
function withFocusTrap(Component) {
|
|
190
|
+
return (0, react_1.forwardRef)((props, forwardedRef) => {
|
|
191
|
+
const containerRef = (0, react_1.useRef)(null);
|
|
192
|
+
const handleKeyDown = (0, react_1.useCallback)((e) => (0, use_focus_cycle_1.trapFocus)(e, containerRef.current), []);
|
|
193
|
+
return ((0, jsx_runtime_1.jsx)(Component, { ref: mergeRefs(containerRef, forwardedRef), onKeyDown: handleKeyDown, ...props }));
|
|
194
|
+
});
|
|
195
|
+
}
|
|
@@ -59,6 +59,11 @@ const RemoveButton = styled_components_1.default.button `
|
|
|
59
59
|
&:hover {
|
|
60
60
|
background: black;
|
|
61
61
|
}
|
|
62
|
+
|
|
63
|
+
&:not([disabled]):focus-visible {
|
|
64
|
+
outline: 1px solid ${(props) => props.theme.colors.outline.focus};
|
|
65
|
+
outline-offset: 2px;
|
|
66
|
+
}
|
|
62
67
|
`;
|
|
63
68
|
const Input = styled_components_1.default.input `
|
|
64
69
|
${TextField_1.commonStyles}
|
|
@@ -121,6 +126,6 @@ const MultiValueInput = ({ id, inputType, placeholder = '', initialValues = [],
|
|
|
121
126
|
: inputType === 'number'
|
|
122
127
|
? 'Enter number and press enter'
|
|
123
128
|
: 'Enter text and press enter';
|
|
124
|
-
return ((0, jsx_runtime_1.jsxs)(Container, { children: [values.map((value, index) => ((0, jsx_runtime_1.jsxs)(Chip, { children: [value, (0, jsx_runtime_1.jsx)(RemoveButton, {
|
|
129
|
+
return ((0, jsx_runtime_1.jsxs)(Container, { children: [values.map((value, index) => ((0, jsx_runtime_1.jsxs)(Chip, { children: [value, (0, jsx_runtime_1.jsx)(RemoveButton, { type: 'button', onClick: () => handleRemoveValue(index), children: "\u00D7" })] }, index))), (0, jsx_runtime_1.jsx)(Input, { id: id, type: inputType, value: currentValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onBlur: handleBlur, placeholder: xplaceholder })] }));
|
|
125
130
|
};
|
|
126
131
|
exports.MultiValueInput = MultiValueInput;
|
|
@@ -58,6 +58,11 @@ const RemoveButton = styled_components_1.default.button `
|
|
|
58
58
|
padding: 0;
|
|
59
59
|
font-family: ${(props) => props.theme.font.family.sans};
|
|
60
60
|
font-weight: ${(props) => props.theme.font.weight.light};
|
|
61
|
+
|
|
62
|
+
&:not([disabled]):focus-visible {
|
|
63
|
+
outline: 1px solid ${(props) => props.theme.colors.outline.focus};
|
|
64
|
+
outline-offset: 2px;
|
|
65
|
+
}
|
|
61
66
|
`;
|
|
62
67
|
const SelectedItemsBox = ({ items, onRemove, placeholder = 'No items selected', ...props }) => {
|
|
63
68
|
return ((0, jsx_runtime_1.jsx)(BoxContainer, { ...props, children: items.length > 0 ? ((0, jsx_runtime_1.jsx)(ItemsList, { children: items.map((item) => ((0, jsx_runtime_1.jsxs)(Item, { "data-cy": "item", children: [item.label, (0, jsx_runtime_1.jsx)(RemoveButton, { onClick: () => onRemove(item.id), "aria-label": `Remove ${item.label}`, children: "x" })] }, item.id))) })) : ((0, jsx_runtime_1.jsx)(EmptyContainer, { children: (0, jsx_runtime_1.jsx)(Placeholder, { children: placeholder }) })) }));
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFocusCycle = void 0;
|
|
4
|
+
exports.getFocusableElements = getFocusableElements;
|
|
5
|
+
exports.trapFocus = trapFocus;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
function getFocusableElements(element) {
|
|
8
|
+
const selectors = [
|
|
9
|
+
'button:not([disabled])',
|
|
10
|
+
'input:not([disabled])',
|
|
11
|
+
'textarea:not([disabled])',
|
|
12
|
+
'select:not([disabled])',
|
|
13
|
+
'a[href]',
|
|
14
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
15
|
+
];
|
|
16
|
+
return Array.from(element.querySelectorAll(selectors.join(','))).filter((el) => {
|
|
17
|
+
const style = window.getComputedStyle(el);
|
|
18
|
+
return (el instanceof HTMLElement &&
|
|
19
|
+
el.offsetParent &&
|
|
20
|
+
style.visibility !== 'hidden' &&
|
|
21
|
+
style.display !== 'none');
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function trapFocus(e, element) {
|
|
25
|
+
if (e.key !== 'Tab' ||
|
|
26
|
+
!element ||
|
|
27
|
+
!element?.contains(document.activeElement)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const focusableElements = getFocusableElements(element);
|
|
31
|
+
if (focusableElements.length === 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const firstElement = focusableElements[0];
|
|
35
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
36
|
+
const activeElement = document.activeElement;
|
|
37
|
+
if (!e.shiftKey && activeElement === lastElement) {
|
|
38
|
+
e.preventDefault();
|
|
39
|
+
firstElement.focus();
|
|
40
|
+
}
|
|
41
|
+
else if (e.shiftKey && activeElement === firstElement) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
lastElement.focus();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const useFocusCycle = (containerRef) => {
|
|
47
|
+
(0, react_1.useEffect)(() => {
|
|
48
|
+
const handleKeyDown = (e) => trapFocus(e, containerRef.current);
|
|
49
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
50
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
51
|
+
}, [containerRef]);
|
|
52
|
+
};
|
|
53
|
+
exports.useFocusCycle = useFocusCycle;
|
package/dist/cjs/index.js
CHANGED
|
@@ -87,6 +87,7 @@ __exportStar(require("./components/Badge"), exports);
|
|
|
87
87
|
__exportStar(require("./components/NavDropdown"), exports);
|
|
88
88
|
__exportStar(require("./components/Dropdown"), exports);
|
|
89
89
|
__exportStar(require("./components/LoadingOverlay"), exports);
|
|
90
|
+
__exportStar(require("./components/List"), exports);
|
|
90
91
|
__exportStar(require("./components/Text"), exports);
|
|
91
92
|
__exportStar(require("./components/RelativeDate"), exports);
|
|
92
93
|
__exportStar(require("./components/Menus"), exports);
|
|
@@ -97,5 +98,6 @@ __exportStar(require("./components/SelectedItemsBox"), exports);
|
|
|
97
98
|
__exportStar(require("./hooks/use-dropdown"), exports);
|
|
98
99
|
__exportStar(require("./hooks/use-menus"), exports);
|
|
99
100
|
__exportStar(require("./hooks/use-scroll-detection"), exports);
|
|
101
|
+
__exportStar(require("./hooks/use-focus-cycle"), exports);
|
|
100
102
|
__exportStar(require("./lib/files"), exports);
|
|
101
103
|
__exportStar(require("./lib/menus"), exports);
|
|
@@ -34,6 +34,12 @@ const miniBtnStyles = css `
|
|
|
34
34
|
margin: 0 ${(props) => props.theme.grid.unit}px;
|
|
35
35
|
line-height: 1;
|
|
36
36
|
`;
|
|
37
|
+
export const outlineStyle = css `
|
|
38
|
+
&:not([disabled]):focus-visible {
|
|
39
|
+
outline: 3px solid ${(props) => props.theme.colors.outline.focus};
|
|
40
|
+
outline-offset: 4px;
|
|
41
|
+
}
|
|
42
|
+
`;
|
|
37
43
|
const btnStyles = css `
|
|
38
44
|
align-items: center;
|
|
39
45
|
background: transparent;
|
|
@@ -68,6 +74,8 @@ const btnStyles = css `
|
|
|
68
74
|
:disabled {
|
|
69
75
|
${disabledBtnStyles}
|
|
70
76
|
}
|
|
77
|
+
|
|
78
|
+
${outlineStyle}
|
|
71
79
|
`;
|
|
72
80
|
const btnColors = (color, bg, border, important) => `
|
|
73
81
|
${color && 'color: ' + color}
|
|
@@ -92,11 +100,6 @@ export const SecondaryButton = styled(ButtonTemplate) `
|
|
|
92
100
|
&:not([disabled]):active {
|
|
93
101
|
${(props) => btnColors(props.theme.colors.button.secondary.color.active, props.theme.colors.button.secondary.background.active, props.theme.colors.button.secondary.border.active, false)}
|
|
94
102
|
}
|
|
95
|
-
|
|
96
|
-
&:focus-visible {
|
|
97
|
-
outline: 3px solid ${(props) => props.theme.colors.outline.focus};
|
|
98
|
-
outline-offset: 4px;
|
|
99
|
-
}
|
|
100
103
|
`;
|
|
101
104
|
export const PrimaryButton = styled(ButtonTemplate) `
|
|
102
105
|
${(props) => btnColors(props.theme.colors.button.primary.color.default, props.theme.colors.button.primary.background.default, props.theme.colors.button.primary.border.default, false)}
|
|
@@ -105,11 +108,6 @@ export const PrimaryButton = styled(ButtonTemplate) `
|
|
|
105
108
|
${(props) => btnColors(props.theme.colors.button.primary.color.hover, props.theme.colors.button.primary.background.hover, props.theme.colors.button.primary.border.hover, false)}
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
&:focus-visible {
|
|
109
|
-
outline: 3px solid ${(props) => props.theme.colors.outline.focus} !important;
|
|
110
|
-
outline-offset: 4px;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
111
|
&:not([disabled]):active {
|
|
114
112
|
${(props) => btnColors(props.theme.colors.button.primary.color.active, props.theme.colors.button.primary.background.active, props.theme.colors.button.primary.border.active, false)}
|
|
115
113
|
}
|
|
@@ -52,6 +52,7 @@ export const CheckboxLabel = styled.label `
|
|
|
52
52
|
border: 1.5px solid ${(props) => props.theme.colors.border.field.default};
|
|
53
53
|
border-radius: 3px;
|
|
54
54
|
margin-right: 10px;
|
|
55
|
+
margin-left: 4px;
|
|
55
56
|
text-align: center;
|
|
56
57
|
background: ${(props) => props.theme.colors.background.primary};
|
|
57
58
|
}
|
|
@@ -68,7 +69,8 @@ export const CheckboxLabel = styled.label `
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
input:focus-visible + div::before {
|
|
71
|
-
outline:
|
|
72
|
+
outline: 2px solid ${(props) => props.theme.colors.outline.focus};
|
|
73
|
+
outline-offset: 2px;
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
input:disabled + div::before {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import styled, { keyframes } from 'styled-components';
|
|
3
|
+
import { SecondaryButton } from './Button';
|
|
4
|
+
import { withFocusTrap, withListNavigation, withNavigableListItem, } from './List';
|
|
3
5
|
const slideIn = keyframes `
|
|
4
6
|
from {
|
|
5
7
|
transform: translateX(100%);
|
|
@@ -8,7 +10,7 @@ const slideIn = keyframes `
|
|
|
8
10
|
transform: translateX(0);
|
|
9
11
|
}
|
|
10
12
|
`;
|
|
11
|
-
const DrawerContainer = styled.div `
|
|
13
|
+
const DrawerContainer = withFocusTrap(styled.div `
|
|
12
14
|
width: ${(props) => props.width || '300px'};
|
|
13
15
|
background: ${(props) => props.theme.colors.background.primary};
|
|
14
16
|
height: 100%;
|
|
@@ -19,9 +21,10 @@ const DrawerContainer = styled.div `
|
|
|
19
21
|
right: 0;
|
|
20
22
|
top: 0;
|
|
21
23
|
z-index: 1;
|
|
22
|
-
|
|
23
|
-
const DrawerBackButton = styled
|
|
24
|
-
|
|
24
|
+
`);
|
|
25
|
+
const DrawerBackButton = styled(SecondaryButton) `
|
|
26
|
+
margin: 0 12px;
|
|
27
|
+
padding: 8px 4px;
|
|
25
28
|
border: none;
|
|
26
29
|
background: none;
|
|
27
30
|
font-weight: ${(props) => props.theme.font.weight.normal};
|
|
@@ -30,7 +33,7 @@ const DrawerBackButton = styled.button `
|
|
|
30
33
|
color: ${(props) => props.theme.colors.brand.default};
|
|
31
34
|
cursor: pointer;
|
|
32
35
|
display: flex;
|
|
33
|
-
|
|
36
|
+
justify-content: start;
|
|
34
37
|
gap: 4px;
|
|
35
38
|
|
|
36
39
|
&:hover {
|
|
@@ -38,13 +41,13 @@ const DrawerBackButton = styled.button `
|
|
|
38
41
|
opacity: 0.8;
|
|
39
42
|
}
|
|
40
43
|
`;
|
|
41
|
-
export const DrawerItemsList = styled.ul `
|
|
44
|
+
export const DrawerItemsList = withListNavigation(styled.ul `
|
|
42
45
|
list-style: none;
|
|
43
|
-
padding:
|
|
46
|
+
padding: 8px;
|
|
44
47
|
margin: 0;
|
|
45
48
|
overflow-y: auto;
|
|
46
|
-
|
|
47
|
-
export const DrawerListItem = styled.li `
|
|
49
|
+
`);
|
|
50
|
+
export const DrawerListItem = withNavigableListItem(styled.li `
|
|
48
51
|
padding: 8px 16px;
|
|
49
52
|
cursor: pointer;
|
|
50
53
|
display: flex;
|
|
@@ -59,7 +62,7 @@ export const DrawerListItem = styled.li `
|
|
|
59
62
|
&:last-child {
|
|
60
63
|
border-bottom: none;
|
|
61
64
|
}
|
|
62
|
-
|
|
65
|
+
`);
|
|
63
66
|
export const DrawerIcon = styled.span `
|
|
64
67
|
width: 20px;
|
|
65
68
|
height: 20px;
|
|
@@ -73,6 +73,11 @@ export const InspectorTab = styled(Tab) `
|
|
|
73
73
|
border-color: ${(props) => props.theme.colors.brand.default};
|
|
74
74
|
color: ${(props) => props.theme.colors.brand.default};
|
|
75
75
|
}
|
|
76
|
+
|
|
77
|
+
&[data-headlessui-state~='selected'][data-headlessui-state~='focus'] {
|
|
78
|
+
outline: 2px solid ${(props) => props.theme.colors.outline.focus};
|
|
79
|
+
outline-offset: -2px;
|
|
80
|
+
}
|
|
76
81
|
}
|
|
77
82
|
`;
|
|
78
83
|
export const InspectorTabPanelHeading = styled.div `
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useCallback, useLayoutEffect, useRef, } from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { outlineStyle } from './Button';
|
|
5
|
+
import { getFocusableElements, trapFocus } from '../hooks/use-focus-cycle';
|
|
6
|
+
function mergeRefs(...refs) {
|
|
7
|
+
return (value) => {
|
|
8
|
+
refs.forEach((ref) => {
|
|
9
|
+
if (typeof ref === 'function') {
|
|
10
|
+
ref(value);
|
|
11
|
+
}
|
|
12
|
+
else if (ref != null) {
|
|
13
|
+
;
|
|
14
|
+
ref.current = value;
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function handleTabWithinListItem(e, items) {
|
|
20
|
+
if (e.key !== 'Tab') {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
const activeElement = document.activeElement;
|
|
24
|
+
const currentItem = activeElement.closest('[data-list-item]');
|
|
25
|
+
const isOnInternalElement = currentItem &&
|
|
26
|
+
currentItem !== activeElement &&
|
|
27
|
+
currentItem.contains(activeElement);
|
|
28
|
+
if (currentItem && isOnInternalElement) {
|
|
29
|
+
const focusableElements = getFocusableElements(currentItem);
|
|
30
|
+
const currentIndex = focusableElements.indexOf(activeElement);
|
|
31
|
+
const isFirstElement = currentIndex === 0;
|
|
32
|
+
const isLastElement = currentIndex === focusableElements.length - 1;
|
|
33
|
+
if (e.shiftKey && isFirstElement) {
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
currentItem.focus();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
else if (!e.shiftKey && isLastElement) {
|
|
39
|
+
const currentIndex = items.indexOf(currentItem);
|
|
40
|
+
const nextItem = items[currentIndex + 1];
|
|
41
|
+
if (nextItem) {
|
|
42
|
+
e.preventDefault();
|
|
43
|
+
nextItem.focus();
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
export function withListNavigation(Component) {
|
|
51
|
+
return styled(forwardRef((props, forwardedRef) => {
|
|
52
|
+
const containerRef = useRef(null);
|
|
53
|
+
const tappingOutIndex = useRef(0);
|
|
54
|
+
const getListItems = useCallback(() => {
|
|
55
|
+
const element = containerRef.current;
|
|
56
|
+
if (!element) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return Array.from(element.querySelectorAll('[data-list-item]'));
|
|
60
|
+
}, []);
|
|
61
|
+
const getColumnCount = useCallback(() => {
|
|
62
|
+
const element = containerRef.current;
|
|
63
|
+
if (!element) {
|
|
64
|
+
return 1;
|
|
65
|
+
}
|
|
66
|
+
const gridStyle = window.getComputedStyle(element);
|
|
67
|
+
return gridStyle
|
|
68
|
+
.getPropertyValue('grid-template-columns')
|
|
69
|
+
.split(' ')
|
|
70
|
+
.filter((val) => val !== '').length;
|
|
71
|
+
}, []);
|
|
72
|
+
const handleKeyDown = useCallback((e) => {
|
|
73
|
+
const items = getListItems();
|
|
74
|
+
const handled = handleTabWithinListItem(e, items);
|
|
75
|
+
if (handled) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const currentIndex = items.indexOf(document.activeElement);
|
|
79
|
+
const element = containerRef.current;
|
|
80
|
+
const columnCount = getColumnCount();
|
|
81
|
+
if (currentIndex === -1 || !element) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
let newIndex = currentIndex;
|
|
85
|
+
switch (e.key) {
|
|
86
|
+
case 'ArrowUp': {
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
newIndex =
|
|
89
|
+
(currentIndex - columnCount + items.length) % items.length;
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
case 'ArrowDown': {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
newIndex = (currentIndex + columnCount) % items.length;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'ArrowLeft': {
|
|
98
|
+
if (columnCount > 1) {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
newIndex = (currentIndex - 1 + items.length) % items.length;
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'ArrowRight': {
|
|
105
|
+
if (columnCount > 1) {
|
|
106
|
+
e.preventDefault();
|
|
107
|
+
newIndex = (currentIndex + 1) % items.length;
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
case 'Tab': {
|
|
112
|
+
tappingOutIndex.current = currentIndex;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (currentIndex !== newIndex) {
|
|
117
|
+
items[currentIndex].setAttribute('tabindex', '-1');
|
|
118
|
+
items[newIndex].setAttribute('tabindex', '0');
|
|
119
|
+
items[newIndex].focus();
|
|
120
|
+
}
|
|
121
|
+
}, [getListItems, getColumnCount, containerRef]);
|
|
122
|
+
const handelOnBlur = useCallback(() => {
|
|
123
|
+
if (tappingOutIndex.current) {
|
|
124
|
+
const items = getListItems();
|
|
125
|
+
items[tappingOutIndex.current].setAttribute('tabindex', '-1');
|
|
126
|
+
items[0].setAttribute('tabindex', '0');
|
|
127
|
+
tappingOutIndex.current = 0;
|
|
128
|
+
}
|
|
129
|
+
}, [getListItems]);
|
|
130
|
+
useLayoutEffect(() => {
|
|
131
|
+
const element = containerRef.current;
|
|
132
|
+
if (!element) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const firstItem = element.querySelector('[data-list-item]');
|
|
136
|
+
if (!firstItem) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (firstItem.tabIndex === 0) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const tabbedElement = element.querySelector('[data-list-item][tabindex="0"]');
|
|
143
|
+
if (tabbedElement) {
|
|
144
|
+
tabbedElement.tabIndex = -1;
|
|
145
|
+
}
|
|
146
|
+
firstItem.tabIndex = 0;
|
|
147
|
+
});
|
|
148
|
+
return (_jsx(Component, { ref: mergeRefs(containerRef, forwardedRef), ...props, onKeyDown: handleKeyDown, onBlur: handelOnBlur }));
|
|
149
|
+
})) `
|
|
150
|
+
outline: none;
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
export function withNavigableListItem(Component) {
|
|
154
|
+
return styled(forwardRef((props, forwardedRef) => {
|
|
155
|
+
const listItemRef = useRef(null);
|
|
156
|
+
const handleKeyDown = useCallback((e) => {
|
|
157
|
+
const listItem = listItemRef.current;
|
|
158
|
+
const element = e.target;
|
|
159
|
+
if (element && listItem && element === listItem && e.key === 'Enter') {
|
|
160
|
+
const interactiveElements = [
|
|
161
|
+
'BUTTON',
|
|
162
|
+
'A',
|
|
163
|
+
'INPUT',
|
|
164
|
+
'TEXTAREA',
|
|
165
|
+
'SELECT',
|
|
166
|
+
];
|
|
167
|
+
if (!interactiveElements.includes(listItem.tagName)) {
|
|
168
|
+
e.preventDefault();
|
|
169
|
+
listItem.click();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}, []);
|
|
173
|
+
return (_jsx(Component, { tabIndex: -1, "data-list-item": true, ref: mergeRefs(listItemRef, forwardedRef), onKeyDown: handleKeyDown, ...props }));
|
|
174
|
+
})) `
|
|
175
|
+
&:not([disabled]):focus-visible {
|
|
176
|
+
background-color: ${(props) => props.theme.colors.background.fifth};
|
|
177
|
+
}
|
|
178
|
+
${outlineStyle}
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
export function withFocusTrap(Component) {
|
|
182
|
+
return forwardRef((props, forwardedRef) => {
|
|
183
|
+
const containerRef = useRef(null);
|
|
184
|
+
const handleKeyDown = useCallback((e) => trapFocus(e, containerRef.current), []);
|
|
185
|
+
return (_jsx(Component, { ref: mergeRefs(containerRef, forwardedRef), onKeyDown: handleKeyDown, ...props }));
|
|
186
|
+
});
|
|
187
|
+
}
|
|
@@ -53,6 +53,11 @@ const RemoveButton = styled.button `
|
|
|
53
53
|
&:hover {
|
|
54
54
|
background: black;
|
|
55
55
|
}
|
|
56
|
+
|
|
57
|
+
&:not([disabled]):focus-visible {
|
|
58
|
+
outline: 1px solid ${(props) => props.theme.colors.outline.focus};
|
|
59
|
+
outline-offset: 2px;
|
|
60
|
+
}
|
|
56
61
|
`;
|
|
57
62
|
const Input = styled.input `
|
|
58
63
|
${commonStyles}
|
|
@@ -115,5 +120,5 @@ export const MultiValueInput = ({ id, inputType, placeholder = '', initialValues
|
|
|
115
120
|
: inputType === 'number'
|
|
116
121
|
? 'Enter number and press enter'
|
|
117
122
|
: 'Enter text and press enter';
|
|
118
|
-
return (_jsxs(Container, { children: [values.map((value, index) => (_jsxs(Chip, { children: [value, _jsx(RemoveButton, {
|
|
123
|
+
return (_jsxs(Container, { children: [values.map((value, index) => (_jsxs(Chip, { children: [value, _jsx(RemoveButton, { type: 'button', onClick: () => handleRemoveValue(index), children: "\u00D7" })] }, index))), _jsx(Input, { id: id, type: inputType, value: currentValue, onChange: handleInputChange, onKeyDown: handleKeyDown, onBlur: handleBlur, placeholder: xplaceholder })] }));
|
|
119
124
|
};
|
|
@@ -52,6 +52,11 @@ const RemoveButton = styled.button `
|
|
|
52
52
|
padding: 0;
|
|
53
53
|
font-family: ${(props) => props.theme.font.family.sans};
|
|
54
54
|
font-weight: ${(props) => props.theme.font.weight.light};
|
|
55
|
+
|
|
56
|
+
&:not([disabled]):focus-visible {
|
|
57
|
+
outline: 1px solid ${(props) => props.theme.colors.outline.focus};
|
|
58
|
+
outline-offset: 2px;
|
|
59
|
+
}
|
|
55
60
|
`;
|
|
56
61
|
export const SelectedItemsBox = ({ items, onRemove, placeholder = 'No items selected', ...props }) => {
|
|
57
62
|
return (_jsx(BoxContainer, { ...props, children: items.length > 0 ? (_jsx(ItemsList, { children: items.map((item) => (_jsxs(Item, { "data-cy": "item", children: [item.label, _jsx(RemoveButton, { onClick: () => onRemove(item.id), "aria-label": `Remove ${item.label}`, children: "x" })] }, item.id))) })) : (_jsx(EmptyContainer, { children: _jsx(Placeholder, { children: placeholder }) })) }));
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
export function getFocusableElements(element) {
|
|
3
|
+
const selectors = [
|
|
4
|
+
'button:not([disabled])',
|
|
5
|
+
'input:not([disabled])',
|
|
6
|
+
'textarea:not([disabled])',
|
|
7
|
+
'select:not([disabled])',
|
|
8
|
+
'a[href]',
|
|
9
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
10
|
+
];
|
|
11
|
+
return Array.from(element.querySelectorAll(selectors.join(','))).filter((el) => {
|
|
12
|
+
const style = window.getComputedStyle(el);
|
|
13
|
+
return (el instanceof HTMLElement &&
|
|
14
|
+
el.offsetParent &&
|
|
15
|
+
style.visibility !== 'hidden' &&
|
|
16
|
+
style.display !== 'none');
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export function trapFocus(e, element) {
|
|
20
|
+
if (e.key !== 'Tab' ||
|
|
21
|
+
!element ||
|
|
22
|
+
!element?.contains(document.activeElement)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const focusableElements = getFocusableElements(element);
|
|
26
|
+
if (focusableElements.length === 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const firstElement = focusableElements[0];
|
|
30
|
+
const lastElement = focusableElements[focusableElements.length - 1];
|
|
31
|
+
const activeElement = document.activeElement;
|
|
32
|
+
if (!e.shiftKey && activeElement === lastElement) {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
firstElement.focus();
|
|
35
|
+
}
|
|
36
|
+
else if (e.shiftKey && activeElement === firstElement) {
|
|
37
|
+
e.preventDefault();
|
|
38
|
+
lastElement.focus();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export const useFocusCycle = (containerRef) => {
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
const handleKeyDown = (e) => trapFocus(e, containerRef.current);
|
|
44
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
45
|
+
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
46
|
+
}, [containerRef]);
|
|
47
|
+
};
|
package/dist/es/index.js
CHANGED
|
@@ -44,6 +44,7 @@ export * from './components/Badge';
|
|
|
44
44
|
export * from './components/NavDropdown';
|
|
45
45
|
export * from './components/Dropdown';
|
|
46
46
|
export * from './components/LoadingOverlay';
|
|
47
|
+
export * from './components/List';
|
|
47
48
|
export * from './components/Text';
|
|
48
49
|
export * from './components/RelativeDate';
|
|
49
50
|
export * from './components/Menus';
|
|
@@ -54,5 +55,6 @@ export * from './components/SelectedItemsBox';
|
|
|
54
55
|
export * from './hooks/use-dropdown';
|
|
55
56
|
export * from './hooks/use-menus';
|
|
56
57
|
export * from './hooks/use-scroll-detection';
|
|
58
|
+
export * from './hooks/use-focus-cycle';
|
|
57
59
|
export * from './lib/files';
|
|
58
60
|
export * from './lib/menus';
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
export declare const outlineStyle: import("styled-components").FlattenInterpolation<import("styled-components").ThemeProps<import("styled-components").DefaultTheme>>;
|
|
16
17
|
export declare const SecondaryButton: import("styled-components").StyledComponent<"button", import("styled-components").DefaultTheme, {
|
|
17
18
|
type: "button" | "submit" | "reset";
|
|
18
19
|
} & {
|
|
@@ -5,10 +5,26 @@ export interface DrawerProps {
|
|
|
5
5
|
width?: string;
|
|
6
6
|
children: React.ReactNode;
|
|
7
7
|
}
|
|
8
|
-
export declare const DrawerItemsList: import("styled-components").StyledComponent<
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
export declare const DrawerItemsList: import("styled-components").StyledComponent<React.ForwardRefExoticComponent<Omit<{
|
|
9
|
+
[x: string]: any;
|
|
10
|
+
[x: number]: any;
|
|
11
|
+
[x: symbol]: any;
|
|
12
|
+
} & {
|
|
13
|
+
theme?: import("styled-components").DefaultTheme | undefined;
|
|
14
|
+
} & {
|
|
15
|
+
as?: string | React.ComponentType<any> | undefined;
|
|
16
|
+
forwardedAs?: string | React.ComponentType<any> | undefined;
|
|
17
|
+
}, "ref"> & React.RefAttributes<HTMLElement>>, import("styled-components").DefaultTheme, {}, never>;
|
|
18
|
+
export declare const DrawerListItem: import("styled-components").StyledComponent<React.ForwardRefExoticComponent<Omit<{
|
|
19
|
+
[x: string]: any;
|
|
20
|
+
[x: number]: any;
|
|
21
|
+
[x: symbol]: any;
|
|
22
|
+
} & {
|
|
23
|
+
theme?: import("styled-components").DefaultTheme | undefined;
|
|
24
|
+
} & {
|
|
25
|
+
as?: string | React.ComponentType<any> | undefined;
|
|
26
|
+
forwardedAs?: string | React.ComponentType<any> | undefined;
|
|
27
|
+
}, "ref"> & React.RefAttributes<HTMLElement>>, import("styled-components").DefaultTheme, {}, never>;
|
|
12
28
|
export declare const DrawerIcon: import("styled-components").StyledComponent<"span", import("styled-components").DefaultTheme, {}, never>;
|
|
13
29
|
export declare const DrawerLabelContainer: import("styled-components").StyledComponent<"div", import("styled-components").DefaultTheme, {}, never>;
|
|
14
30
|
export declare const DrawerItemLabel: import("styled-components").StyledComponent<"span", import("styled-components").DefaultTheme, {}, never>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import React, { ComponentType } from 'react';
|
|
2
|
+
export declare function withListNavigation<P extends object>(Component: ComponentType<P>): import("styled-components").StyledComponent<React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<HTMLElement>>, import("styled-components").DefaultTheme, {}, never>;
|
|
3
|
+
export declare function withNavigableListItem<P extends object>(Component: ComponentType<P>): import("styled-components").StyledComponent<React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<HTMLElement>>, import("styled-components").DefaultTheme, {}, never>;
|
|
4
|
+
export declare function withFocusTrap<P extends object>(Component: ComponentType<P>): React.ForwardRefExoticComponent<React.PropsWithoutRef<P> & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { RefObject } from 'react';
|
|
2
|
+
export declare function getFocusableElements(element: HTMLElement): HTMLElement[];
|
|
3
|
+
export declare function trapFocus(e: KeyboardEvent, element: HTMLElement | null): void;
|
|
4
|
+
export declare const useFocusCycle: (containerRef: RefObject<HTMLDivElement | null>) => void;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -45,6 +45,7 @@ export * from './components/Badge';
|
|
|
45
45
|
export * from './components/NavDropdown';
|
|
46
46
|
export * from './components/Dropdown';
|
|
47
47
|
export * from './components/LoadingOverlay';
|
|
48
|
+
export * from './components/List';
|
|
48
49
|
export * from './components/Text';
|
|
49
50
|
export * from './components/RelativeDate';
|
|
50
51
|
export * from './components/Menus';
|
|
@@ -55,5 +56,6 @@ export * from './components/SelectedItemsBox';
|
|
|
55
56
|
export * from './hooks/use-dropdown';
|
|
56
57
|
export * from './hooks/use-menus';
|
|
57
58
|
export * from './hooks/use-scroll-detection';
|
|
59
|
+
export * from './hooks/use-focus-cycle';
|
|
58
60
|
export * from './lib/files';
|
|
59
61
|
export * from './lib/menus';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manuscripts/style-guide",
|
|
3
3
|
"description": "Shared components for Manuscripts applications",
|
|
4
|
-
"version": "3.5.
|
|
4
|
+
"version": "3.5.6",
|
|
5
5
|
"repository": "github:Atypon-OpenSource/manuscripts-style-guide",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"main": "dist/cjs",
|