@canonical/react-components 0.60.0 → 1.1.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.
@@ -153,7 +153,7 @@ const ContextualMenuDropdown = _ref => {
153
153
  contextualMenuClassName,
154
154
  ...props
155
155
  } = _ref;
156
- const dropdown = (0, _react.useRef)();
156
+ const dropdown = (0, _react.useRef)(null);
157
157
  const [verticalPosition, setVerticalPosition] = (0, _react.useState)("bottom");
158
158
  const [positionStyle, setPositionStyle] = (0, _react.useState)(getPositionStyle(adjustedPosition, verticalPosition, positionCoords, constrainPanelWidth));
159
159
  const [maxHeight, setMaxHeight] = (0, _react.useState)();
@@ -162,6 +162,7 @@ const ContextualMenuDropdown = _ref => {
162
162
  setPositionStyle(getPositionStyle(adjustedPosition, verticalPosition, positionCoords, constrainPanelWidth));
163
163
  }, [adjustedPosition, positionCoords, verticalPosition, constrainPanelWidth]);
164
164
  const updateVerticalPosition = (0, _react.useCallback)(() => {
165
+ var _dropdown$current$get;
165
166
  if (!positionNode) {
166
167
  return null;
167
168
  }
@@ -170,18 +171,18 @@ const ContextualMenuDropdown = _ref => {
170
171
  return null;
171
172
  }
172
173
  const scrollableParentRect = scrollableParent.getBoundingClientRect();
173
- const rect = positionNode.getBoundingClientRect();
174
+ const toggleRect = positionNode.getBoundingClientRect();
174
175
 
175
176
  // Calculate the rect in relation to the scrollableParent
176
- const relativeRect = {
177
- top: rect.top - scrollableParentRect.top,
178
- bottom: rect.bottom - scrollableParentRect.top,
179
- height: rect.height
177
+ const relativeToScrollParentRect = {
178
+ top: toggleRect.top - scrollableParentRect.top,
179
+ bottom: toggleRect.bottom - scrollableParentRect.top
180
180
  };
181
- const spaceBelow = scrollableParentRect.height - relativeRect.bottom;
182
- const spaceAbove = relativeRect.top;
183
- const dropdownHeight = relativeRect.height;
184
- setVerticalPosition(spaceBelow >= dropdownHeight || spaceBelow > spaceAbove ? "bottom" : "top");
181
+ const scrollParentSpaceBelow = scrollableParentRect.height - relativeToScrollParentRect.bottom;
182
+ const scrollParentSpaceAbove = relativeToScrollParentRect.top;
183
+ const dropdownHeight = (_dropdown$current$get = dropdown.current.getBoundingClientRect().height) !== null && _dropdown$current$get !== void 0 ? _dropdown$current$get : 0;
184
+ const windowSpaceBelow = window.innerHeight - toggleRect.bottom;
185
+ setVerticalPosition(scrollParentSpaceBelow >= dropdownHeight && windowSpaceBelow >= dropdownHeight || windowSpaceBelow > scrollParentSpaceAbove ? "bottom" : "top");
185
186
  }, [positionNode]);
186
187
 
187
188
  // Update the position when the window fitment info changes.
@@ -51,7 +51,7 @@ export type Props = {
51
51
  /**
52
52
  * Optional class(es) to pass to the label component.
53
53
  */
54
- labelClassName?: string;
54
+ labelClassName?: string | null;
55
55
  /**
56
56
  * Whether the label should show before the input.
57
57
  */
@@ -30,13 +30,20 @@ const LoginPageLayout = _ref => {
30
30
  title,
31
31
  logo = defaultLogo
32
32
  } = _ref;
33
- (0, _react.useEffect)(() => {
34
- document.title = title;
35
- }, [title]);
36
33
  (0, _react.useLayoutEffect)(() => {
37
34
  var _document$querySelect;
38
- (_document$querySelect = document.querySelector("body")) === null || _document$querySelect === void 0 || _document$querySelect.classList.add("is-paper");
39
- });
35
+ const bodyInitiallyContainsIsPaper = (_document$querySelect = document.querySelector("body")) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.classList.contains("is-paper");
36
+ if (!bodyInitiallyContainsIsPaper) {
37
+ var _document$querySelect2;
38
+ (_document$querySelect2 = document.querySelector("body")) === null || _document$querySelect2 === void 0 || _document$querySelect2.classList.add("is-paper");
39
+ }
40
+ return () => {
41
+ if (!bodyInitiallyContainsIsPaper) {
42
+ var _document$querySelect3;
43
+ (_document$querySelect3 = document.querySelector("body")) === null || _document$querySelect3 === void 0 || _document$querySelect3.classList.remove("is-paper");
44
+ }
45
+ };
46
+ }, []);
40
47
  return /*#__PURE__*/_react.default.createElement(_Row.default, {
41
48
  className: "p-strip page-row"
42
49
  }, /*#__PURE__*/_react.default.createElement(_Col.default, {
@@ -12,7 +12,10 @@ export type MultiSelectProps = {
12
12
  selectedItems?: MultiSelectItem[];
13
13
  help?: string;
14
14
  label?: string | null;
15
+ listSelected?: boolean;
16
+ onDeselectItem?: (item: MultiSelectItem) => void;
15
17
  onItemsUpdate?: (items: MultiSelectItem[]) => void;
18
+ onSelectItem?: (item: MultiSelectItem) => void;
16
19
  placeholder?: string;
17
20
  required?: boolean;
18
21
  items: MultiSelectItem[];
@@ -20,6 +23,7 @@ export type MultiSelectProps = {
20
23
  renderItem?: (item: MultiSelectItem) => ReactNode;
21
24
  dropdownHeader?: ReactNode;
22
25
  dropdownFooter?: ReactNode;
26
+ showDropdownFooter?: boolean;
23
27
  variant?: "condensed" | "search";
24
28
  };
25
29
  type GroupFn = (items: Parameters<typeof getGroupedItems>[0]) => ReturnType<typeof getGroupedItems>;
@@ -31,6 +35,8 @@ type MultiSelectDropdownProps = {
31
35
  disabledItems: MultiSelectItem[];
32
36
  header?: ReactNode;
33
37
  updateItems: (newItems: MultiSelectItem[]) => void;
38
+ onDeselectItem?: (item: MultiSelectItem) => void;
39
+ onSelectItem?: (item: MultiSelectItem) => void;
34
40
  footer?: ReactNode;
35
41
  groupFn?: GroupFn;
36
42
  sortFn?: SortFn;
@@ -50,6 +50,8 @@ const MultiSelectDropdown = _ref2 => {
50
50
  disabledItems,
51
51
  header,
52
52
  updateItems,
53
+ onSelectItem,
54
+ onDeselectItem,
53
55
  isOpen,
54
56
  footer,
55
57
  sortFn = sortAlphabetically,
@@ -82,6 +84,11 @@ const MultiSelectDropdown = _ref2 => {
82
84
  var _selectedItems$filter;
83
85
  const newSelectedItems = checked ? [...selectedItems, foundItem] : (_selectedItems$filter = selectedItems.filter(item => "".concat(item.value) !== value)) !== null && _selectedItems$filter !== void 0 ? _selectedItems$filter : [];
84
86
  updateItems(newSelectedItems);
87
+ if (checked) {
88
+ onSelectItem === null || onSelectItem === void 0 || onSelectItem(foundItem);
89
+ } else {
90
+ onDeselectItem === null || onDeselectItem === void 0 || onDeselectItem(foundItem);
91
+ }
85
92
  }
86
93
  };
87
94
  return /*#__PURE__*/_react.default.createElement(_FadeInDown.FadeInDown, {
@@ -144,6 +151,8 @@ MultiSelectDropdown.propTypes = {
144
151
  })).isRequired,
145
152
  header: _propTypes.default.node,
146
153
  updateItems: _propTypes.default.func.isRequired,
154
+ onDeselectItem: _propTypes.default.func,
155
+ onSelectItem: _propTypes.default.func,
147
156
  footer: _propTypes.default.node,
148
157
  groupFn: _propTypes.default.func,
149
158
  sortFn: _propTypes.default.any,
@@ -154,13 +163,17 @@ const MultiSelect = _ref4 => {
154
163
  disabled,
155
164
  selectedItems: externalSelectedItems = [],
156
165
  label,
166
+ listSelected = true,
157
167
  onItemsUpdate,
168
+ onSelectItem,
169
+ onDeselectItem,
158
170
  placeholder,
159
171
  required = false,
160
172
  items = [],
161
173
  disabledItems = [],
162
174
  dropdownHeader,
163
175
  dropdownFooter,
176
+ showDropdownFooter = true,
164
177
  variant = "search"
165
178
  } = _ref4;
166
179
  const wrapperRef = (0, _index.useClickOutside)(() => {
@@ -188,6 +201,24 @@ const MultiSelect = _ref4 => {
188
201
  const dropdownId = (0, _react.useId)();
189
202
  const inputId = (0, _react.useId)();
190
203
  const selectedItemsLabel = selectedItems.filter(selectedItem => items.some(item => item.value === selectedItem.value)).map(el => el.label).join(", ");
204
+ let footer = null;
205
+ if (showDropdownFooter) {
206
+ footer = dropdownFooter ? dropdownFooter : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_index.Button, {
207
+ appearance: "link",
208
+ onClick: () => {
209
+ const enabledItems = items.filter(item => !disabledItems.some(disabledItem => disabledItem.value === item.value));
210
+ updateItems([...selectedItems, ...enabledItems]);
211
+ },
212
+ type: "button"
213
+ }, "Select all"), /*#__PURE__*/_react.default.createElement(_index.Button, {
214
+ appearance: "link",
215
+ onClick: () => {
216
+ const disabledSelectedItems = selectedItems.filter(item => disabledItems.some(disabledItem => disabledItem.value === item.value));
217
+ updateItems(disabledSelectedItems);
218
+ },
219
+ type: "button"
220
+ }, "Clear"));
221
+ }
191
222
  return /*#__PURE__*/_react.default.createElement("div", {
192
223
  ref: wrapperRef
193
224
  }, /*#__PURE__*/_react.default.createElement("div", {
@@ -224,7 +255,7 @@ const MultiSelect = _ref4 => {
224
255
  }
225
256
  }, /*#__PURE__*/_react.default.createElement("span", {
226
257
  className: "multi-select__condensed-text"
227
- }, selectedItems.length > 0 ? selectedItemsLabel : placeholder !== null && placeholder !== void 0 ? placeholder : "Select items")), /*#__PURE__*/_react.default.createElement(MultiSelectDropdown, {
258
+ }, listSelected && selectedItems.length > 0 ? selectedItemsLabel : placeholder !== null && placeholder !== void 0 ? placeholder : "Select items")), /*#__PURE__*/_react.default.createElement(MultiSelectDropdown, {
228
259
  id: dropdownId,
229
260
  isOpen: isDropdownOpen,
230
261
  items: filter.length > 0 ? items.filter(item => item.label.toLowerCase().includes(filter.toLowerCase())) : items,
@@ -232,21 +263,9 @@ const MultiSelect = _ref4 => {
232
263
  disabledItems: disabledItems,
233
264
  header: dropdownHeader,
234
265
  updateItems: updateItems,
235
- footer: dropdownFooter ? dropdownFooter : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_index.Button, {
236
- appearance: "link",
237
- onClick: () => {
238
- const enabledItems = items.filter(item => !disabledItems.some(disabledItem => disabledItem.value === item.value));
239
- updateItems([...selectedItems, ...enabledItems]);
240
- },
241
- type: "button"
242
- }, "Select all"), /*#__PURE__*/_react.default.createElement(_index.Button, {
243
- appearance: "link",
244
- onClick: () => {
245
- const disabledSelectedItems = selectedItems.filter(item => disabledItems.some(disabledItem => disabledItem.value === item.value));
246
- updateItems(disabledSelectedItems);
247
- },
248
- type: "button"
249
- }, "Clear"))
266
+ onSelectItem: onSelectItem,
267
+ onDeselectItem: onDeselectItem,
268
+ footer: footer
250
269
  })));
251
270
  };
252
271
  exports.MultiSelect = MultiSelect;
@@ -260,7 +279,10 @@ MultiSelect.propTypes = {
260
279
  })),
261
280
  help: _propTypes.default.string,
262
281
  label: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.oneOf([null])]),
282
+ listSelected: _propTypes.default.bool,
283
+ onDeselectItem: _propTypes.default.func,
263
284
  onItemsUpdate: _propTypes.default.func,
285
+ onSelectItem: _propTypes.default.func,
264
286
  placeholder: _propTypes.default.string,
265
287
  required: _propTypes.default.bool,
266
288
  items: _propTypes.default.arrayOf(_propTypes.default.shape({
@@ -276,5 +298,6 @@ MultiSelect.propTypes = {
276
298
  renderItem: _propTypes.default.func,
277
299
  dropdownHeader: _propTypes.default.node,
278
300
  dropdownFooter: _propTypes.default.node,
301
+ showDropdownFooter: _propTypes.default.bool,
279
302
  variant: _propTypes.default.oneOf(["condensed", "search"])
280
303
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@canonical/react-components",
3
- "version": "0.60.0",
3
+ "version": "1.1.0",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "author": {