@canonical/react-components 1.7.2 → 1.8.0
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/components/CustomSelect/CustomSelect.d.ts +36 -0
- package/dist/components/CustomSelect/CustomSelect.js +145 -0
- package/dist/components/CustomSelect/CustomSelect.scss +82 -0
- package/dist/components/CustomSelect/CustomSelect.stories.d.ts +28 -0
- package/dist/components/CustomSelect/CustomSelect.stories.js +132 -0
- package/dist/components/CustomSelect/CustomSelect.test.d.ts +1 -0
- package/dist/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.d.ts +25 -0
- package/dist/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.js +300 -0
- package/dist/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.test.d.ts +1 -0
- package/dist/components/CustomSelect/CustomSelectDropdown/index.d.ts +2 -0
- package/dist/components/CustomSelect/CustomSelectDropdown/index.js +20 -0
- package/dist/components/CustomSelect/index.d.ts +3 -0
- package/dist/components/CustomSelect/index.js +13 -0
- package/dist/components/MultiSelect/MultiSelect.scss +1 -0
- package/dist/esm/components/CustomSelect/CustomSelect.d.ts +36 -0
- package/dist/esm/components/CustomSelect/CustomSelect.js +139 -0
- package/dist/esm/components/CustomSelect/CustomSelect.scss +82 -0
- package/dist/esm/components/CustomSelect/CustomSelect.stories.d.ts +28 -0
- package/dist/esm/components/CustomSelect/CustomSelect.stories.js +126 -0
- package/dist/esm/components/CustomSelect/CustomSelect.test.d.ts +1 -0
- package/dist/esm/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.d.ts +25 -0
- package/dist/esm/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.js +285 -0
- package/dist/esm/components/CustomSelect/CustomSelectDropdown/CustomSelectDropdown.test.d.ts +1 -0
- package/dist/esm/components/CustomSelect/CustomSelectDropdown/index.d.ts +2 -0
- package/dist/esm/components/CustomSelect/CustomSelectDropdown/index.js +1 -0
- package/dist/esm/components/CustomSelect/index.d.ts +3 -0
- package/dist/esm/components/CustomSelect/index.js +1 -0
- package/dist/esm/components/MultiSelect/MultiSelect.scss +1 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/package.json +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
2
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
3
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
4
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
5
|
+
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
6
|
+
function _objectDestructuringEmpty(t) { if (null == t) throw new TypeError("Cannot destructure " + t); }
|
|
7
|
+
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
8
|
+
import CustomSelect from "./CustomSelect";
|
|
9
|
+
import React, { useState } from "react";
|
|
10
|
+
var generateStandardOptions = num => Array(num).fill(null).map((_, i) => ({
|
|
11
|
+
value: "option-".concat(i + 1),
|
|
12
|
+
label: "Option ".concat(i + 1),
|
|
13
|
+
text: "Option ".concat(i + 1),
|
|
14
|
+
disabled: false
|
|
15
|
+
}));
|
|
16
|
+
var generateCustomOptions = () => {
|
|
17
|
+
return [{
|
|
18
|
+
value: "smile",
|
|
19
|
+
label: /*#__PURE__*/React.createElement("div", null, "\uD83D\uDE00"),
|
|
20
|
+
text: "Smile",
|
|
21
|
+
disabled: false
|
|
22
|
+
}, {
|
|
23
|
+
value: "grin",
|
|
24
|
+
label: /*#__PURE__*/React.createElement("div", null, "\uD83D\uDE01"),
|
|
25
|
+
text: "Grin",
|
|
26
|
+
disabled: false
|
|
27
|
+
}, {
|
|
28
|
+
value: "cry",
|
|
29
|
+
label: /*#__PURE__*/React.createElement("div", null, "\uD83D\uDE2D"),
|
|
30
|
+
text: "Cry",
|
|
31
|
+
disabled: false
|
|
32
|
+
}, {
|
|
33
|
+
value: "angry",
|
|
34
|
+
label: /*#__PURE__*/React.createElement("div", null, "\uD83D\uDE21"),
|
|
35
|
+
text: "Angry",
|
|
36
|
+
disabled: false
|
|
37
|
+
}, {
|
|
38
|
+
value: "sad",
|
|
39
|
+
label: /*#__PURE__*/React.createElement("div", null, "\uD83D\uDE22"),
|
|
40
|
+
text: "Sad",
|
|
41
|
+
disabled: false
|
|
42
|
+
}];
|
|
43
|
+
};
|
|
44
|
+
var Template = _ref => {
|
|
45
|
+
var props = _extends({}, (_objectDestructuringEmpty(_ref), _ref));
|
|
46
|
+
var [selected, setSelected] = useState(props.value || "");
|
|
47
|
+
return /*#__PURE__*/React.createElement(CustomSelect, _extends({}, props, {
|
|
48
|
+
value: selected,
|
|
49
|
+
onChange: value => setSelected(value)
|
|
50
|
+
}));
|
|
51
|
+
};
|
|
52
|
+
var meta = {
|
|
53
|
+
component: CustomSelect,
|
|
54
|
+
render: Template,
|
|
55
|
+
tags: ["autodocs"],
|
|
56
|
+
args: {
|
|
57
|
+
name: "customSelect",
|
|
58
|
+
label: "Custom Select",
|
|
59
|
+
searchable: "auto",
|
|
60
|
+
initialPosition: "left"
|
|
61
|
+
},
|
|
62
|
+
argTypes: {
|
|
63
|
+
searchable: {
|
|
64
|
+
options: ["auto", "always", "never"],
|
|
65
|
+
control: {
|
|
66
|
+
type: "select"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
initialPosition: {
|
|
70
|
+
options: ["left", "right"],
|
|
71
|
+
control: {
|
|
72
|
+
type: "select"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
export default meta;
|
|
78
|
+
/**
|
|
79
|
+
* If `label` is of `string` type. You do not have to do anything extra to render it.
|
|
80
|
+
*/
|
|
81
|
+
export var StandardOptions = {
|
|
82
|
+
args: {
|
|
83
|
+
options: generateStandardOptions(10)
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* If `label` is of `ReactNode` type. You can render custom content.
|
|
89
|
+
* In this case, the `text` property for each option is required and is used for display in the toggle, search and sort functionalities.
|
|
90
|
+
*/
|
|
91
|
+
export var CustomOptions = {
|
|
92
|
+
args: {
|
|
93
|
+
options: generateCustomOptions()
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* For each option, if `disabled` is set to `true`, the option will be disabled.
|
|
99
|
+
*/
|
|
100
|
+
export var DisabledOptions = {
|
|
101
|
+
args: {
|
|
102
|
+
options: generateStandardOptions(5).map((option, i) => _objectSpread(_objectSpread({}, option), {}, {
|
|
103
|
+
disabled: i % 2 === 0
|
|
104
|
+
}))
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Search is enabled by default when there are 5 or more options.
|
|
110
|
+
*/
|
|
111
|
+
export var AutoSearchable = {
|
|
112
|
+
args: {
|
|
113
|
+
options: generateStandardOptions(5),
|
|
114
|
+
searchable: "auto"
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Search can be enabled manually by setting `searchable` to `always`.
|
|
120
|
+
*/
|
|
121
|
+
export var ManualSearchable = {
|
|
122
|
+
args: {
|
|
123
|
+
options: generateStandardOptions(4),
|
|
124
|
+
searchable: "always"
|
|
125
|
+
}
|
|
126
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { FC, LiHTMLAttributes, ReactNode } from "react";
|
|
2
|
+
export type CustomSelectOption = LiHTMLAttributes<HTMLLIElement> & {
|
|
3
|
+
value: string;
|
|
4
|
+
label: ReactNode;
|
|
5
|
+
text?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type Props = {
|
|
9
|
+
searchable?: "auto" | "always" | "never";
|
|
10
|
+
name: string;
|
|
11
|
+
options: CustomSelectOption[];
|
|
12
|
+
onSelect: (value: string) => void;
|
|
13
|
+
onSearch?: (value: string) => void;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
header?: ReactNode;
|
|
16
|
+
toggleId: string;
|
|
17
|
+
};
|
|
18
|
+
export declare const adjustDropdownHeightBelow: (dropdown: HTMLUListElement) => void;
|
|
19
|
+
export declare const adjustDropdownHeightAbove: (dropdown: HTMLUListElement, search: HTMLInputElement | null) => void;
|
|
20
|
+
export declare const dropdownIsAbove: (dropdown: HTMLUListElement) => boolean;
|
|
21
|
+
export declare const adjustDropdownHeight: (dropdown: HTMLUListElement | null, search: HTMLInputElement | null) => void;
|
|
22
|
+
export declare const getNearestParentsZIndex: (element: HTMLElement | null) => string;
|
|
23
|
+
export declare const getOptionText: (option: CustomSelectOption) => string;
|
|
24
|
+
declare const CustomSelectDropdown: FC<Props>;
|
|
25
|
+
export default CustomSelectDropdown;
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import _pt from "prop-types";
|
|
2
|
+
import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import classnames from "classnames";
|
|
4
|
+
import { useListener } from "../../../hooks";
|
|
5
|
+
import SearchBox from "../../SearchBox";
|
|
6
|
+
var DROPDOWN_MAX_HEIGHT = 16 * 30; // 30rem with base 16px
|
|
7
|
+
var DROPDOWN_MARGIN = 20;
|
|
8
|
+
export var adjustDropdownHeightBelow = dropdown => {
|
|
9
|
+
var _window$visualViewpor;
|
|
10
|
+
var dropdownRect = dropdown.getBoundingClientRect();
|
|
11
|
+
var dropdownHeight = dropdown.offsetHeight;
|
|
12
|
+
var viewportHeight = ((_window$visualViewpor = window.visualViewport) === null || _window$visualViewpor === void 0 ? void 0 : _window$visualViewpor.height) || window.innerHeight;
|
|
13
|
+
|
|
14
|
+
// If the dropdown is cut off at the bottom of the viewport
|
|
15
|
+
// adjust the height to fit within the viewport minus fixed margin.
|
|
16
|
+
// This usually becomes an issue when the dropdown is at the bottom of the viewport or screen getting smaller.
|
|
17
|
+
if (dropdownRect.bottom >= viewportHeight) {
|
|
18
|
+
var _adjustedHeight = dropdownHeight - dropdownRect.bottom + viewportHeight - DROPDOWN_MARGIN;
|
|
19
|
+
dropdown.style.height = "".concat(_adjustedHeight, "px");
|
|
20
|
+
dropdown.style.maxHeight = "".concat(_adjustedHeight, "px");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// If the dropdown does not have overflow, the dropdown should fit its content.
|
|
25
|
+
var hasOverflow = dropdown.scrollHeight > dropdown.clientHeight;
|
|
26
|
+
if (!hasOverflow) {
|
|
27
|
+
dropdown.style.height = "auto";
|
|
28
|
+
dropdown.style.maxHeight = "";
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// If the dropdown is not cut off at the bottom of the viewport
|
|
33
|
+
// adjust the height of the dropdown so that its bottom edge is 20px from the bottom of the viewport
|
|
34
|
+
// until the dropdown max height is reached.
|
|
35
|
+
var adjustedHeight = Math.min(viewportHeight - dropdownRect.top - DROPDOWN_MARGIN, DROPDOWN_MAX_HEIGHT);
|
|
36
|
+
dropdown.style.height = "".concat(adjustedHeight, "px");
|
|
37
|
+
dropdown.style.maxHeight = "".concat(adjustedHeight, "px");
|
|
38
|
+
};
|
|
39
|
+
export var adjustDropdownHeightAbove = (dropdown, search) => {
|
|
40
|
+
// The search height is subtracted (if necessary) so that no options will be hidden behind the search input.
|
|
41
|
+
var searchRect = search === null || search === void 0 ? void 0 : search.getBoundingClientRect();
|
|
42
|
+
var searchHeight = (searchRect === null || searchRect === void 0 ? void 0 : searchRect.height) || 0;
|
|
43
|
+
var dropdownRect = dropdown.getBoundingClientRect();
|
|
44
|
+
|
|
45
|
+
// If the dropdown does not have overflow, do not adjust.
|
|
46
|
+
var hasOverflow = dropdown.scrollHeight > dropdown.clientHeight;
|
|
47
|
+
if (!hasOverflow) {
|
|
48
|
+
dropdown.style.height = "auto";
|
|
49
|
+
dropdown.style.maxHeight = "";
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// adjust the height of the dropdown so that its top edge is 20px from the top of the viewport.
|
|
54
|
+
// until the dropdown max height is reached.
|
|
55
|
+
// unlike the case where the dropdown is bellow the toggle, dropdown.bottom represents the available space above the toggle always.
|
|
56
|
+
// this makes the calculation simpler since we only need to work with dropdown.bottom regardless if the element is cut off or not.
|
|
57
|
+
var adjustedHeight = Math.min(dropdownRect.bottom - searchHeight - DROPDOWN_MARGIN, DROPDOWN_MAX_HEIGHT);
|
|
58
|
+
dropdown.style.height = "".concat(adjustedHeight, "px");
|
|
59
|
+
dropdown.style.maxHeight = "".concat(adjustedHeight, "px");
|
|
60
|
+
};
|
|
61
|
+
export var dropdownIsAbove = dropdown => {
|
|
62
|
+
var toggle = document.querySelector(".p-custom-select__toggle");
|
|
63
|
+
var dropdownRect = dropdown.getBoundingClientRect();
|
|
64
|
+
var toggleRect = toggle.getBoundingClientRect();
|
|
65
|
+
return toggleRect.top >= dropdownRect.bottom;
|
|
66
|
+
};
|
|
67
|
+
export var adjustDropdownHeight = (dropdown, search) => {
|
|
68
|
+
if (!dropdown) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (dropdownIsAbove(dropdown)) {
|
|
72
|
+
adjustDropdownHeightAbove(dropdown, search);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
adjustDropdownHeightBelow(dropdown);
|
|
76
|
+
};
|
|
77
|
+
export var getNearestParentsZIndex = element => {
|
|
78
|
+
if (!document.defaultView || !element) {
|
|
79
|
+
return "0";
|
|
80
|
+
}
|
|
81
|
+
var zIndex = document.defaultView.getComputedStyle(element, null).getPropertyValue("z-index");
|
|
82
|
+
if (!element.parentElement) {
|
|
83
|
+
return zIndex;
|
|
84
|
+
}
|
|
85
|
+
if (zIndex === "auto" || zIndex === "0" || zIndex === "") {
|
|
86
|
+
return getNearestParentsZIndex(element.parentElement);
|
|
87
|
+
}
|
|
88
|
+
return zIndex;
|
|
89
|
+
};
|
|
90
|
+
export var getOptionText = option => {
|
|
91
|
+
if (option.text) {
|
|
92
|
+
return option.text;
|
|
93
|
+
}
|
|
94
|
+
if (typeof option.label === "string") {
|
|
95
|
+
return option.label;
|
|
96
|
+
}
|
|
97
|
+
throw new Error("CustomSelect: options must have a string label or a text property");
|
|
98
|
+
};
|
|
99
|
+
var CustomSelectDropdown = _ref => {
|
|
100
|
+
var {
|
|
101
|
+
searchable,
|
|
102
|
+
name,
|
|
103
|
+
options,
|
|
104
|
+
onSelect,
|
|
105
|
+
onSearch,
|
|
106
|
+
onClose,
|
|
107
|
+
header,
|
|
108
|
+
toggleId
|
|
109
|
+
} = _ref;
|
|
110
|
+
var [search, setSearch] = useState("");
|
|
111
|
+
// track highlighted option index for keyboard actions
|
|
112
|
+
var [highlightedOptionIndex, setHighlightedOptionIndex] = useState(0);
|
|
113
|
+
// use ref to keep a reference to all option HTML elements so we do not need to make DOM calls later for scrolling
|
|
114
|
+
var optionsRef = useRef([]);
|
|
115
|
+
var dropdownRef = useRef(null);
|
|
116
|
+
var searchRef = useRef(null);
|
|
117
|
+
var dropdownListRef = useRef(null);
|
|
118
|
+
var isSearchable = searchable !== "never" && options.length > 1 && (searchable === "always" || searchable === "auto" && options.length >= 5);
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (dropdownRef.current) {
|
|
121
|
+
var _toggle$getBoundingCl, _toggle$getBoundingCl2;
|
|
122
|
+
var toggle = document.getElementById(toggleId);
|
|
123
|
+
|
|
124
|
+
// align width with wrapper toggle width
|
|
125
|
+
var toggleWidth = (_toggle$getBoundingCl = toggle === null || toggle === void 0 || (_toggle$getBoundingCl2 = toggle.getBoundingClientRect()) === null || _toggle$getBoundingCl2 === void 0 ? void 0 : _toggle$getBoundingCl2.width) !== null && _toggle$getBoundingCl !== void 0 ? _toggle$getBoundingCl : 0;
|
|
126
|
+
dropdownRef.current.style.setProperty("min-width", "".concat(toggleWidth, "px"));
|
|
127
|
+
|
|
128
|
+
// align z-index: when we are in a modal context, we want the dropdown to be above the modal
|
|
129
|
+
// apply the nearest parents z-index + 1
|
|
130
|
+
var zIndex = getNearestParentsZIndex(toggle);
|
|
131
|
+
if (parseInt(zIndex) > 0) {
|
|
132
|
+
var _dropdownRef$current$;
|
|
133
|
+
(_dropdownRef$current$ = dropdownRef.current.parentElement) === null || _dropdownRef$current$ === void 0 || _dropdownRef$current$.style.setProperty("z-index", zIndex + 1);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
var _dropdownRef$current;
|
|
138
|
+
if (isSearchable) {
|
|
139
|
+
var _searchRef$current;
|
|
140
|
+
(_searchRef$current = searchRef.current) === null || _searchRef$current === void 0 || _searchRef$current.focus();
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
(_dropdownRef$current = dropdownRef.current) === null || _dropdownRef$current === void 0 || _dropdownRef$current.focus();
|
|
144
|
+
}, 100);
|
|
145
|
+
}, [isSearchable, toggleId]);
|
|
146
|
+
var handleResize = () => {
|
|
147
|
+
adjustDropdownHeight(dropdownListRef.current, searchRef.current);
|
|
148
|
+
};
|
|
149
|
+
useLayoutEffect(handleResize, []);
|
|
150
|
+
useListener(window, handleResize, "resize");
|
|
151
|
+
|
|
152
|
+
// track selected index from key board action and scroll into view if needed
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
var _optionsRef$current$h;
|
|
155
|
+
(_optionsRef$current$h = optionsRef.current[highlightedOptionIndex]) === null || _optionsRef$current$h === void 0 || _optionsRef$current$h.scrollIntoView({
|
|
156
|
+
block: "nearest",
|
|
157
|
+
inline: "nearest"
|
|
158
|
+
});
|
|
159
|
+
}, [highlightedOptionIndex]);
|
|
160
|
+
var filteredOptions = onSearch ? options : options === null || options === void 0 ? void 0 : options.filter(option => {
|
|
161
|
+
if (!search || option.disabled) return true;
|
|
162
|
+
var searchText = getOptionText(option) || option.value;
|
|
163
|
+
return searchText.toLowerCase().includes(search);
|
|
164
|
+
});
|
|
165
|
+
var getNextOptionIndex = (goingUp, prevIndex) => {
|
|
166
|
+
var increment = goingUp ? -1 : 1;
|
|
167
|
+
var currIndex = prevIndex + increment;
|
|
168
|
+
// skip disabled options for key board action
|
|
169
|
+
while (filteredOptions[currIndex] && (_filteredOptions$curr = filteredOptions[currIndex]) !== null && _filteredOptions$curr !== void 0 && _filteredOptions$curr.disabled) {
|
|
170
|
+
var _filteredOptions$curr;
|
|
171
|
+
currIndex += increment;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// consider upper bound for navigating down the list
|
|
175
|
+
if (increment > 0) {
|
|
176
|
+
return currIndex < filteredOptions.length ? currIndex : prevIndex;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// consider lower bound for navigating up the list
|
|
180
|
+
return currIndex >= 0 ? currIndex : prevIndex;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// handle keyboard actions for navigating the select dropdown
|
|
184
|
+
var handleKeyDown = event => {
|
|
185
|
+
var upDownKeys = ["ArrowUp", "ArrowDown"];
|
|
186
|
+
|
|
187
|
+
// prevent default browser actions for up, down, enter and escape keys
|
|
188
|
+
// also prevent any other event listeners from being called up the DOM tree
|
|
189
|
+
if ([...upDownKeys, "Enter", "Escape", "Tab"].includes(event.key)) {
|
|
190
|
+
event.preventDefault();
|
|
191
|
+
event.nativeEvent.stopImmediatePropagation();
|
|
192
|
+
}
|
|
193
|
+
if (upDownKeys.includes(event.key)) {
|
|
194
|
+
setHighlightedOptionIndex(prevIndex => {
|
|
195
|
+
var goingUp = event.key === "ArrowUp";
|
|
196
|
+
return getNextOptionIndex(goingUp, prevIndex);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (event.key === "Enter" && filteredOptions[highlightedOptionIndex]) {
|
|
200
|
+
onSelect(filteredOptions[highlightedOptionIndex].value);
|
|
201
|
+
}
|
|
202
|
+
if (event.key === "Escape" || event.key === "Tab") {
|
|
203
|
+
onClose();
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
var handleSearch = value => {
|
|
207
|
+
setSearch(value.toLowerCase());
|
|
208
|
+
// reset selected index when search text changes
|
|
209
|
+
setHighlightedOptionIndex(0);
|
|
210
|
+
optionsRef.current = [];
|
|
211
|
+
if (onSearch) {
|
|
212
|
+
onSearch(value);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
var handleSelect = option => {
|
|
216
|
+
if (option.disabled) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
onSelect(option.value);
|
|
220
|
+
};
|
|
221
|
+
var optionItems = filteredOptions.map((option, idx) => {
|
|
222
|
+
return /*#__PURE__*/React.createElement("li", {
|
|
223
|
+
key: "".concat(option.value, "-").concat(idx),
|
|
224
|
+
onClick: () => handleSelect(option),
|
|
225
|
+
className: classnames("p-list__item", "p-custom-select__option", "u-truncate", {
|
|
226
|
+
disabled: option.disabled,
|
|
227
|
+
highlight: idx === highlightedOptionIndex && !option.disabled
|
|
228
|
+
})
|
|
229
|
+
// adding option elements to a ref array makes it easier to scroll the element later
|
|
230
|
+
// else we'd have to make a DOM call to find the element based on some identifier
|
|
231
|
+
,
|
|
232
|
+
ref: el => {
|
|
233
|
+
if (!el) return;
|
|
234
|
+
optionsRef.current[idx] = el;
|
|
235
|
+
},
|
|
236
|
+
role: "option",
|
|
237
|
+
onMouseMove: () => setHighlightedOptionIndex(idx)
|
|
238
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
239
|
+
className: classnames({
|
|
240
|
+
"u-text--muted": option.disabled
|
|
241
|
+
})
|
|
242
|
+
}, option.label));
|
|
243
|
+
});
|
|
244
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
245
|
+
className: "p-custom-select__dropdown u-no-padding",
|
|
246
|
+
role: "combobox",
|
|
247
|
+
onKeyDownCapture: handleKeyDown
|
|
248
|
+
// allow focus on the dropdown so that keyboard actions can be captured
|
|
249
|
+
,
|
|
250
|
+
tabIndex: -1,
|
|
251
|
+
ref: dropdownRef,
|
|
252
|
+
onMouseDown: e => {
|
|
253
|
+
// when custom select is used in a modal, which is a portal, a dropdown click
|
|
254
|
+
// should not close the modal itself, so we stop the event right here.
|
|
255
|
+
e.stopPropagation();
|
|
256
|
+
}
|
|
257
|
+
}, isSearchable && /*#__PURE__*/React.createElement("div", {
|
|
258
|
+
className: "p-custom-select__search u-no-padding--bottom"
|
|
259
|
+
}, /*#__PURE__*/React.createElement(SearchBox, {
|
|
260
|
+
ref: searchRef,
|
|
261
|
+
id: "select-search-".concat(name),
|
|
262
|
+
name: "select-search-".concat(name),
|
|
263
|
+
type: "text",
|
|
264
|
+
"aria-label": "Search for ".concat(name),
|
|
265
|
+
className: "u-no-margin--bottom",
|
|
266
|
+
onChange: handleSearch,
|
|
267
|
+
value: search,
|
|
268
|
+
autocomplete: "off"
|
|
269
|
+
})), header, /*#__PURE__*/React.createElement("ul", {
|
|
270
|
+
className: "p-list u-no-margin--bottom",
|
|
271
|
+
role: "listbox",
|
|
272
|
+
ref: dropdownListRef
|
|
273
|
+
}, optionItems));
|
|
274
|
+
};
|
|
275
|
+
CustomSelectDropdown.propTypes = {
|
|
276
|
+
searchable: _pt.oneOf(["auto", "always", "never"]),
|
|
277
|
+
name: _pt.string.isRequired,
|
|
278
|
+
options: _pt.array.isRequired,
|
|
279
|
+
onSelect: _pt.func.isRequired,
|
|
280
|
+
onSearch: _pt.func,
|
|
281
|
+
onClose: _pt.func.isRequired,
|
|
282
|
+
header: _pt.node,
|
|
283
|
+
toggleId: _pt.string.isRequired
|
|
284
|
+
};
|
|
285
|
+
export default CustomSelectDropdown;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, getOptionText } from "./CustomSelectDropdown";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "./CustomSelect";
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ export { default as Textarea } from "./components/Textarea";
|
|
|
67
67
|
export { default as Tooltip } from "./components/Tooltip";
|
|
68
68
|
export { default as TablePagination } from "./components/TablePagination";
|
|
69
69
|
export { default as TablePaginationControls } from "./components/TablePagination/TablePaginationControls";
|
|
70
|
+
export { default as CustomSelect } from "./components/CustomSelect";
|
|
70
71
|
export type { AccordionProps } from "./components/Accordion";
|
|
71
72
|
export type { ActionButtonProps } from "./components/ActionButton";
|
|
72
73
|
export type { ArticlePaginationProps } from "./components/ArticlePagination";
|
|
@@ -131,6 +132,7 @@ export type { TabsProps } from "./components/Tabs";
|
|
|
131
132
|
export type { TextareaProps } from "./components/Textarea";
|
|
132
133
|
export type { TooltipProps } from "./components/Tooltip";
|
|
133
134
|
export type { TablePaginationProps } from "./components/TablePagination";
|
|
135
|
+
export type { CustomSelectProps, CustomSelectDropdownProps, CustomSelectOption, } from "./components/CustomSelect";
|
|
134
136
|
export { useOnClickOutside, useClickOutside, useId, useListener, useOnEscapePressed, usePagination, usePrevious, useThrottle, useWindowFitment, } from "./hooks";
|
|
135
137
|
export type { WindowFitment } from "./hooks";
|
|
136
138
|
export { isNavigationAnchor, isNavigationButton } from "./utils";
|
package/dist/esm/index.js
CHANGED
|
@@ -67,6 +67,7 @@ export { default as Textarea } from "./components/Textarea";
|
|
|
67
67
|
export { default as Tooltip } from "./components/Tooltip";
|
|
68
68
|
export { default as TablePagination } from "./components/TablePagination";
|
|
69
69
|
export { default as TablePaginationControls } from "./components/TablePagination/TablePaginationControls";
|
|
70
|
+
export { default as CustomSelect } from "./components/CustomSelect";
|
|
70
71
|
export { useOnClickOutside, useClickOutside, useId, useListener, useOnEscapePressed, usePagination, usePrevious, useThrottle, useWindowFitment } from "./hooks";
|
|
71
72
|
export { isNavigationAnchor, isNavigationButton } from "./utils";
|
|
72
73
|
export { Theme } from "./enums";
|
package/dist/index.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ export { default as Textarea } from "./components/Textarea";
|
|
|
67
67
|
export { default as Tooltip } from "./components/Tooltip";
|
|
68
68
|
export { default as TablePagination } from "./components/TablePagination";
|
|
69
69
|
export { default as TablePaginationControls } from "./components/TablePagination/TablePaginationControls";
|
|
70
|
+
export { default as CustomSelect } from "./components/CustomSelect";
|
|
70
71
|
export type { AccordionProps } from "./components/Accordion";
|
|
71
72
|
export type { ActionButtonProps } from "./components/ActionButton";
|
|
72
73
|
export type { ArticlePaginationProps } from "./components/ArticlePagination";
|
|
@@ -131,6 +132,7 @@ export type { TabsProps } from "./components/Tabs";
|
|
|
131
132
|
export type { TextareaProps } from "./components/Textarea";
|
|
132
133
|
export type { TooltipProps } from "./components/Tooltip";
|
|
133
134
|
export type { TablePaginationProps } from "./components/TablePagination";
|
|
135
|
+
export type { CustomSelectProps, CustomSelectDropdownProps, CustomSelectOption, } from "./components/CustomSelect";
|
|
134
136
|
export { useOnClickOutside, useClickOutside, useId, useListener, useOnEscapePressed, usePagination, usePrevious, useThrottle, useWindowFitment, } from "./hooks";
|
|
135
137
|
export type { WindowFitment } from "./hooks";
|
|
136
138
|
export { isNavigationAnchor, isNavigationButton } from "./utils";
|
package/dist/index.js
CHANGED
|
@@ -84,6 +84,7 @@ var _exportNames = {
|
|
|
84
84
|
Tooltip: true,
|
|
85
85
|
TablePagination: true,
|
|
86
86
|
TablePaginationControls: true,
|
|
87
|
+
CustomSelect: true,
|
|
87
88
|
useOnClickOutside: true,
|
|
88
89
|
useClickOutside: true,
|
|
89
90
|
useId: true,
|
|
@@ -235,6 +236,12 @@ Object.defineProperty(exports, "ContextualMenu", {
|
|
|
235
236
|
return _ContextualMenu.default;
|
|
236
237
|
}
|
|
237
238
|
});
|
|
239
|
+
Object.defineProperty(exports, "CustomSelect", {
|
|
240
|
+
enumerable: true,
|
|
241
|
+
get: function () {
|
|
242
|
+
return _CustomSelect.default;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
238
245
|
Object.defineProperty(exports, "DoughnutChart", {
|
|
239
246
|
enumerable: true,
|
|
240
247
|
get: function () {
|
|
@@ -729,6 +736,7 @@ var _Textarea = _interopRequireDefault(require("./components/Textarea"));
|
|
|
729
736
|
var _Tooltip = _interopRequireDefault(require("./components/Tooltip"));
|
|
730
737
|
var _TablePagination = _interopRequireDefault(require("./components/TablePagination"));
|
|
731
738
|
var _TablePaginationControls = _interopRequireDefault(require("./components/TablePagination/TablePaginationControls"));
|
|
739
|
+
var _CustomSelect = _interopRequireDefault(require("./components/CustomSelect"));
|
|
732
740
|
var _hooks = require("./hooks");
|
|
733
741
|
var _utils = require("./utils");
|
|
734
742
|
var _enums = require("./enums");
|