@skyscanner/backpack-web 34.0.0-premajor → 34.0.0-premajor.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,7 +14,7 @@
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */import BpkPopoverPortal, { propTypes as bpkPopoverPortalPropTypes, defaultProps as bpkPopoverPortalDefaultProps } from "./src/BpkPopoverPortal";
17
+ */import BpkPopover from "./src/BpkPopover";
18
18
  import themeAttributes from "./src/themeAttributes";
19
- export { themeAttributes, bpkPopoverPortalPropTypes, bpkPopoverPortalDefaultProps };
20
- export default BpkPopoverPortal;
19
+ export { themeAttributes };
20
+ export default BpkPopover;
@@ -14,172 +14,96 @@
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */import PropTypes from 'prop-types';
18
- import { useState, cloneElement } from 'react';
19
- import { useFloating, autoUpdate, offset, flip, shift, useClick, useDismiss, useInteractions, FloatingFocusManager } from '@floating-ui/react';
20
- import BpkCloseButton from "../../bpk-component-close-button";
21
- import { BpkButtonLink } from "../../bpk-component-link";
22
- import BpkText, { TEXT_STYLES } from "../../bpk-component-text";
23
- import { TransitionInitialMount, cssModules } from "../../bpk-react-utils";
24
- import { ARROW_ID } from "./constants";
25
- import STYLES from "./BpkPopover.module.css";
26
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
27
- const getClassName = cssModules(STYLES);
17
+ */
18
+
19
+ import { useState, useRef } from 'react';
20
+ import { Popover, PopoverContent, PopoverTrigger } from "./PopperUtils";
21
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
28
22
  const EVENT_SOURCES = {
29
23
  CLOSE_BUTTON: 'CLOSE_BUTTON',
30
24
  CLOSE_LINK: 'CLOSE_LINK'
31
25
  };
32
- const bindEventSource = (source, callback) => event => {
33
- if (event.persist) {
34
- event.persist();
35
- }
36
- callback(event, {
37
- source
38
- });
39
- };
40
- const BpkPopover = props => {
41
- const {
42
- children,
43
- className,
44
- closeButtonIcon,
45
- closeButtonProps,
46
- closeButtonText,
47
- id,
48
- isOpen,
49
- label,
50
- labelAsTitle,
51
- onClose,
52
- padded,
53
- placement,
54
- popperModifiers,
55
- portalStyle,
56
- renderTarget,
57
- target,
58
- ...rest
59
- } = props;
26
+ const BpkPopover = ({
27
+ // children,
28
+ // className = null,
29
+ // closeButtonIcon = true,
30
+ // closeButtonProps = {},
31
+ // closeButtonText,
32
+ // id,
33
+ isOpen = false,
34
+ // label,
35
+ // labelAsTitle = false,
36
+ // onClose,
37
+ // padded = true,
38
+ placement = 'bottom',
39
+ showArrow = true,
40
+ target,
41
+ ...rest
42
+ }) => {
60
43
  const [isOpenState, setIsOpenState] = useState(isOpen);
61
- const {
62
- context,
63
- floatingStyles,
64
- refs
65
- } = useFloating({
66
- open: isOpenState,
67
- onOpenChange: setIsOpenState,
68
- middleware: [offset(17), popperModifiers],
69
- whileElementsMounted: autoUpdate
70
- });
71
- const click = useClick(context);
72
- const dismiss = useDismiss(context);
44
+ const arrowRef = useRef(null);
45
+
46
+ // const { context, floatingStyles, refs } = useFloating({
47
+ // open: isOpenState,
48
+ // onOpenChange: setIsOpenState,
49
+ // placement,
50
+ // middleware: [
51
+ // showArrow && offset(14),
52
+ // flip({ fallbackAxisSideDirection: 'start' }),
53
+ // arrow({ element: arrowRef }),
54
+ // ],
55
+ // whileElementsMounted: autoUpdate,
56
+ // });
57
+
58
+ // const click = useClick(context);
59
+ // const dismiss = useDismiss(context);
73
60
 
74
61
  // Merge all the interactions into prop getters
75
- const {
76
- getFloatingProps,
77
- getReferenceProps
78
- } = useInteractions([click, dismiss]);
62
+ // const { getFloatingProps, getReferenceProps } = useInteractions([
63
+ // click,
64
+ // dismiss,
65
+ // ]);
79
66
 
80
- // const getTargetElement = () => typeof target === 'function' ? target() : (target as JSXElementWithRefProps)?.ref.current
67
+ // const targetElement = cloneElement(target, {
68
+ // ...getReferenceProps(),
69
+ // ref: refs.setReference,
70
+ // });
71
+ // const classNames = getClassName('bpk-popover', className);
72
+ // const bodyClassNames = getClassName(padded && 'bpk-popover__body--padded');
81
73
 
82
- // const targetEl = cloneElement(getRenderTarget(renderTarget), { ...getReferenceProps(), ref: refs.setReference });
83
- // console.error('renderTarget', renderTarget());
84
- const targetEl = /*#__PURE__*/cloneElement(target, {
85
- ...getReferenceProps(),
86
- ref: refs.setReference
87
- });
88
- const classNames = getClassName('bpk-popover', className);
89
- const bodyClassNames = getClassName(padded && 'bpk-popover__body--padded');
90
- const labelId = `bpk-popover-label-${id}`;
91
- return /*#__PURE__*/_jsxs(_Fragment, {
92
- children: [targetEl, isOpenState && /*#__PURE__*/_jsx(FloatingFocusManager, {
93
- context: context,
94
- modal: false,
95
- children: /*#__PURE__*/_jsx("div", {
96
- ref: refs.setFloating,
97
- style: floatingStyles,
98
- ...getFloatingProps(),
99
- children: /*#__PURE__*/_jsx(TransitionInitialMount, {
100
- appearClassName: getClassName('bpk-popover--appear'),
101
- appearActiveClassName: getClassName('bpk-popover--appear-active'),
102
- transitionTimeout: 200,
103
- children: /*#__PURE__*/_jsxs("section", {
104
- id: id,
105
- tabIndex: "-1",
106
- role: "dialog",
107
- "aria-labelledby": labelId,
108
- className: classNames,
109
- ...rest,
110
- children: [labelAsTitle ? /*#__PURE__*/_jsxs("header", {
111
- className: getClassName('bpk-popover__header'),
112
- children: [/*#__PURE__*/_jsx(BpkText, {
113
- tagName: "h2",
114
- id: labelId,
115
- textStyle: TEXT_STYLES.label1,
116
- children: label
117
- }), "\xA0", closeButtonIcon ? /*#__PURE__*/_jsx(BpkCloseButton
118
- // TODO: className to be removed
119
- // eslint-disable-next-line @skyscanner/rules/forbid-component-props
120
- , {
121
- className: getClassName('bpk-popover__close-button'),
122
- label: closeButtonText,
123
- onClick: () => {
124
- bindEventSource(EVENT_SOURCES.CLOSE_BUTTON, props.onClose);
125
- setIsOpenState(false);
126
- },
127
- ...closeButtonProps
128
- }) : /*#__PURE__*/_jsx(BpkButtonLink, {
129
- onClick: () => {
130
- bindEventSource(EVENT_SOURCES.CLOSE_LINK, props.onClose);
131
- setIsOpenState(false);
132
- },
133
- ...closeButtonProps,
134
- children: closeButtonText
135
- })]
136
- }) : /*#__PURE__*/_jsx("span", {
137
- id: labelId,
138
- className: getClassName('bpk-popover__label'),
139
- children: label
140
- }), /*#__PURE__*/_jsx("div", {
141
- className: bodyClassNames,
142
- children: children
143
- }), !labelAsTitle && /*#__PURE__*/_jsx("footer", {
144
- className: getClassName('bpk-popover__footer'),
145
- children: /*#__PURE__*/_jsx(BpkButtonLink, {
146
- onClick: () => {
147
- bindEventSource(EVENT_SOURCES.CLOSE_LINK, props.onClose);
148
- setIsOpenState(false);
149
- },
150
- ...closeButtonProps,
151
- children: closeButtonText
152
- })
153
- })]
154
- })
155
- })
156
- })
157
- })]
158
- });
159
- };
160
- export const propTypes = {
161
- children: PropTypes.node.isRequired,
162
- closeButtonText: PropTypes.string.isRequired,
163
- id: PropTypes.string.isRequired,
164
- label: PropTypes.string.isRequired,
165
- onClose: PropTypes.func.isRequired,
166
- className: PropTypes.string,
167
- closeButtonIcon: PropTypes.bool,
168
- closeButtonProps: PropTypes.object,
169
- labelAsTitle: PropTypes.bool,
170
- padded: PropTypes.bool
171
- };
172
- export const defaultProps = {
173
- className: null,
174
- closeButtonIcon: true,
175
- closeButtonProps: null,
176
- labelAsTitle: false,
177
- padded: true
178
- };
179
- BpkPopover.propTypes = {
180
- ...propTypes
181
- };
182
- BpkPopover.defaultProps = {
183
- ...defaultProps
74
+ // const labelId = `bpk-popover-label-${id}`;
75
+
76
+ return (
77
+ /*#__PURE__*/
78
+ // <>
79
+ // {targetElement}
80
+ // {isOpenState && (
81
+ // <FloatingFocusManager context={context} modal={false}>
82
+ // <div
83
+ // ref={refs.setFloating}
84
+ // style={floatingStyles}
85
+ // {...getFloatingProps()}
86
+ // >
87
+ // </div>
88
+ // </FloatingFocusManager>
89
+ // )}
90
+ // </>
91
+ _jsxs(Popover, {
92
+ open: isOpenState,
93
+ onOpenChange: setIsOpenState,
94
+ arrowRef: arrowRef,
95
+ showArrow: showArrow,
96
+ placement: placement,
97
+ children: [/*#__PURE__*/_jsx(PopoverTrigger, {
98
+ onClick: () => setIsOpenState(v => !v),
99
+ children: target
100
+ }), /*#__PURE__*/_jsx(PopoverContent, {
101
+ setState: setIsOpenState,
102
+ arrowRef: arrowRef,
103
+ showArrow: showArrow,
104
+ ...rest
105
+ })]
106
+ })
107
+ );
184
108
  };
185
109
  export default BpkPopover;
@@ -15,4 +15,4 @@
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
17
  */
18
- .bpk-popover-portal{z-index:900}@media(max-width: 32rem){.bpk-popover-portal{margin-right:1rem;margin-left:1rem}}.bpk-popover{transition:opacity 200ms ease-in-out;outline:0;opacity:1;border:.0625rem solid #e0e4e9;background-color:#fff;border-radius:.5rem;box-shadow:0px 4px 14px 0px rgba(37,32,31,.25)}@media(min-width: 32.0625rem){.bpk-popover{max-width:32rem;transition:opacity 50ms ease-in-out}}.bpk-popover--appear{opacity:0}.bpk-popover--appear-active{opacity:1}.bpk-popover__arrow{position:absolute;width:1.5rem;height:1.5rem;transform:rotate(45deg);border:.0625rem solid rgba(0,0,0,0);background:inherit}.bpk-popover-portal[data-popper-placement=top] .bpk-popover__arrow{bottom:-0.6875rem;border-right-color:#e0e4e9;border-bottom-color:#e0e4e9}.bpk-popover-portal[data-popper-placement=right] .bpk-popover__arrow{left:-0.6875rem;border-bottom-color:#e0e4e9;border-left-color:#e0e4e9}.bpk-popover-portal[data-popper-placement=bottom] .bpk-popover__arrow{top:-0.6875rem;border-top-color:#e0e4e9;border-left-color:#e0e4e9}.bpk-popover-portal[data-popper-placement=left] .bpk-popover__arrow{right:-0.6875rem;border-top-color:#e0e4e9;border-right-color:#e0e4e9}.bpk-popover__arrow[data-hide]{visibility:hidden}.bpk-popover__body--padded{padding:1rem}.bpk-popover__header{display:flex;padding:1rem;justify-content:space-between;box-shadow:0 -1px 0 0 #e0e4e9 inset}.bpk-popover__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0)}.bpk-popover__close-button{float:right}html[dir=rtl] .bpk-popover__close-button{float:left}.bpk-popover__footer{padding:.5rem 1rem;text-align:right;box-shadow:0 1px 0 0 #e0e4e9 inset}html[dir=rtl] .bpk-popover__footer{text-align:left}
18
+ .bpk-popover-portal{z-index:900}@media(max-width: 32rem){.bpk-popover-portal{margin-right:1rem;margin-left:1rem}}.bpk-popover{transition:opacity 200ms ease-in-out;outline:0;opacity:1;border:.0625rem solid #e0e4e9;background-color:#fff;border-radius:.5rem;box-shadow:0px 4px 14px 0px rgba(37,32,31,.25)}@media(min-width: 32.0625rem){.bpk-popover{max-width:32rem;transition:opacity 50ms ease-in-out}}.bpk-popover--appear{opacity:0}.bpk-popover--appear-active{opacity:1}.bpk-popover__arrow{width:1.5rem;height:1.5rem;fill:#fff}.bpk-popover__arrow[data-hide]{visibility:hidden}.bpk-popover__body--padded{padding:1rem}.bpk-popover__header{display:flex;padding:1rem;justify-content:space-between;box-shadow:0 -1px 0 0 #e0e4e9 inset}.bpk-popover__label{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0 0 0 0)}.bpk-popover__footer{padding:.5rem 1rem;text-align:right;box-shadow:0 1px 0 0 #e0e4e9 inset}html[dir=rtl] .bpk-popover__footer{text-align:left}
@@ -0,0 +1,224 @@
1
+ import * as React from 'react';
2
+ import { useFloating, autoUpdate, offset, flip, useClick, useDismiss, useRole, useInteractions, useMergeRefs, FloatingPortal, FloatingFocusManager, arrow, FloatingArrow } from '@floating-ui/react';
3
+
4
+ // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`.
5
+ import { surfaceHighlightDay } from '@skyscanner/bpk-foundations-web/tokens/base.es6';
6
+
7
+ // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`.
8
+ import BpkCloseButton from "../../bpk-component-close-button";
9
+ // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`.
10
+ import { BpkButtonLink } from "../../bpk-component-link";
11
+ import BpkText, { TEXT_STYLES } from "../../bpk-component-text";
12
+ import { TransitionInitialMount, cssModules } from "../../bpk-react-utils";
13
+ import { ARROW_ID } from "./constants";
14
+ import STYLES from "./BpkPopover.module.css";
15
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
16
+ const getClassName = cssModules(STYLES);
17
+ export function usePopover({
18
+ arrowRef,
19
+ modal,
20
+ onOpenChange: setControlledOpen,
21
+ open: controlledOpen,
22
+ placement,
23
+ showArrow
24
+ } = {}) {
25
+ const open = controlledOpen;
26
+ const setOpen = setControlledOpen;
27
+ const data = useFloating({
28
+ placement,
29
+ open,
30
+ onOpenChange: setOpen,
31
+ whileElementsMounted: autoUpdate,
32
+ middleware: [showArrow && offset(14), arrow({
33
+ element: arrowRef
34
+ }), flip({
35
+ fallbackAxisSideDirection: 'start'
36
+ })]
37
+ });
38
+ const {
39
+ context
40
+ } = data;
41
+ const click = useClick(context, {
42
+ enabled: controlledOpen == null
43
+ });
44
+ const dismiss = useDismiss(context);
45
+ const role = useRole(context);
46
+ const interactions = useInteractions([click, dismiss, role]);
47
+ return React.useMemo(() => ({
48
+ open,
49
+ setOpen,
50
+ ...interactions,
51
+ ...data,
52
+ modal
53
+ }), [open, setOpen, interactions, data, modal]);
54
+ }
55
+ const PopoverContext = /*#__PURE__*/React.createContext(null);
56
+ export const usePopoverContext = () => {
57
+ const context = React.useContext(PopoverContext);
58
+ if (context == null) {
59
+ throw new Error('Popover components must be wrapped in <Popover />');
60
+ }
61
+ return context;
62
+ };
63
+ export function Popover({
64
+ children,
65
+ modal = false,
66
+ ...restOptions
67
+ }) {
68
+ // This can accept any props as options, e.g. `placement`,
69
+ // or other positioning options.
70
+ const popover = usePopover({
71
+ modal,
72
+ ...restOptions
73
+ });
74
+ return /*#__PURE__*/_jsx(PopoverContext.Provider, {
75
+ value: popover,
76
+ children: children
77
+ });
78
+ }
79
+ export const PopoverTrigger = /*#__PURE__*/React.forwardRef(({
80
+ asChild = false,
81
+ children,
82
+ ...props
83
+ }, propRef) => {
84
+ const context = usePopoverContext();
85
+ const childrenRef = children.ref;
86
+ const ref = useMergeRefs([context.refs.setReference, propRef, childrenRef]);
87
+
88
+ // `asChild` allows the user to pass any element as the anchor
89
+ if (asChild && /*#__PURE__*/React.isValidElement(children)) {
90
+ return /*#__PURE__*/React.cloneElement(children, context.getReferenceProps({
91
+ ref,
92
+ ...props,
93
+ ...children.props,
94
+ 'data-state': context.open ? 'open' : 'closed'
95
+ }));
96
+ }
97
+ return /*#__PURE__*/_jsx("div", {
98
+ ref: ref
99
+ // The user can style the trigger based on the state
100
+ ,
101
+ "data-state": context.open ? 'open' : 'closed',
102
+ ...context.getReferenceProps(props),
103
+ children: children
104
+ });
105
+ });
106
+ const EVENT_SOURCES = {
107
+ CLOSE_BUTTON: 'CLOSE_BUTTON',
108
+ CLOSE_LINK: 'CLOSE_LINK'
109
+ };
110
+ const bindEventSource = (source, callback) => event => {
111
+ if (event.persist) {
112
+ event.persist();
113
+ }
114
+ callback(event, {
115
+ source
116
+ });
117
+ };
118
+ export const PopoverContent = /*#__PURE__*/React.forwardRef(({
119
+ arrowRef,
120
+ children,
121
+ className = null,
122
+ closeButtonIcon = true,
123
+ closeButtonProps = {},
124
+ closeButtonText,
125
+ id,
126
+ isOpen = false,
127
+ label,
128
+ labelAsTitle = false,
129
+ onClose,
130
+ padded = true,
131
+ placement = 'bottom',
132
+ setState: setIsOpenState,
133
+ showArrow,
134
+ target,
135
+ ...rest
136
+ }, propRef) => {
137
+ // const [isOpenState, setIsOpenState] = React.useState(isOpen);
138
+
139
+ const {
140
+ context: floatingContext,
141
+ ...context
142
+ } = usePopoverContext();
143
+ const ref = useMergeRefs([context.refs.setFloating, propRef]);
144
+ const classNames = getClassName('bpk-popover', className);
145
+ const bodyClassNames = getClassName(padded && 'bpk-popover__body--padded');
146
+ const labelId = `bpk-popover-label-${id}`;
147
+ if (!floatingContext.open) return null;
148
+ return /*#__PURE__*/_jsx(FloatingPortal, {
149
+ children: /*#__PURE__*/_jsx(FloatingFocusManager, {
150
+ context: floatingContext,
151
+ modal: context.modal,
152
+ children: /*#__PURE__*/_jsx("div", {
153
+ ref: ref,
154
+ style: {
155
+ ...context.floatingStyles
156
+ },
157
+ ...context.getFloatingProps(),
158
+ children: /*#__PURE__*/_jsx(TransitionInitialMount, {
159
+ appearClassName: getClassName('bpk-popover--appear'),
160
+ appearActiveClassName: getClassName('bpk-popover--appear-active'),
161
+ transitionTimeout: 200,
162
+ children: /*#__PURE__*/_jsxs("section", {
163
+ id: id,
164
+ tabIndex: -1,
165
+ role: "dialog",
166
+ "aria-labelledby": labelId,
167
+ className: classNames,
168
+ ...rest,
169
+ children: [showArrow && /*#__PURE__*/_jsx(FloatingArrow, {
170
+ ref: arrowRef,
171
+ context: floatingContext,
172
+ id: ARROW_ID
173
+ // eslint-disable-next-line @skyscanner/rules/forbid-component-props
174
+ ,
175
+ className: getClassName('bpk-popover__arrow'),
176
+ role: "presentation",
177
+ stroke: surfaceHighlightDay,
178
+ strokeWidth: 0.0625
179
+ }), labelAsTitle ? /*#__PURE__*/_jsxs("header", {
180
+ className: getClassName('bpk-popover__header'),
181
+ children: [/*#__PURE__*/_jsx(BpkText, {
182
+ tagName: "h2",
183
+ id: labelId,
184
+ textStyle: TEXT_STYLES.label1,
185
+ children: label
186
+ }), "\xA0", closeButtonIcon ? /*#__PURE__*/_jsx(BpkCloseButton, {
187
+ label: closeButtonText,
188
+ onClick: () => {
189
+ bindEventSource(EVENT_SOURCES.CLOSE_BUTTON, onClose);
190
+ setIsOpenState(false);
191
+ },
192
+ ...closeButtonProps
193
+ }) : /*#__PURE__*/_jsx(BpkButtonLink, {
194
+ onClick: () => {
195
+ bindEventSource(EVENT_SOURCES.CLOSE_LINK, onClose);
196
+ setIsOpenState(false);
197
+ },
198
+ ...closeButtonProps,
199
+ children: closeButtonText
200
+ })]
201
+ }) : /*#__PURE__*/_jsx("span", {
202
+ id: labelId,
203
+ className: getClassName('bpk-popover__label'),
204
+ children: label
205
+ }), /*#__PURE__*/_jsx("div", {
206
+ className: bodyClassNames,
207
+ children: children
208
+ }), !labelAsTitle && /*#__PURE__*/_jsx("footer", {
209
+ className: getClassName('bpk-popover__footer'),
210
+ children: /*#__PURE__*/_jsx(BpkButtonLink, {
211
+ onClick: () => {
212
+ bindEventSource(EVENT_SOURCES.CLOSE_LINK, onClose);
213
+ setIsOpenState(false);
214
+ },
215
+ ...closeButtonProps,
216
+ children: closeButtonText
217
+ })
218
+ })]
219
+ })
220
+ })
221
+ })
222
+ })
223
+ });
224
+ });
@@ -14,5 +14,8 @@
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */import { themeAttributes as linkAttributes } from "../../bpk-component-link";
17
+ */
18
+
19
+ // @ts-expect-error Untyped import. See `decisions/imports-ts-suppressions.md`.
20
+ import { themeAttributes as linkAttributes } from "../../bpk-component-link";
18
21
  export default [...linkAttributes];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyscanner/backpack-web",
3
- "version": "34.0.0-premajor",
3
+ "version": "34.0.0-premajor.2",
4
4
  "description": "Backpack Design System web library",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,7 +22,6 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@floating-ui/dom": "^1.6.3",
26
25
  "@floating-ui/react": "^0.26.12",
27
26
  "@popperjs/core": "^2.11.8",
28
27
  "@react-google-maps/api": "^2.12.0",
@@ -1,222 +0,0 @@
1
- /*
2
- * Backpack - Skyscanner's Design System
3
- *
4
- * Copyright 2016 Skyscanner Ltd
5
- *
6
- * Licensed under the Apache License, Version 2.0 (the "License");
7
- * you may not use this file except in compliance with the License.
8
- * You may obtain a copy of the License at
9
- *
10
- * http://www.apache.org/licenses/LICENSE-2.0
11
- *
12
- * Unless required by applicable law or agreed to in writing, software
13
- * distributed under the License is distributed on an "AS IS" BASIS,
14
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- * See the License for the specific language governing permissions and
16
- * limitations under the License.
17
- */import PropTypes from 'prop-types';
18
- import { Component } from 'react';
19
- import { createPopper } from '@popperjs/core';
20
- import focusStore from 'a11y-focus-store';
21
- import { Portal, cssModules } from "../../bpk-react-utils";
22
- import BpkPopover from "./BpkPopover";
23
- import keyboardFocusScope from "./keyboardFocusScope";
24
- import STYLES from "./BpkPopover.module.css";
25
- import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
26
- const getClassName = cssModules(STYLES);
27
- class BpkPopoverPortal extends Component {
28
- constructor() {
29
- super();
30
- this.popper = null;
31
- this.previousTargetElement = null;
32
- }
33
-
34
- // onRender = (popoverElement: HTMLElement, targetElement: ?HTMLElement) => {
35
- // this.position(popoverElement, targetElement);
36
- // };
37
-
38
- // The order of events here is as follows:
39
- // - `onClose` is called by `Portal`
40
- // - The consumer changes `isOpen` to `false`
41
- // - `beforeClose` is called by `Portal`
42
- // - `beforeClose` calls the `done` callback which closes the `Portal`
43
-
44
- // `onClose` is called by the `Portal` to inform the consumer that `isOpen` should be made false.
45
- // Before we pass this information to the consumer, we want to note if restoring focus should be suppressed
46
- // onClose = (event: Object, information: { source: string }) => {
47
- // // If the user has clicked outside the popover then we don't want focus to be restored
48
- // // otherwise it will be stolen back from the element they clicked on.
49
- // // Here we suppress restoring focus before the consumer is told about the close and updates state.
50
- // this.suppressRestoreFocus = information.source === 'DOCUMENT_CLICK';
51
-
52
- // if (this.props.onClose) {
53
- // this.props.onClose(event, information);
54
- // }
55
- // };
56
-
57
- // `beforeClose` is called by the `Portal` when `isOpen` is changed to false.
58
- // As a result, `onClose` is called first, followed by `beforeClose`.
59
- // beforeClose = (done: () => void) => {
60
- // if (this.popper) {
61
- // this.popper.destroy();
62
- // this.popper = null;
63
- // this.previousTargetElement = null;
64
- // }
65
-
66
- // keyboardFocusScope.unscopeFocus();
67
- // if (!this.suppressRestoreFocus) {
68
- // focusStore.restoreFocus();
69
- // this.suppressRestoreFocus = false;
70
- // }
71
-
72
- // done();
73
- // };
74
-
75
- // position(popoverElement: HTMLElement, targetElement: ?HTMLElement) {
76
- // if (!targetElement) {
77
- // return;
78
- // }
79
-
80
- // const targetElementHasChanged =
81
- // targetElement !== this.previousTargetElement;
82
-
83
- // if (targetElementHasChanged && this.popper) {
84
- // this.popper.destroy();
85
- // this.popper = null;
86
- // }
87
-
88
- // // Custom modifier that makes the arrow display at the edge of the target.
89
- // const applyArrowHide = {
90
- // name: 'applyArrowHide',
91
- // enabled: true,
92
- // phase: 'write',
93
- // fn({ state }) {
94
- // const { arrow } = state.elements;
95
- // if (arrow) {
96
- // if (state.modifiersData.arrow.centerOffset !== 0) {
97
- // arrow.setAttribute('data-hide', '');
98
- // } else {
99
- // arrow.removeAttribute('data-hide');
100
- // }
101
- // }
102
- // },
103
- // };
104
-
105
- // // The default modifiers for the popper
106
- // // Note that GPU acceleration should be disabled otherwise Popper will use `translate3d`
107
- // // which can cause blurriness in Safari and Chrome.
108
- // const stdModifiers = [
109
- // {
110
- // name: 'computeStyles',
111
- // options: {
112
- // gpuAcceleration: false,
113
- // },
114
- // },
115
- // {
116
- // name: 'offset',
117
- // options: {
118
- // offset: [0, 17],
119
- // },
120
- // },
121
- // applyArrowHide,
122
- // ];
123
-
124
- // if (!this.popper) {
125
- // this.popper = createPopper(targetElement, popoverElement, {
126
- // placement: this.props.placement,
127
- // onFirstUpdate: () => {
128
- // if (targetElement) {
129
- // targetElement.focus();
130
- // }
131
- // focusStore.storeFocus();
132
- // keyboardFocusScope.scopeFocus(popoverElement);
133
- // },
134
- // modifiers: this.props.popperModifiers
135
- // ? [...this.props.popperModifiers, ...stdModifiers]
136
- // : stdModifiers,
137
- // });
138
- // }
139
-
140
- // this.previousTargetElement = targetElement;
141
- // if (this.popper) {
142
- // this.popper.update();
143
- // }
144
- // }
145
-
146
- render() {
147
- const {
148
- // isOpen,
149
- // onClose,
150
- // placement,
151
- // popperModifiers,
152
- portalClassName,
153
- // portalStyle,
154
- // renderTarget,
155
- // target,
156
- ...rest
157
- } = this.props;
158
- const classNames = getClassName('bpk-popover-portal', portalClassName);
159
-
160
- // if (portalClassName) {
161
- // classNames.push(portalClassName);
162
- // }
163
-
164
- return /*#__PURE__*/_jsx(_Fragment, {
165
- children: /*#__PURE__*/_jsx(BpkPopover, {
166
- className: classNames,
167
- onClose: this.onClose,
168
- ...rest
169
- })
170
- });
171
- }
172
- }
173
- export const propTypes = {
174
- // BpkPopover props - when migrating the popover to TS, we can import the type from BpkPopover
175
- children: PropTypes.node.isRequired,
176
- closeButtonText: PropTypes.string.isRequired,
177
- id: PropTypes.string.isRequired,
178
- label: PropTypes.string.isRequired,
179
- onClose: PropTypes.func.isRequired,
180
- className: PropTypes.string,
181
- closeButtonIcon: PropTypes.bool,
182
- closeButtonProps: PropTypes.object,
183
- labelAsTitle: PropTypes.bool,
184
- padded: PropTypes.bool,
185
- // BpkPopoverPortal additional props
186
- /**
187
- * In order to attach the popover to a regular DOM element, provide a function which returns it to `target`.
188
- * `target` can be a DOM element with a `ref` attached to it or a function that returns a DOM element.
189
- */
190
- target: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
191
- isOpen: PropTypes.bool.isRequired,
192
- placement: PropTypes.oneOf(['top', 'right', 'bottom', 'left']),
193
- portalStyle: PropTypes.object,
194
- portalClassName: PropTypes.string,
195
- renderTarget: PropTypes.func,
196
- /**
197
- * Please refer to the [documentation](https://popper.js.org/docs/v2/modifiers/) for the underlying positioning library "Popper.js".
198
- * You can achieve various behaviours such as allowing the popover to overflow the viewport etc.
199
- */
200
- popperModifiers: PropTypes.arrayOf(PropTypes.object)
201
- };
202
- export const defaultProps = {
203
- // BpkPopover props - when migrating the popover to TS, we can import the type from BpkPopover
204
- className: null,
205
- closeButtonIcon: true,
206
- closeButtonProps: null,
207
- labelAsTitle: false,
208
- padded: true,
209
- // BpkPopoverPortal additional props
210
- placement: 'bottom',
211
- portalStyle: null,
212
- portalClassName: null,
213
- renderTarget: null,
214
- popperModifiers: null
215
- };
216
- BpkPopoverPortal.propTypes = {
217
- ...propTypes
218
- };
219
- BpkPopoverPortal.defaultProps = {
220
- ...defaultProps
221
- };
222
- export default BpkPopoverPortal;
@@ -1,76 +0,0 @@
1
- /*
2
- * Backpack - Skyscanner's Design System
3
- *
4
- * Copyright 2016 Skyscanner Ltd
5
- *
6
- * Licensed under the Apache License, Version 2.0 (the "License");
7
- * you may not use this file except in compliance with the License.
8
- * You may obtain a copy of the License at
9
- *
10
- * http://www.apache.org/licenses/LICENSE-2.0
11
- *
12
- * Unless required by applicable law or agreed to in writing, software
13
- * distributed under the License is distributed on an "AS IS" BASIS,
14
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- * See the License for the specific language governing permissions and
16
- * limitations under the License.
17
- */
18
-
19
- // This code is based on that from `a11y-focus-scope`.
20
- // Instead of completely preventing focus leaving the target element,
21
- // it only prevents it leaving due to keyboard events.
22
- // Clicks outside the target element will move focus as normal.
23
-
24
- import focusin from 'focusin';
25
- import tabbable from 'tabbable';
26
- let polyfilled = false;
27
- let focusTrapped = false;
28
- const init = element => {
29
- // lazily polyfill focusin for firefox
30
- if (!polyfilled) {
31
- focusin.polyfill();
32
- polyfilled = true;
33
- }
34
- const focus = () => {
35
- (tabbable(element)[0] || element).focus();
36
- };
37
- const enableFocusTrapping = () => {
38
- focusTrapped = true;
39
- };
40
- const disableFocusTrapping = () => {
41
- focusTrapped = false;
42
- };
43
- const checkFocus = event => {
44
- if (!focusTrapped) {
45
- return;
46
- }
47
- if (element !== event.target && !element.contains(event.target)) {
48
- focus();
49
- }
50
- };
51
- focus();
52
-
53
- // As we only want to trap focus for keyboard navigation, we enable focus trapping on keydown and disable it again on keyup.
54
- // This means that focus is trapped when pressing the tab key, but not trapped for other navigation events such as mouse clicks.
55
- document.addEventListener('keydown', enableFocusTrapping);
56
- document.addEventListener('focusin', checkFocus);
57
- document.addEventListener('keyup', disableFocusTrapping);
58
- return () => {
59
- document.removeEventListener('keydown', enableFocusTrapping);
60
- document.removeEventListener('focusin', checkFocus);
61
- document.removeEventListener('keyup', disableFocusTrapping);
62
- };
63
- };
64
- let teardownFn;
65
- const scopeFocus = element => {
66
- if (teardownFn) teardownFn();
67
- teardownFn = init(element);
68
- };
69
- const unscopeFocus = () => {
70
- if (teardownFn) teardownFn();
71
- teardownFn = null;
72
- };
73
- export default {
74
- scopeFocus,
75
- unscopeFocus
76
- };