orcs-design-system 3.2.10 → 3.2.12

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.
@@ -39,7 +39,7 @@ export const leftOffsetActionsMenu = () => /*#__PURE__*/_jsx(Flex, {
39
39
  justifyContent: "flex-end",
40
40
  children: /*#__PURE__*/_jsxs(ActionsMenu, {
41
41
  ariaLabel: "Options Menu",
42
- direction: "left",
42
+ direction: "left-start",
43
43
  children: [/*#__PURE__*/_jsx(ActionsMenuItem, {
44
44
  href: "https://orchestrated.io/",
45
45
  children: "Open details page"
@@ -172,7 +172,7 @@ export const advancedActionsMenu = () => {
172
172
  justifyContent: "flex-end",
173
173
  children: /*#__PURE__*/_jsx(ActionsMenu, {
174
174
  ref: ref,
175
- direction: "left",
175
+ direction: "left-start",
176
176
  className: "ignore-onclickoutside",
177
177
  customTriggerComponent: /*#__PURE__*/_jsx(Button, {
178
178
  variant: "default",
@@ -201,27 +201,17 @@ export const customActionsMenu = () => {
201
201
  const closeMenu = () => {
202
202
  setToggle(false);
203
203
  };
204
- const resetAndCloseMenu = () => {
205
- if (menuItemsRef.current) {
206
- menuItemsRef.current.reset();
207
- }
208
- closeMenu();
209
- };
210
- const menuRef = useOnclickOutside(resetAndCloseMenu, {
211
- disabled: !toggleState
212
- });
213
204
  const onToggle = e => {
214
205
  e.stopPropagation();
215
206
  setToggle(!toggleState);
216
207
  };
217
208
  return /*#__PURE__*/_jsx("div", {
218
- ref: menuRef,
219
209
  children: /*#__PURE__*/_jsx(Flex, {
220
210
  justifyContent: "flex-end",
221
211
  children: /*#__PURE__*/_jsx(ActionsMenuBody, {
222
212
  toggleState: toggleState,
223
213
  onToggle: onToggle,
224
- direction: "left",
214
+ direction: "left-start",
225
215
  customTriggerComponent: /*#__PURE__*/_jsx(Button, {
226
216
  variant: "danger",
227
217
  iconOnly: true,
@@ -269,9 +259,8 @@ export const textButtonActionsMenu = () => {
269
259
  children: /*#__PURE__*/_jsxs(ActionsMenuBody, {
270
260
  toggleState: toggleState,
271
261
  onToggle: onToggle,
272
- menuTopPosition: "30px",
273
- menuLeftPosition: "0",
274
262
  menuWidth: "120px",
263
+ direction: "bottom-start",
275
264
  customTriggerComponent: /*#__PURE__*/_jsxs(Button, {
276
265
  variant: "ghost",
277
266
  type: "button",
@@ -320,8 +309,7 @@ export const keepInViewExample = () => /*#__PURE__*/_jsxs(Flex, {
320
309
  children: "Remove"
321
310
  })]
322
311
  }), /*#__PURE__*/_jsxs(ActionsMenu, {
323
- menuTopPosition: "30px",
324
- menuLeftPosition: "0",
312
+ direction: "bottom-start",
325
313
  menuWidth: "200px",
326
314
  customTriggerComponent: /*#__PURE__*/_jsxs(Button, {
327
315
  variant: "ghost",
@@ -1,10 +1,11 @@
1
- import React, { useState, useEffect, useImperativeHandle, useCallback, createContext, useContext, useMemo, useRef, useId } from "react";
2
- import styled, { css, keyframes, ThemeProvider } from "styled-components";
1
+ import React, { useState, useImperativeHandle, createContext, useContext, useMemo, useId, useLayoutEffect } from "react";
2
+ import styled, { keyframes, ThemeProvider } from "styled-components";
3
3
  import PropTypes from "prop-types";
4
4
  import { space, layout } from "styled-system";
5
5
  import { themeGet } from "@styled-system/theme-get";
6
- import { useKeepInView } from "../../hooks/keepInView";
7
6
  import { commonKeys } from "../../hooks/keypress";
7
+ import useActionMenu from "./useActionMenu";
8
+ import { FloatingFocusManager, FloatingPortal, useMergeRefs } from "@floating-ui/react";
8
9
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
10
  const ActionMenuContext = /*#__PURE__*/createContext({});
10
11
  const crossTransform1 = keyframes(["0%{transform:translate(0,-6px);border-radius:2px;}50%{transform:translate(0,0);border-radius:2px;}75%{transform:rotate(-45deg) translate(0,0);border-radius:2px;}100%{transform:rotate(-45deg) translate(0,0) scaleX(4);border-radius:0;}"]);
@@ -16,16 +17,27 @@ const Wrapper = styled.div.withConfig({
16
17
  const Control = styled.button.withConfig({
17
18
  displayName: "ActionsMenu__Control",
18
19
  componentId: "sc-yvbni2-1"
19
- })(["position:relative;background-color:", ";border:none;display:flex;align-items:center;justify-content:center;-moz-appearance:none;-webkit-appearance:none;appearance:none;box-shadow:none;text-decoration:none;text-align:center;border-radius:", ";transition:", ";cursor:pointer;width:30px;height:30px;&:hover,&:focus{outline:0;background-color:", ";}"], props => themeGet("colors.greyLighter")(props), props => themeGet("radii.2")(props), props => themeGet("transition.transitionDefault")(props), props => themeGet("colors.greyLight")(props));
20
+ })(["position:relative;background-color:", ";border:none;display:flex;align-items:center;justify-content:center;-moz-appearance:none;-webkit-appearance:none;appearance:none;box-shadow:none;text-decoration:none;text-align:center;border-radius:", ";transition:", ";cursor:pointer;width:30px;height:30px;&:hover,&:focus{outline:0;background-color:", ";}&[data-state=\"open\"] .action-menu-icon{&:before{animation:400ms ", " ease-in-out forwards;}&:after{animation:400ms ", " ease-in-out forwards;}}"], props => themeGet("colors.greyLighter")(props), props => themeGet("radii.2")(props), props => themeGet("transition.transitionDefault")(props), props => themeGet("colors.greyLight")(props), crossTransform1, crossTransform2);
20
21
  const Icon = styled.div.withConfig({
21
22
  displayName: "ActionsMenu__Icon",
22
23
  componentId: "sc-yvbni2-2"
23
- })(["border-radius:2px;height:4px;width:4px;background-color:", ";&:before,&:after{content:\"\";display:block;position:absolute;border-radius:2px;height:4px;width:4px;background-color:", ";transition:", ";transform-origin:50% 50%;}&:before{transform:translate(0,-6px) scaleX(1);}&:after{transform:translate(0,6px) scaleX(1);}", ";"], props => themeGet("colors.greyDarker")(props), props => themeGet("colors.greyDarker")(props), props => themeGet("transition.transitionDefault")(props), props => props.isOpen ? css(["&:before{animation:400ms ", " ease-in-out forwards;}&:after{animation:400ms ", " ease-in-out forwards;}"], crossTransform1, crossTransform2) : css([""]));
24
+ })(["border-radius:2px;height:4px;width:4px;background-color:", ";&:before,&:after{content:\"\";display:block;position:absolute;border-radius:2px;height:4px;width:4px;background-color:", ";transition:", ";transform-origin:50% 50%;}&:before{transform:translate(0,-6px) scaleX(1);}&:after{transform:translate(0,6px) scaleX(1);}"], props => themeGet("colors.greyDarker")(props), props => themeGet("colors.greyDarker")(props), props => themeGet("transition.transitionDefault")(props));
24
25
  const Menu = styled.div.withConfig({
25
26
  displayName: "ActionsMenu__Menu",
26
27
  componentId: "sc-yvbni2-3"
27
- })(["display:", ";position:absolute;left:", ";right:", ";top:", ";width:", ";z-index:5;background-color:", ";border-radius:", ";transform:scale(0);opacity:0;pointer-events:none;transform-origin:", ";transition:all 300ms;transition-timing-function:cubic-bezier(0,1.4,1,1);", ";"], props => props.isOpen ? "block" : "none", props => props.menuLeftPosition ? props.menuLeftPosition : props.direction == "left" ? "auto" : "34px", props => props.menuRightPosition ? props.menuRightPosition : props.direction == "left" ? "34px" : "auto", props => props.menuTopPosition ? props.menuTopPosition : "0", props => props.menuWidth ? props.menuWidth : "auto", props => themeGet("colors.greyDarker")(props), props => themeGet("radii.2")(props), props => props.direction == "left" ? "top right" : "top left", props => props.isOpen ? css(["transform:scale(1);opacity:1;pointer-events:auto;"]) : css([""]));
28
+ })(["display:block;width:", ";z-index:5;background-color:", ";border-radius:", ";transition:all 300ms;transition-timing-function:cubic-bezier(0,1.4,1,1);pointer-events:auto;"], props => props.menuWidth ? props.menuWidth : "auto", props => themeGet("colors.greyDarker")(props), props => themeGet("radii.2")(props));
28
29
  export const ActionsMenuHeading = styled(props => {
30
+ const {
31
+ actionMenu
32
+ } = useContext(ActionMenuContext);
33
+ const id = useId();
34
+
35
+ // // Only sets `aria-labelledby` on the Popover root element
36
+ // // if this component is mounted inside it.
37
+ useLayoutEffect(() => {
38
+ actionMenu.setLabelId(id);
39
+ return () => actionMenu.setLabelId(undefined);
40
+ }, [id, actionMenu]);
29
41
  return /*#__PURE__*/_jsx("div", {
30
42
  ...props,
31
43
  onKeyUp: e => {
@@ -81,10 +93,8 @@ export const ActionsMenuBody = _ref => {
81
93
  theme,
82
94
  onToggle,
83
95
  toggleState,
84
- direction,
85
- menuTopPosition,
86
- menuLeftPosition,
87
- menuRightPosition,
96
+ // direction - Deprecated
97
+ direction = "right-start",
88
98
  menuWidth,
89
99
  customTriggerComponent,
90
100
  children,
@@ -94,101 +104,76 @@ export const ActionsMenuBody = _ref => {
94
104
  closeOnClick = false,
95
105
  ...props
96
106
  } = _ref;
97
- const [isTabbed, setTabbed] = useState(false);
98
107
  const id = useId();
99
- const wrapperRef = useRef();
100
- const [menuPosition] = useState({
101
- menuLeftPosition,
102
- menuRightPosition,
103
- menuTopPosition
104
- });
105
- const [inViewDirection, setInViewDirection] = useState(direction);
106
- const setMenuPosition = useCallback(newDirection => {
107
- if (typeof menuLeftPosition !== "undefined" || typeof menuRightPosition !== "undefined") {
108
- if (menuPosition.menuLeftPosition) {
109
- menuPosition.menuRightPosition = menuPosition.menuLeftPosition;
110
- menuPosition.menuLeftPosition = null;
111
- } else if (menuPosition.menuRightPosition) {
112
- menuPosition.menuLeftPosition = menuPosition.menuRightPosition;
113
- menuPosition.menuRightPosition = null;
108
+ const actionMenu = useActionMenu({
109
+ placement: direction,
110
+ open: toggleState,
111
+ onOpenChange: (_, e) => {
112
+ if (e) {
113
+ onToggle === null || onToggle === void 0 || onToggle(e);
114
114
  }
115
115
  }
116
- setInViewDirection(newDirection);
117
- }, [menuLeftPosition, menuRightPosition, menuPosition, setInViewDirection]);
118
- const [ref, setIsShown] = useKeepInView({
119
- direction,
120
- callback: setMenuPosition
121
116
  });
122
- useEffect(() => {
123
- setIsShown(toggleState);
124
- }, [toggleState, setIsShown]);
125
- let triggerBtn = null;
126
- const value = useMemo(() => ({
117
+ const childrenRef = children.ref;
118
+ const triggerRef = useMergeRefs([actionMenu.refs.setReference, childrenRef]);
119
+ const ref = useMergeRefs([actionMenu.refs.setFloating]);
120
+ const triggerProps = {
121
+ ariaLabel,
122
+ "aria-label": ariaLabel,
123
+ onFocus: onTriggerFocus,
127
124
  id,
128
- onItemClick: e => {
129
- if (closeOnClick && !isTabbed) {
130
- closeMenu(e);
131
- }
132
- }
133
- }), [closeOnClick, isTabbed, closeMenu, id]);
125
+ onClick: onToggle,
126
+ ...actionMenu.getReferenceProps({
127
+ ref: triggerRef,
128
+ ...props,
129
+ ...children.props,
130
+ "data-state": actionMenu.open ? "open" : "closed"
131
+ })
132
+ };
133
+ let triggerComponent = /*#__PURE__*/_jsx(Control, {
134
+ ...triggerProps,
135
+ children: /*#__PURE__*/_jsx(Icon, {
136
+ className: "action-menu-icon"
137
+ })
138
+ });
134
139
  if (customTriggerComponent) {
135
- triggerBtn = /*#__PURE__*/React.cloneElement(customTriggerComponent, {
136
- onClick: onToggle,
137
- "aria-label": ariaLabel,
138
- "aria-expanded": "".concat(toggleState),
139
- onFocus: onTriggerFocus,
140
- id
141
- });
142
- } else {
143
- triggerBtn = /*#__PURE__*/_jsx(Control, {
144
- "aria-label": ariaLabel,
145
- "aria-expanded": "".concat(toggleState),
146
- onClick: onToggle,
147
- onFocus: onTriggerFocus,
148
- id: id,
149
- children: /*#__PURE__*/_jsx(Icon, {
150
- isOpen: toggleState
151
- })
152
- });
140
+ triggerComponent = /*#__PURE__*/React.cloneElement(customTriggerComponent, triggerProps);
153
141
  }
154
- const component = /*#__PURE__*/_jsxs(Wrapper, {
155
- ...props,
156
- ref: wrapperRef,
157
- onKeyUp: e => {
158
- if ([commonKeys.ESCAPE, commonKeys.ESC].includes(e.key)) {
142
+ const value = useMemo(() => ({
143
+ id,
144
+ onItemClick: e => {
145
+ if (closeOnClick) {
159
146
  closeMenu(e);
160
- document.getElementById(id).focus();
161
- }
162
- if (commonKeys.TAB === e.key && !isTabbed) {
163
- setTabbed === null || setTabbed === void 0 || setTabbed(true);
164
147
  }
165
148
  },
166
- onBlur: e => {
167
- setTimeout(() => {
168
- var _wrapperRef$current, _document$getElementB;
169
- const focusedElement = document.activeElement;
170
- const isChild = (_wrapperRef$current = wrapperRef.current) === null || _wrapperRef$current === void 0 ? void 0 : _wrapperRef$current.contains(focusedElement);
171
- const isModalChild = (_document$getElementB = document.getElementById("modal-overlay")) === null || _document$getElementB === void 0 ? void 0 : _document$getElementB.contains(focusedElement);
172
- const isModalAndTabbed = isTabbed && isModalChild;
173
- if (isChild || isModalAndTabbed) return;
174
- setTabbed === null || setTabbed === void 0 || setTabbed(false);
175
- closeMenu(e);
176
- }, 0);
177
- },
178
- children: [triggerBtn, /*#__PURE__*/_jsx(Menu, {
179
- isOpen: toggleState,
180
- direction: inViewDirection,
181
- menuTopPosition: menuPosition.menuTopPosition,
182
- menuLeftPosition: menuPosition.menuLeftPosition,
183
- menuRightPosition: menuPosition.menuRightPosition,
184
- menuWidth: menuWidth,
185
- ref: ref,
186
- role: "list",
187
- children: /*#__PURE__*/_jsx(ActionMenuContext.Provider, {
188
- value: value,
189
- children: children
190
- })
191
- })]
149
+ actionMenu
150
+ }), [closeOnClick, closeMenu, id, actionMenu]);
151
+ const component = /*#__PURE__*/_jsx(ActionMenuContext.Provider, {
152
+ value: value,
153
+ children: /*#__PURE__*/_jsxs(Wrapper, {
154
+ ...props,
155
+ children: [triggerComponent, actionMenu.context.open && /*#__PURE__*/_jsx(FloatingPortal, {
156
+ children: /*#__PURE__*/_jsx(FloatingFocusManager, {
157
+ context: actionMenu.context,
158
+ modal: actionMenu.modal,
159
+ children: /*#__PURE__*/_jsx("div", {
160
+ ref: ref,
161
+ style: {
162
+ ...actionMenu.floatingStyles
163
+ },
164
+ "aria-labelledby": actionMenu.labelId,
165
+ ...actionMenu.getFloatingProps(props),
166
+ className: "actionMenu-content",
167
+ children: /*#__PURE__*/_jsx(Menu, {
168
+ menuWidth: menuWidth,
169
+ isOpen: toggleState,
170
+ role: "list",
171
+ children: children
172
+ })
173
+ })
174
+ })
175
+ })]
176
+ })
192
177
  });
193
178
  return theme ? /*#__PURE__*/_jsx(ThemeProvider, {
194
179
  theme: theme,
@@ -202,6 +187,7 @@ ActionsMenuBody.propTypes = {
202
187
  toggleState: PropTypes.bool.isRequired,
203
188
  closeOnClick: PropTypes.bool,
204
189
  direction: PropTypes.string,
190
+ placement: PropTypes.string,
205
191
  menuTopPosition: PropTypes.string,
206
192
  menuLeftPosition: PropTypes.string,
207
193
  menuRightPosition: PropTypes.string,
@@ -211,47 +197,28 @@ ActionsMenuBody.propTypes = {
211
197
  theme: PropTypes.object,
212
198
  ariaLabel: PropTypes.string
213
199
  };
214
- const ActionsMenu = /*#__PURE__*/React.forwardRef((_ref2, ref) => {
215
- let {
216
- children,
217
- customTriggerComponent,
218
- direction,
219
- isOpen = false,
220
- theme,
221
- closeOnClick = true,
222
- ariaLabel = "Options Menu",
223
- ...props
224
- } = _ref2;
225
- const [toggleState, setToggle] = useState(isOpen);
200
+ const ActionsMenu = /*#__PURE__*/React.forwardRef((props, ref) => {
201
+ const [toggleState, setToggle] = useState(false);
202
+ const onToggle = e => {
203
+ e.stopPropagation();
204
+ setToggle(!toggleState);
205
+ };
226
206
  useImperativeHandle(ref, () => ({
227
207
  closeMenu: () => {
228
208
  setToggle(false);
229
209
  }
230
210
  }));
231
- const onToggle = e => {
232
- e.stopPropagation();
233
- setToggle(!toggleState);
234
- };
235
- const closeMenu = e => {
236
- e.stopPropagation();
237
- setToggle(false);
238
- };
239
211
  return /*#__PURE__*/_jsx(ActionsMenuBody, {
240
- onToggle: onToggle,
241
- closeMenu: closeMenu,
242
- toggleState: toggleState,
243
- customTriggerComponent: customTriggerComponent,
244
- direction: direction,
245
- theme: theme,
246
- ariaLabel: ariaLabel,
247
- closeOnClick: closeOnClick,
248
212
  ...props,
249
- children: children
213
+ closeMenu: () => setToggle(false),
214
+ toggleState: toggleState,
215
+ onToggle: onToggle,
216
+ children: props.children
250
217
  });
251
218
  });
252
219
  ActionsMenu.propTypes = {
253
220
  isOpen: PropTypes.bool,
254
- direction: PropTypes.string,
221
+ direction: PropTypes.oneOf(["left", "right", "top", "bottom", "top-start", "top-end", "bottom-start", "bottom-end", "left-start", "left-end", "right-start", "right-end"]),
255
222
  customTriggerComponent: PropTypes.node,
256
223
  closeOnClick: PropTypes.bool,
257
224
  children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf(PropTypes.node)]),
@@ -266,49 +233,67 @@ ActionsMenu.__docgenInfo = {
266
233
  "displayName": "ActionsMenu",
267
234
  "props": {
268
235
  "isOpen": {
269
- "defaultValue": {
270
- "value": "false",
271
- "computed": false
272
- },
273
236
  "description": "",
274
237
  "type": {
275
238
  "name": "bool"
276
239
  },
277
240
  "required": false
278
241
  },
279
- "closeOnClick": {
280
- "defaultValue": {
281
- "value": "true",
282
- "computed": false
283
- },
242
+ "direction": {
284
243
  "description": "",
285
244
  "type": {
286
- "name": "bool"
287
- },
288
- "required": false
289
- },
290
- "ariaLabel": {
291
- "defaultValue": {
292
- "value": "\"Options Menu\"",
293
- "computed": false
294
- },
295
- "description": "Specifies the aria-label for the button",
296
- "type": {
297
- "name": "object"
245
+ "name": "enum",
246
+ "value": [{
247
+ "value": "\"left\"",
248
+ "computed": false
249
+ }, {
250
+ "value": "\"right\"",
251
+ "computed": false
252
+ }, {
253
+ "value": "\"top\"",
254
+ "computed": false
255
+ }, {
256
+ "value": "\"bottom\"",
257
+ "computed": false
258
+ }, {
259
+ "value": "\"top-start\"",
260
+ "computed": false
261
+ }, {
262
+ "value": "\"top-end\"",
263
+ "computed": false
264
+ }, {
265
+ "value": "\"bottom-start\"",
266
+ "computed": false
267
+ }, {
268
+ "value": "\"bottom-end\"",
269
+ "computed": false
270
+ }, {
271
+ "value": "\"left-start\"",
272
+ "computed": false
273
+ }, {
274
+ "value": "\"left-end\"",
275
+ "computed": false
276
+ }, {
277
+ "value": "\"right-start\"",
278
+ "computed": false
279
+ }, {
280
+ "value": "\"right-end\"",
281
+ "computed": false
282
+ }]
298
283
  },
299
284
  "required": false
300
285
  },
301
- "direction": {
286
+ "customTriggerComponent": {
302
287
  "description": "",
303
288
  "type": {
304
- "name": "string"
289
+ "name": "node"
305
290
  },
306
291
  "required": false
307
292
  },
308
- "customTriggerComponent": {
293
+ "closeOnClick": {
309
294
  "description": "",
310
295
  "type": {
311
- "name": "node"
296
+ "name": "bool"
312
297
  },
313
298
  "required": false
314
299
  },
@@ -333,6 +318,13 @@ ActionsMenu.__docgenInfo = {
333
318
  "name": "object"
334
319
  },
335
320
  "required": false
321
+ },
322
+ "ariaLabel": {
323
+ "description": "Specifies the aria-label for the button",
324
+ "type": {
325
+ "name": "object"
326
+ },
327
+ "required": false
336
328
  }
337
329
  }
338
330
  };
@@ -342,6 +334,17 @@ ActionsMenuBody.__docgenInfo = {
342
334
  "methods": [],
343
335
  "displayName": "ActionsMenuBody",
344
336
  "props": {
337
+ "direction": {
338
+ "defaultValue": {
339
+ "value": "\"right-start\"",
340
+ "computed": false
341
+ },
342
+ "description": "",
343
+ "type": {
344
+ "name": "string"
345
+ },
346
+ "required": false
347
+ },
345
348
  "ariaLabel": {
346
349
  "defaultValue": {
347
350
  "value": "\"Options Menu\"",
@@ -392,7 +395,7 @@ ActionsMenuBody.__docgenInfo = {
392
395
  },
393
396
  "required": true
394
397
  },
395
- "direction": {
398
+ "placement": {
396
399
  "description": "",
397
400
  "type": {
398
401
  "name": "string"
@@ -0,0 +1,57 @@
1
+ import React, { useCallback, useMemo, useRef, useState } from "react";
2
+ import { useFloating, autoUpdate, offset, flip, shift, useClick, useDismiss, useRole, useInteractions, FloatingArrow, arrow } from "@floating-ui/react";
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ export default function useActionMenu(_ref) {
5
+ let {
6
+ initialOpen = false,
7
+ placement = "right",
8
+ modal,
9
+ open: controlledOpen,
10
+ onOpenChange: setControlledOpen,
11
+ isTooltip = false
12
+ } = _ref;
13
+ const [uncontrolledOpen, setUncontrolledOpen] = useState(initialOpen);
14
+ const [labelId, setLabelId] = useState();
15
+ const [descriptionId, setDescriptionId] = useState();
16
+ const arrowRef = useRef();
17
+ const open = controlledOpen !== null && controlledOpen !== void 0 ? controlledOpen : uncontrolledOpen;
18
+ const setOpen = setControlledOpen !== null && setControlledOpen !== void 0 ? setControlledOpen : setUncontrolledOpen;
19
+ const data = useFloating({
20
+ placement,
21
+ open,
22
+ onOpenChange: setOpen,
23
+ whileElementsMounted: autoUpdate,
24
+ middleware: [offset(5), flip({
25
+ crossAxis: placement.includes("-"),
26
+ fallbackAxisSideDirection: "end",
27
+ padding: 5
28
+ }), shift({
29
+ padding: 5
30
+ }), isTooltip && arrow({
31
+ element: arrowRef
32
+ })]
33
+ });
34
+ const context = data.context;
35
+ const click = useClick(context, {
36
+ enabled: !isTooltip && controlledOpen == null
37
+ });
38
+ const dismiss = useDismiss(context);
39
+ const role = useRole(context);
40
+ const interactions = useInteractions([click, dismiss, role]);
41
+ const Arrow = useCallback(() => /*#__PURE__*/_jsx(FloatingArrow, {
42
+ ref: arrowRef,
43
+ context: context
44
+ }), [context]);
45
+ return useMemo(() => ({
46
+ open,
47
+ setOpen,
48
+ ...interactions,
49
+ ...data,
50
+ modal,
51
+ labelId,
52
+ descriptionId,
53
+ setLabelId,
54
+ setDescriptionId,
55
+ Arrow
56
+ }), [open, setOpen, interactions, data, modal, labelId, descriptionId, Arrow]);
57
+ }
@@ -24,73 +24,76 @@ export const defaultPopover = () => /*#__PURE__*/_jsx(Popover, {
24
24
  })
25
25
  });
26
26
  defaultPopover.storyName = "Default";
27
- export const direction = () => /*#__PURE__*/_jsxs(Grid, {
28
- gridTemplateColumns: "1fr 1fr 1fr",
29
- gridAutoFlow: "row",
30
- gridGap: "r",
31
- alignItems: "center",
32
- justifyContent: "center",
33
- children: [/*#__PURE__*/_jsx(Popover, {
34
- direction: "topLeft",
35
- text: "Description that explains child element",
36
- children: /*#__PURE__*/_jsx(Button, {
37
- width: "125px",
38
- children: "Top left"
39
- })
40
- }), /*#__PURE__*/_jsx(Popover, {
41
- direction: "top",
42
- text: "Description that explains child element",
43
- children: /*#__PURE__*/_jsx(Button, {
44
- width: "125px",
45
- children: "Top"
46
- })
47
- }), /*#__PURE__*/_jsx(Popover, {
48
- direction: "topRight",
49
- text: "Description that explains child element",
50
- children: /*#__PURE__*/_jsx(Button, {
27
+ export const direction = () => /*#__PURE__*/_jsx(Box, {
28
+ py: "6",
29
+ children: /*#__PURE__*/_jsxs(Grid, {
30
+ gridTemplateColumns: "1fr 1fr 1fr",
31
+ gridAutoFlow: "row",
32
+ gridGap: "r",
33
+ alignItems: "center",
34
+ justifyContent: "center",
35
+ children: [/*#__PURE__*/_jsx(Popover, {
36
+ direction: "topLeft",
37
+ text: "Description that explains child element",
38
+ children: /*#__PURE__*/_jsx(Button, {
39
+ width: "125px",
40
+ children: "Top left"
41
+ })
42
+ }), /*#__PURE__*/_jsx(Popover, {
43
+ direction: "top",
44
+ text: "Description that explains child element",
45
+ children: /*#__PURE__*/_jsx(Button, {
46
+ width: "125px",
47
+ children: "Top"
48
+ })
49
+ }), /*#__PURE__*/_jsx(Popover, {
50
+ direction: "topRight",
51
+ text: "Description that explains child element",
52
+ children: /*#__PURE__*/_jsx(Button, {
53
+ width: "125px",
54
+ children: "Top right"
55
+ })
56
+ }), /*#__PURE__*/_jsx(Popover, {
57
+ direction: "left",
58
+ text: "Description that explains child element",
59
+ children: /*#__PURE__*/_jsx(Button, {
60
+ width: "125px",
61
+ children: "Left"
62
+ })
63
+ }), /*#__PURE__*/_jsx(Box, {
51
64
  width: "125px",
52
- children: "Top right"
53
- })
54
- }), /*#__PURE__*/_jsx(Popover, {
55
- direction: "left",
56
- text: "Description that explains child element",
57
- children: /*#__PURE__*/_jsx(Button, {
58
- width: "125px",
59
- children: "Left"
60
- })
61
- }), /*#__PURE__*/_jsx(Box, {
62
- width: "125px",
63
- bg: "greyLighter",
64
- m: "auto"
65
- }), /*#__PURE__*/_jsx(Popover, {
66
- direction: "right",
67
- text: "Description that explains child element",
68
- children: /*#__PURE__*/_jsx(Button, {
69
- width: "125px",
70
- children: "Right"
71
- })
72
- }), /*#__PURE__*/_jsx(Popover, {
73
- direction: "bottomLeft",
74
- text: "Description that explains child element",
75
- children: /*#__PURE__*/_jsx(Button, {
76
- width: "125px",
77
- children: "Bottom left"
78
- })
79
- }), /*#__PURE__*/_jsx(Popover, {
80
- direction: "bottom",
81
- text: "Description that explains child element",
82
- children: /*#__PURE__*/_jsx(Button, {
83
- width: "125px",
84
- children: "Bottom"
85
- })
86
- }), /*#__PURE__*/_jsx(Popover, {
87
- direction: "bottomRight",
88
- text: "Description that explains child element",
89
- children: /*#__PURE__*/_jsx(Button, {
90
- width: "125px",
91
- children: "Bottom right"
92
- })
93
- })]
65
+ bg: "greyLighter",
66
+ m: "auto"
67
+ }), /*#__PURE__*/_jsx(Popover, {
68
+ direction: "right",
69
+ text: "Description that explains child element",
70
+ children: /*#__PURE__*/_jsx(Button, {
71
+ width: "125px",
72
+ children: "Right"
73
+ })
74
+ }), /*#__PURE__*/_jsx(Popover, {
75
+ direction: "bottomLeft",
76
+ text: "Description that explains child element",
77
+ children: /*#__PURE__*/_jsx(Button, {
78
+ width: "125px",
79
+ children: "Bottom left"
80
+ })
81
+ }), /*#__PURE__*/_jsx(Popover, {
82
+ direction: "bottom",
83
+ text: "Description that explains child element",
84
+ children: /*#__PURE__*/_jsx(Button, {
85
+ width: "125px",
86
+ children: "Bottom"
87
+ })
88
+ }), /*#__PURE__*/_jsx(Popover, {
89
+ direction: "bottomRight",
90
+ text: "Description that explains child element",
91
+ children: /*#__PURE__*/_jsx(Button, {
92
+ width: "125px",
93
+ children: "Bottom right"
94
+ })
95
+ })]
96
+ })
94
97
  });
95
98
  direction.storyName = "Direction";
96
99
  export const specifiedWidth = () => /*#__PURE__*/_jsx(Popover, {
@@ -1,118 +1,136 @@
1
- import React, { useId, useRef, useState } from "react";
2
- import PropTypes from "prop-types";
3
- import styled, { css, ThemeProvider } from "styled-components";
4
- import { space, layout } from "styled-system";
5
- import { keys } from "lodash";
1
+ import React, { cloneElement, useState } from "react";
2
+ import { useFloating, autoUpdate, offset, flip, shift, useHover, useFocus, useDismiss, useRole, useInteractions, FloatingPortal, safePolygon, useClick } from "@floating-ui/react";
3
+ import themeGet from "@styled-system/theme-get";
4
+ import styled from "styled-components";
6
5
  import Icon from "../Icon";
7
- import { themeGet } from "@styled-system/theme-get";
8
- import { useKeepInView, directions } from "../../hooks/keepInView";
9
- import { commonKeys } from "../../hooks/keypress";
6
+ import { PropTypes } from "prop-types";
10
7
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
11
- const Container = styled.div.withConfig({
12
- displayName: "Popover__Container",
8
+ const DIRECTIONS_MAP = {
9
+ topLeft: "top-start",
10
+ top: "top",
11
+ topRight: "top-end",
12
+ left: "left",
13
+ right: "right",
14
+ bottomLeft: "bottom-start",
15
+ bottom: "bottom",
16
+ bottomRight: "bottom-end"
17
+ };
18
+ const StyledPopoverContainer = styled.div.withConfig({
19
+ displayName: "Popover__StyledPopoverContainer",
13
20
  componentId: "sc-1bwoak-0"
14
- })(["", " ", " display:", ";position:relative;&:hover,&:focus-within{.popoverText{opacity:1;z-index:100;visibility:visible;pointer-events:auto;}}"], space, layout, props => props.inlineBlock ? "inline-block !important" : "block !important");
21
+ })(["pointer-events:none;opacity:0;transition:all 300ms ease-in-out;transition-delay:300ms;&.isOpen{opacity:1;pointer-events:auto;}"]);
15
22
  const TooltipControl = styled.div.withConfig({
16
23
  displayName: "Popover__TooltipControl",
17
24
  componentId: "sc-1bwoak-1"
18
- })(["border:none;background:none;padding:0;cursor:help;font-size:1em;color:", ";transition:", ";&:hover,&:focus{outline:0;color:", ";}"], props => themeGet("colors.black")(props), props => themeGet("transition.transitionDefault")(props), props => themeGet("colors.primary")(props));
19
- const Text = styled.div.withConfig({
20
- displayName: "Popover__Text",
25
+ })(["border:none;background:none;padding:0;cursor:help;font-size:1em;color:", ";transition:", ";&:hover,&:focus{outline:0;color:", ";}"], props => props.active ? themeGet("colors.primary")(props) : themeGet("colors.black")(props), themeGet("transition.transitionDefault"), themeGet("colors.primary"));
26
+ const StyledPopover = styled.div.withConfig({
27
+ displayName: "Popover__StyledPopover",
21
28
  componentId: "sc-1bwoak-2"
22
- })(["position:absolute;font-size:", ";line-height:", ";font-weight:", ";text-align:", ";word-break:break-word;left:100%;top:50%;color:", ";outline:0;padding:", " ", ";border-radius:", ";transform:translateX(10px) translateY(-50%);width:", ";background:", ";border:1px solid ", ";box-shadow:", ";transition:all 300ms ease-in-out;transition-delay:300ms;opacity:0;z-index:-100;visibility:hidden;pointer-events:none;user-select:", ";& a{font-size:", ";}&:before{content:\"\";z-index:2;height:0;width:0;border-style:solid;border-width:6px 6px 6px 0;border-color:transparent;border-right-color:", ";left:-6px;top:50%;margin-top:-6px;position:absolute;}&:after{content:\"\";z-index:1;position:absolute;border-color:transparent;border-right-color:", ";height:0;width:0;border-style:solid;border-width:6px 6px 6px 0;left:-7px;top:50%;margin-top:-6px;}", ";"], props => themeGet("fontSizes.0")(props), props => themeGet("fontSizes.1")(props), props => themeGet("fontWeights.1")(props), props => props.textAlign ? props.textAlign : "left", props => themeGet("colors.greyDarkest")(props), props => themeGet("space.3")(props), props => themeGet("space.3")(props), props => themeGet("radii.1")(props), props => props.width ? props.width : "200px", props => themeGet("colors.white")(props), props => themeGet("colors.greyLight")(props), props => themeGet("shadows.boxDefault")(props), props => props.enableSelectAll ? "all" : "auto", props => themeGet("fontSizes.0")(props), props => themeGet("colors.white")(props), props => themeGet("colors.grey")(props), _ref => {
23
- let {
24
- direction
25
- } = _ref;
26
- return direction === "top" ? css(["left:50%;top:auto;bottom:100%;transform:translateX(-50%) translateY(-10px);&:before{left:50%;top:auto;margin-top:0;bottom:-9px;margin-left:-3px;transform:rotate(-90deg);}&:after{left:50%;top:auto;margin-top:0;bottom:-10px;margin-left:-3px;transform:rotate(-90deg);}"]) : direction === "topRight" ? css(["left:100%;top:auto;bottom:100%;transform:translateX(5px) translateY(-5px);&:before{left:0;top:auto;margin-top:0;bottom:-5px;margin-left:-5px;transform:rotate(-45deg);border-width:5px 10px 5px 0;}&:after{left:0;top:auto;margin-top:0;bottom:-6px;margin-left:-6px;transform:rotate(-45deg);border-width:5px 10px 5px 0;}"]) : direction === "right" ? css([""]) : direction === "bottomRight" ? css(["left:100%;top:100%;bottom:auto;transform:translateX(5px) translateY(5px);&:before{left:0;bottom:auto;margin-top:0;top:-5px;margin-left:-5px;transform:rotate(45deg);border-width:5px 10px 5px 0;}&:after{left:0;bottom:auto;margin-top:0;top:-6px;margin-left:-6px;transform:rotate(45deg);border-width:5px 10px 5px 0;}"]) : direction === "bottom" ? css(["left:50%;top:100%;transform:translateX(-50%) translateY(10px);&:before{left:50%;top:-9px;margin-top:0;margin-left:-3px;transform:rotate(90deg);}&:after{left:50%;top:-10px;margin-top:0;margin-left:-3px;transform:rotate(90deg);}"]) : direction === "bottomLeft" ? css(["right:100%;left:auto;top:100%;bottom:auto;transform:translateX(-5px) translateY(5px);&:before{right:0;left:auto;bottom:auto;margin-top:0;top:-5px;margin-right:-5px;transform:rotate(135deg);border-width:5px 10px 5px 0;}&:after{right:0;left:auto;bottom:auto;margin-top:0;top:-6px;margin-right:-6px;transform:rotate(135deg);border-width:5px 10px 5px 0;}"]) : direction === "left" ? css(["left:auto;right:100%;transform:translateX(-10px) translateY(-50%);&:before{left:auto;right:-6px;transform:rotate(180deg);}&:after{left:auto;right:-7px;transform:rotate(180deg);top:50%;margin-top:-6px;}"]) : direction === "topLeft" ? css(["right:100%;left:auto;top:auto;bottom:100%;transform:translateX(-5px) translateY(-5px);&:before{right:0;left:auto;top:auto;margin-top:0;bottom:-5px;margin-right:-5px;transform:rotate(225deg);border-width:5px 10px 5px 0;}&:after{right:0;left:auto;top:auto;margin-top:0;bottom:-6px;margin-right:-6px;transform:rotate(225deg);border-width:5px 10px 5px 0;}"]) : css([""]);
27
- });
28
- const HoverSpacer = styled.div.withConfig({
29
- displayName: "Popover__HoverSpacer",
30
- componentId: "sc-1bwoak-3"
31
- })(["position:absolute;background:transparent;height:calc(100% + 24px);width:calc(100% + 24px);left:-12px;top:-12px;"]);
32
-
33
- /**
34
- * This popover component is intended to be used to supplement buttons (or other elements) that require some helper text. It supports customisation of direction and width. This is so that you can ensure that the popover doesn't run off the screen, and that the width suits the amount of text in the popover.
35
- *
36
- * If you don't specify a width, 200px is the default, but as a general guide try and keep widths somewhere between 150-250 if you are modifying. Make sure if setting width you include the unit you want it to use, e.g. pixels, %.
37
- */
38
-
39
- export default function Popover(_ref2) {
29
+ })(["font-size:", ";line-height:", ";font-weight:", ";text-align:", ";word-break:break-word;color:", ";outline:0;padding:", ";border-radius:", ";width:", ";background:", ";border:1px solid ", ";box-shadow:", ";user-select:", ";opacity:1;& a{font-size:", ";}&:before{content:\"\";z-index:2;height:0;width:0;border-style:solid;border-width:6px 6px 6px 0;border-color:transparent;border-right-color:", ";left:-5px;top:50%;margin-top:-6px;position:absolute;}&:after{content:\"\";z-index:1;position:absolute;border-color:transparent;border-right-color:", ";height:0;width:0;border-style:solid;border-width:6px 6px 6px 0;left:-6px;top:50%;margin-top:-6px;}&.top{&:before{left:50%;top:auto;margin-top:0;bottom:-8px;margin-left:-3px;transform:rotate(-90deg);}&:after{left:50%;top:auto;margin-top:0;bottom:-9px;margin-left:-3px;transform:rotate(-90deg);}}&.topRight,&.top-end{&:before{left:1px;top:auto;margin-top:0;bottom:-4px;margin-left:-5px;transform:rotate(-45deg);border-width:5px 10px 5px 0;}&:after{left:1px;top:auto;margin-top:0;bottom:-5px;margin-left:-6px;transform:rotate(-45deg);border-width:5px 10px 5px 0;}}&.bottomRight,&.bottom-end{&:before{left:1px;bottom:auto;margin-top:0;top:-4px;margin-left:-5px;transform:rotate(45deg);border-width:5px 10px 5px 0;}&:after{left:1px;bottom:auto;margin-top:0;top:-5px;margin-left:-6px;transform:rotate(45deg);border-width:5px 10px 5px 0;}}&.bottom{&:before{left:50%;top:-8px;margin-top:0;margin-left:-3px;transform:rotate(90deg);}&:after{left:50%;top:-9px;margin-top:0;margin-left:-3px;transform:rotate(90deg);}}&.bottomLeft,&.bottom-start{&:before{right:1px;left:auto;bottom:auto;margin-top:0;top:-4px;margin-right:-5px;transform:rotate(135deg);border-width:5px 10px 5px 0;}&:after{right:1px;left:auto;bottom:auto;margin-top:0;top:-5px;margin-right:-6px;transform:rotate(135deg);border-width:5px 10px 5px 0;}}&.left{&:before{left:auto;right:-5px;transform:rotate(180deg);}&:after{left:auto;right:-6px;transform:rotate(180deg);top:50%;margin-top:-6px;}}&.topLeft,&.top-start{&:before{right:1px;left:auto;top:auto;margin-top:0;bottom:-4px;margin-right:-5px;transform:rotate(225deg);border-width:5px 10px 5px 0;}&:after{right:1px;left:auto;top:auto;margin-top:0;bottom:-5px;margin-right:-6px;transform:rotate(225deg);border-width:5px 10px 5px 0;}}"], themeGet("fontSizes.0"), themeGet("fontSizes.1"), themeGet("fontWeights.1"), props => props.textAlign ? props.textAlign : "left", themeGet("colors.greyDarkest"), themeGet("space.3"), themeGet("radii.1"), props => props.width ? props.width : "200px", themeGet("colors.white"), themeGet("colors.greyLight"), themeGet("shadows.boxDefault"), props => props.enableSelectAll ? "all" : "auto", themeGet("fontSizes.0"), themeGet("colors.white"), themeGet("colors.grey"));
30
+ export default function Popover(_ref) {
40
31
  let {
41
32
  children,
42
- direction,
43
- width,
44
- textAlign,
33
+ direction = "right",
45
34
  text,
46
- inlineBlock,
47
- theme,
35
+ textAlign,
36
+ width,
37
+ enableSelectAll,
48
38
  variant,
49
- enableSelectAll = true,
50
- tabIndex = 0,
39
+ ariaLabel,
51
40
  ...props
52
- } = _ref2;
53
- const containerRef = useRef();
54
- const [inViewDirection, setInViewDirection] = useState(direction);
55
- const [dismissed, setDismissed] = useState(false);
56
- const toolTipId = useId();
57
- const [ref, setIsShown] = useKeepInView({
58
- direction,
59
- callback: setInViewDirection
41
+ } = _ref;
42
+ const [isOpen, setIsOpen] = useState(false);
43
+ const {
44
+ refs,
45
+ floatingStyles,
46
+ context
47
+ } = useFloating({
48
+ open: isOpen,
49
+ onOpenChange: setIsOpen,
50
+ placement: DIRECTIONS_MAP[direction] || direction || "right",
51
+ whileElementsMounted: autoUpdate,
52
+ middleware: [offset(_ref2 => {
53
+ let {
54
+ rects
55
+ } = _ref2;
56
+ return {
57
+ mainAxis: 10,
58
+ alignmentAxis: -rects.floating.width
59
+ };
60
+ }), flip({
61
+ fallbackAxisSideDirection: "start"
62
+ }), shift()]
60
63
  });
61
- const component = /*#__PURE__*/_jsxs(Container, {
62
- ref: containerRef,
63
- inlineBlock: inlineBlock,
64
- ...props,
65
- onMouseEnter: () => {
66
- setIsShown(true);
67
- setDismissed(false);
68
- },
69
- onKeyDownCapture: e => {
70
- if ([commonKeys.ESCAPE, commonKeys.ESC].includes(e.key)) {
71
- setDismissed(true);
72
- if (containerRef !== null && containerRef !== void 0 && containerRef.current.contains(document.activeElement)) {
73
- document.activeElement.blur();
74
- }
75
- }
76
- },
77
- onBlur: () => {
78
- setDismissed(false);
79
- },
80
- onMouseLeave: () => {
81
- setIsShown(false);
82
- setDismissed(false);
83
- },
84
- "aria-describedby": toolTipId,
85
- children: [variant === "tooltip" && /*#__PURE__*/_jsx(TooltipControl, {
86
- tabIndex: tabIndex,
64
+ const hover = useHover(context, {
65
+ move: false,
66
+ handleClose: safePolygon()
67
+ });
68
+ const focus = useFocus(context);
69
+ const click = useClick(context, {
70
+ enabled: true
71
+ });
72
+ const dismiss = useDismiss(context);
73
+ const role = useRole(context, {
74
+ role: "tooltip"
75
+ });
76
+ const {
77
+ getReferenceProps,
78
+ getFloatingProps
79
+ } = useInteractions([hover, focus, dismiss, role, click]);
80
+ const triggerProps = {
81
+ ...getReferenceProps({
82
+ ref: refs.setReference
83
+ })
84
+ };
85
+ let triggerComponent = null;
86
+ if (variant === "tooltip") {
87
+ triggerComponent = /*#__PURE__*/_jsx(TooltipControl, {
88
+ ...triggerProps,
89
+ active: isOpen,
87
90
  children: /*#__PURE__*/_jsx(Icon, {
88
91
  transform: "grow-4",
89
92
  icon: ["fas", "question-circle"],
90
93
  fontSize: "2"
91
94
  })
92
- }), !!text && !dismissed && /*#__PURE__*/_jsxs(Text, {
93
- ref: ref,
94
- role: "tooltip",
95
- className: "popoverText",
96
- textAlign: textAlign,
97
- direction: inViewDirection,
98
- width: width,
99
- enableSelectAll: enableSelectAll,
100
- id: toolTipId,
101
- children: [/*#__PURE__*/_jsx(HoverSpacer, {
102
- className: "popoverSpacer"
103
- }), text]
104
- }), children]
95
+ });
96
+ } else if (children.type.$$typeof === Symbol.for("react.forward_ref")) {
97
+ triggerComponent = /*#__PURE__*/cloneElement(children, triggerProps);
98
+ } else {
99
+ triggerComponent = /*#__PURE__*/_jsx("div", {
100
+ ...triggerProps,
101
+ children: children
102
+ });
103
+ }
104
+ const directionClass = context.placement === DIRECTIONS_MAP[direction] ? direction : context.placement;
105
+ return /*#__PURE__*/_jsxs("div", {
106
+ ...props,
107
+ children: [triggerComponent, /*#__PURE__*/_jsx(FloatingPortal, {
108
+ children: text && /*#__PURE__*/_jsx(StyledPopoverContainer, {
109
+ className: "Tooltip ".concat(isOpen ? "isOpen" : ""),
110
+ ref: refs.setFloating,
111
+ style: floatingStyles,
112
+ ...getFloatingProps({
113
+ onMouseEnter: () => setIsOpen(true),
114
+ onMouseLeave: () => setIsOpen(false)
115
+ }),
116
+ children: /*#__PURE__*/_jsx(StyledPopover, {
117
+ className: "".concat(directionClass),
118
+ role: "tooltip",
119
+ textAlign: textAlign,
120
+ width: width,
121
+ enableSelectAll: enableSelectAll,
122
+ ariaLabel: ariaLabel,
123
+ children: text
124
+ })
125
+ })
126
+ })]
105
127
  });
106
- return theme ? /*#__PURE__*/_jsx(ThemeProvider, {
107
- theme: theme,
108
- children: component
109
- }) : component;
110
128
  }
111
129
  Popover.propTypes = {
112
130
  /** The element that requires the popover helper text. */
113
131
  children: PropTypes.element,
114
132
  /** Specifies the direction of the popover. Defaults to right if not specified */
115
- direction: PropTypes.oneOf(keys(directions)),
133
+ direction: PropTypes.oneOf([...Object.keys(DIRECTIONS_MAP), ...Object.values(DIRECTIONS_MAP)]),
116
134
  /** The text contained in the popover element */
117
135
  text: PropTypes.node,
118
136
  /** Specifies the alignment of the text inside the popover */
@@ -133,29 +151,25 @@ Popover.propTypes = {
133
151
  tabIndex: PropTypes.number
134
152
  };
135
153
  Popover.__docgenInfo = {
136
- "description": "This popover component is intended to be used to supplement buttons (or other elements) that require some helper text. It supports customisation of direction and width. This is so that you can ensure that the popover doesn't run off the screen, and that the width suits the amount of text in the popover.\n\nIf you don't specify a width, 200px is the default, but as a general guide try and keep widths somewhere between 150-250 if you are modifying. Make sure if setting width you include the unit you want it to use, e.g. pixels, %.",
154
+ "description": "",
137
155
  "methods": [],
138
156
  "displayName": "Popover",
139
157
  "props": {
140
- "enableSelectAll": {
141
- "defaultValue": {
142
- "value": "true",
143
- "computed": false
144
- },
145
- "description": "Specifies whether enable select all behaviour",
146
- "type": {
147
- "name": "bool"
148
- },
149
- "required": false
150
- },
151
- "tabIndex": {
158
+ "direction": {
152
159
  "defaultValue": {
153
- "value": "0",
160
+ "value": "\"right\"",
154
161
  "computed": false
155
162
  },
156
- "description": "Provide a tab index for accessibilty, defaults to 0",
163
+ "description": "Specifies the direction of the popover. Defaults to right if not specified",
157
164
  "type": {
158
- "name": "number"
165
+ "name": "enum",
166
+ "value": [{
167
+ "value": "...Object.keys(DIRECTIONS_MAP)",
168
+ "computed": true
169
+ }, {
170
+ "value": "...Object.values(DIRECTIONS_MAP)",
171
+ "computed": true
172
+ }]
159
173
  },
160
174
  "required": false
161
175
  },
@@ -166,15 +180,6 @@ Popover.__docgenInfo = {
166
180
  },
167
181
  "required": false
168
182
  },
169
- "direction": {
170
- "description": "Specifies the direction of the popover. Defaults to right if not specified",
171
- "type": {
172
- "name": "enum",
173
- "computed": true,
174
- "value": "keys(directions)"
175
- },
176
- "required": false
177
- },
178
183
  "text": {
179
184
  "description": "The text contained in the popover element",
180
185
  "type": {
@@ -231,12 +236,26 @@ Popover.__docgenInfo = {
231
236
  },
232
237
  "required": false
233
238
  },
239
+ "enableSelectAll": {
240
+ "description": "Specifies whether enable select all behaviour",
241
+ "type": {
242
+ "name": "bool"
243
+ },
244
+ "required": false
245
+ },
234
246
  "ariaLabel": {
235
247
  "description": "Provide an aria-label if text is not a string",
236
248
  "type": {
237
249
  "name": "string"
238
250
  },
239
251
  "required": false
252
+ },
253
+ "tabIndex": {
254
+ "description": "Provide a tab index for accessibilty, defaults to 0",
255
+ "type": {
256
+ "name": "number"
257
+ },
258
+ "required": false
240
259
  }
241
260
  }
242
261
  };
@@ -16,11 +16,11 @@ export const defaultProgressBar = () => /*#__PURE__*/_jsxs(Spacer, {
16
16
  children: [/*#__PURE__*/_jsx(ProgressBar, {
17
17
  ariaLabel: "Percent Completed",
18
18
  containerWidth: 100,
19
- fillWidth: 40
19
+ fillWidth: 75
20
20
  }), /*#__PURE__*/_jsx(ProgressBar, {
21
21
  ariaLabel: "Percent Completed",
22
22
  containerWidth: 50,
23
- fillWidth: 70
23
+ fillWidth: 75
24
24
  })]
25
25
  });
26
26
  defaultProgressBar.storyName = "Default";
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useRef, useEffect, useState } from "react";
2
2
  import PropTypes from "prop-types";
3
3
  import styled, { css, keyframes, ThemeProvider } from "styled-components";
4
4
  import { space, layout } from "styled-system";
@@ -8,11 +8,20 @@ const expandWidth = keyframes(["0%{width:0;}"]);
8
8
  const Background = styled.div.withConfig({
9
9
  displayName: "ProgressBar__Background",
10
10
  componentId: "sc-q9qaaf-0"
11
- })(["", " ", " position:relative;background:", ";height:16px;border-radius:8px;"], space, layout, props => themeGet("colors.greyLighter")(props));
11
+ })(["", " ", " position:relative;background:", ";height:16px;border-radius:8px;width:", ";"], space, layout, props => themeGet("colors.greyLighter")(props), props => props.containerWidth ? props.containerWidth + "%" : "100%");
12
12
  const Fill = styled.div.withConfig({
13
13
  displayName: "ProgressBar__Fill",
14
14
  componentId: "sc-q9qaaf-1"
15
- })(["position:absolute;left:3px;top:3px;height:10px;border-radius:5px;animation:", " 1000ms ease-in-out 1;transition:", ";", ";"], expandWidth, props => themeGet("transition.transitionDefault")(props), props => props.gradient ? css(["background:linear-gradient( to right,", " 0%,", " 50%,", " 100% );"], themeGet("colors.danger")(props), themeGet("colors.warning")(props), themeGet("colors.success")(props)) : css(["background:", ";"], themeGet("colors.primary")(props)));
15
+ })(["width:", ";position:absolute;left:3px;top:3px;height:10px;border-radius:5px;animation:", " 1000ms ease-in-out 1;transition:", ";", ";"], props => {
16
+ if (props.fillWidth && props.containerWidthPx) {
17
+ const sixPxInPercentage = 6 / props.containerWidthPx * 100;
18
+ const minPixelValue = 1; // Minimum pixel value for visibility
19
+ const minPercentageValue = minPixelValue / props.containerWidthPx * 100;
20
+ const calculatedWidth = "calc(".concat(props.fillWidth, "% - ").concat(sixPxInPercentage, "%)");
21
+ return "max(".concat(calculatedWidth, ", ").concat(minPercentageValue, "%)");
22
+ }
23
+ return "0";
24
+ }, expandWidth, props => themeGet("transition.transitionDefault")(props), props => props.gradient ? css(["background:linear-gradient( to right,", " 0%,", " 50%,", " 100% );"], themeGet("colors.danger")(props), themeGet("colors.warning")(props), themeGet("colors.success")(props)) : css(["background:", ";"], themeGet("colors.primary")(props)));
16
25
 
17
26
  /**
18
27
  * Progress bar is not intended to be used for loading (that's what the Loading component is for). The intended use is for indicating progress through steps or progress towards a goal.
@@ -24,27 +33,32 @@ const Fill = styled.div.withConfig({
24
33
 
25
34
  export default function ProgressBar(_ref) {
26
35
  let {
27
- containerWidth,
36
+ containerWidth = "100%",
28
37
  fillWidth,
29
38
  gradient,
30
39
  theme,
31
40
  ariaLabel = "Progress",
32
41
  ...props
33
42
  } = _ref;
43
+ const backgroundRef = useRef(null);
44
+ const [containerWidthPx, setContainerWidthPx] = useState(0);
45
+ useEffect(() => {
46
+ if (backgroundRef.current) {
47
+ setContainerWidthPx(backgroundRef.current.offsetWidth);
48
+ }
49
+ }, [containerWidth]);
34
50
  const component = /*#__PURE__*/_jsx(Background, {
35
- style: {
36
- width: containerWidth + "%"
37
- },
51
+ ref: backgroundRef,
52
+ containerWidth: containerWidth,
38
53
  ...props,
39
54
  "aria-label": ariaLabel,
40
55
  "aria-valuenow": fillWidth,
41
56
  "aria-valuemin": "0",
42
57
  "aria-valuemax": "100",
43
58
  children: /*#__PURE__*/_jsx(Fill, {
59
+ containerWidthPx: containerWidthPx,
44
60
  gradient: gradient,
45
- style: {
46
- width: fillWidth + "%"
47
- }
61
+ fillWidth: fillWidth
48
62
  })
49
63
  });
50
64
  return theme ? /*#__PURE__*/_jsx(ThemeProvider, {
@@ -69,21 +83,25 @@ ProgressBar.__docgenInfo = {
69
83
  "methods": [],
70
84
  "displayName": "ProgressBar",
71
85
  "props": {
72
- "ariaLabel": {
86
+ "containerWidth": {
73
87
  "defaultValue": {
74
- "value": "\"Progress\"",
88
+ "value": "\"100%\"",
75
89
  "computed": false
76
90
  },
77
- "description": "Specifies the aria-label for the progress bar",
91
+ "description": "Sets the percentage width of the parent container",
78
92
  "type": {
79
- "name": "object"
93
+ "name": "number"
80
94
  },
81
95
  "required": false
82
96
  },
83
- "containerWidth": {
84
- "description": "Sets the percentage width of the parent container",
97
+ "ariaLabel": {
98
+ "defaultValue": {
99
+ "value": "\"Progress\"",
100
+ "computed": false
101
+ },
102
+ "description": "Specifies the aria-label for the progress bar",
85
103
  "type": {
86
- "name": "number"
104
+ "name": "object"
87
105
  },
88
106
  "required": false
89
107
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orcs-design-system",
3
- "version": "3.2.10",
3
+ "version": "3.2.12",
4
4
  "engines": {
5
5
  "node": "20.12.2"
6
6
  },
@@ -46,6 +46,7 @@
46
46
  "dependencies": {
47
47
  "@emotion/react": "^11.11.4",
48
48
  "@emotion/styled": "^11.11.5",
49
+ "@floating-ui/react": "^0.26.19",
49
50
  "@mui/icons-material": "^5.15.18",
50
51
  "@mui/material": "^5.15.18",
51
52
  "@mui/x-date-pickers": "^7.5.0",