From 6539c5f96a8a37a848f41b83d8c253ada775a00f Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Fri, 10 Sep 2021 16:18:52 +0300 Subject: [PATCH 01/17] Implement first working draft --- package.json | 2 + src/components/Flyout2/Example.md | 319 ++++++++++++++++++++++++++++++ src/components/Flyout2/index.tsx | 225 +++++++++++++++++++++ src/components/index.ts | 1 + 4 files changed, 547 insertions(+) create mode 100644 src/components/Flyout2/Example.md create mode 100644 src/components/Flyout2/index.tsx diff --git a/package.json b/package.json index 6c371f3c..24c241ac 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,12 @@ } }, "dependencies": { + "@popperjs/core": "^2.10.1", "date-fns": "^2.3.0", "memoize-one": "^5.1.0", "react-datepicker": "^4.2.1", "react-onclickoutside": "^6.7.1", + "react-popper": "^2.2.5", "typescript-plugin-inner-jsx": "^0.1.9" }, "devDependencies": { diff --git a/src/components/Flyout2/Example.md b/src/components/Flyout2/Example.md new file mode 100644 index 00000000..9394e95f --- /dev/null +++ b/src/components/Flyout2/Example.md @@ -0,0 +1,319 @@ +**Elementary** + +We can use the (default) auto placement to just show a simple flyout on its ideal position. The component supports two modes - managed and controlled. By default, the managed mode is selected. + +```jsx +const { Flyout2 } = require('precise-ui'); + + + Wrapped element + +``` + +**Controlled Mode** + +Flyout different placements example in controlled mode. The controlled mode is triggered by explicitly setting the `open` prop. + +```jsx +const { Tag, Flyout2 } = require('precise-ui'); + +
+ + Element + + + + Element + + + + Element + + + + Element + +
+``` + +In this example a combination of `Button` and `Flyout` is used. + +```jsx +const { Flyout2, Button } = require('precise-ui'); + +const positions = ['top', 'right', 'bottom', 'left', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'right-top', 'right-bottom', 'left-top', 'left-bottom']; + +class RandomFlyout extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + }; + + this.change = e => this.setState({ + open: !this.state.open, + }); + } + + render() { + const { open } = this.state; + return ( +
+ +

Note

+ Flyout appears in random position +
+ }> + + + + ); + } +} + + +``` + +**Interactive List** + +Example of `InteractiveList` component usage together with flyout. + +```jsx +const { Flyout2, Button, InteractiveList } = require('precise-ui'); + +const CustomWrapper = function (props) { + const { children, ...rest } = props; + return
{children}
; +}; + +class ListFlyout extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + }; + + this.change = e => this.setState({ + open: !this.state.open, + }); + } + + render() { + const { open } = this.state; + return ( +
+ + } + position="right-top"> + + +
+ ); + } + +} + + +``` + +Likewise the next example. + +```jsx +const { Flyout2, Button, Avatar, InteractiveList } = require('precise-ui'); + +const CustomWrapper = function (props) { + const { children, ...rest } = props; + return
{children}
; +}; + +class ListFlyout extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + }; + + this.change = e => this.setState({ + open: !this.state.open, + }); + } + + render() { + const { open } = this.state; + return ( +
+ + } + position="right-top"> + + +
+ ); + } + +} + + +``` + +**Playground** + +A little playground for the various options. + +```jsx +const { Flyout2, RadioButton, StackPanel, StackItem } = require('precise-ui'); + +const positions = [ + 'top', + 'right', + 'bottom', + 'left', + 'top-left', + 'top-right', + 'bottom-left', + 'bottom-right', + 'right-top', + 'right-bottom', + 'left-top', + 'left-bottom', +]; + +class LongExample extends React.Component { + constructor(props) { + super(props); + this.state = { + open: false, + position: 'left', + contentSize: 'long', + positionType: 'position', + }; + } + + switch() { + this.setState({ + open: !this.state.open, + }); + } + + changeContentType(type) { + this.setState({ + contentSize: type, + }); + } + + changePositionType(positionType) { + this.setState({ + positionType + }); + } + + getShortContent() { + return
Flyout
; + } + + getMiddleContent() { + return ( +
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit
+
+ ); + } + + getLongContent() { + return ( +
+ {Array.from({ length: 100 }, (value, index) => ( +
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et + dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex + ea commodo consequat. +
+ ))} +
+ ); + } + + render() { + const { open } = this.state; + return ( +
+ + + + + + Position: +
+ {positions.map(position => ( +
+ this.setState({ position })}> + {position} + +
+ ))} +
+
+ + Content length: + {['short', 'middle', 'long'].map(size => ( +
+ this.changeContentType(size)}> + {size} + +
+ ))} +
+ + Position type: + {[{ + positionType: 'position', + description: 'enforces defined position', + }, { + positionType: 'defaultPosition', + description: 'flyout takes the position only if enough space', + }].map(({positionType, description}) => ( +
+ this.changePositionType(positionType)}> + {positionType} ({description}) + +
+ ))} +
+
+
+ ); + } +} + +; +``` diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx new file mode 100644 index 00000000..86a06208 --- /dev/null +++ b/src/components/Flyout2/index.tsx @@ -0,0 +1,225 @@ +import * as React from 'react'; +import { usePopper } from 'react-popper' +import { Placement } from '@popperjs/core' +import onClickOutside, { AdditionalProps } from 'react-onclickoutside'; +import styled, { css, themed } from '../../utils/styled'; +import { getFontStyle } from '../../textStyles'; +import { StandardProps } from '../../common'; +import { ResponsiveComponentProps } from '../../hoc/withResponsive'; +import { distance } from '../../distance'; +import { FlyoutPosition, FlyoutProps } from '../Flyout/Flyout.types.part'; +import { forwardRef } from 'react'; +const { useState, useEffect, useImperativeHandle } = React; + +const toolTipArrowSize = 18; + +const FlyoutContainer = styled.div` +z-index: 100; +position: relative; +display: inline-block; +width: fit-content; +`; + +export interface FlyoutWindowProps extends ResponsiveComponentProps, StandardProps { + targetRect: ClientRect; + children: React.ReactNode; + position?: FlyoutPosition; + defaultPosition?: FlyoutPosition; + offset?: number; +} + +export interface Position { + top?: number; + right?: number; + bottom?: number; + left?: number; +} + +const StyledTargetWrapper = styled.div``; + +interface StyledFlyoutArrowProps extends Position { + rotate?: number; + popperStyles: any; + show: boolean; +} +const StyledFlyoutArrow = styled('div')( + themed( + ({ theme, show }) => { + return css` + visibility: ${show ? 'visible' : 'hidden'}; + pointer-events: none; + position: absolute; + box-sizing: border-box; + z-index: 101; + width: ${toolTipArrowSize}px; + height: ${toolTipArrowSize}px; + + :before { + content: ' '; + position: absolute; + top: ${toolTipArrowSize - 1}px; + left: 0; + border-style: solid; + border-width: ${toolTipArrowSize / 2}px; + border-color: ${theme.ui4} transparent transparent transparent; + } + + :after { + content: ' '; + position: absolute; + top: ${toolTipArrowSize - 1}px; + left: 0; + border-style: solid; + border-width: ${toolTipArrowSize / 2 - 1}px; + margin-left: 1px; + border-color: ${theme.flyout.background} transparent transparent transparent; + } +`})); + +export interface StyledFlyoutWindowProps { + size?: { + width?: number; + height?: number; + }; + position?: Position; + noGutter?: boolean; + open?: boolean; + popperStyles: any; +} + +const StyledFlyoutWindow = styled('div')( + themed( + ({ theme, open, noGutter }) => css` + ${getFontStyle({ size: 'medium' })} + visibility: ${open ? 'visible' : 'hidden'}; + z-index: 100; + box-sizing: border-box; + box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); + border: 1px solid ${theme.ui4}; + background: ${theme.flyout.background}; + color: ${theme.flyout.textColor}; + max-width: ${theme.flyout.maxWidth}; + max-height: ${theme.flyout.maxHeight}; + ${!noGutter ? `padding: ${distance.small} ${distance.medium};` : ''} box-sizing: border-box; + overflow: auto; +`)); + +const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): Placement => { + if (position) { + return ({ + 'top': 'top', + 'right': 'right', + 'bottom': 'bottom', + 'left': 'left', + 'top-left': 'top-start', + 'top-right': 'top-end', + 'bottom-left': 'bottom-start', + 'bottom-right': 'bottom-end', + 'right-top': 'right-start', + 'right-bottom': 'right-end', + 'left-top': 'left-start', + 'left-bottom': 'left-end', + } as const)[position] + } + + return 'auto'; +} + +const FlyoutInt2: React.FC = forwardRef((props, ref) => { + const [controlled ] = useState(props.open !== undefined); + const [open, setOpenInt] = useState(Boolean(props.open)) + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); + const [arrowElement, setArrowElement] = useState(null); + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: mapFlyoutPositionToPopperPlacement(props.position), + modifiers: [ + { name: 'hide' }, + { name: 'arrow', options: { + element: arrowElement, + padding: 10 + } }, + { name: 'offset', options: { offset: [0,20]}} + ], + }); + + useImperativeHandle(ref, () => ({ + handleClickOutside: () => { + setOpen(false) + } + })); + + useEffect(() => setOpenInt(Boolean(props.open)), [props.open]) + const onClick = () => { + setOpen(!open); + } + + const setOpen = (nextOpen: boolean) => { + if (controlled || nextOpen === open) { + return + } + typeof props.onChange === 'function' && props.onChange({ open: nextOpen }); + setOpenInt(nextOpen); + } + + const placement = ((attributes?.popper || {})['data-popper-placement'] ?? 'bottom').split('-')[0] + + const rotate = { + left: -90, + right: 90, + top: 0, + bottom: 180, + } [placement]; + + const { transform, ...arrowStylesWithoutTransform } = styles.arrow; + const popperArrowStyles: any = { + transform: transform + ` rotate(${rotate}deg)`, + } + switch (placement) { + case 'top': { + popperArrowStyles.bottom = '0px'; + break; + }; + case 'bottom': { + popperArrowStyles.top = '0px'; + break; + }; + case 'left': { + popperArrowStyles.right = '0px'; + break; + }; + case 'right': { + popperArrowStyles.left = '0px'; + break; + } + } + + const arrowStyles = {...arrowStylesWithoutTransform, ...popperArrowStyles} + + return ( + + {props.children} +
+ + {props.content} + + +
+
+ ); +}) + +class OutsideClickAdapter extends React.Component { + private ref = React.createRef() + + // @ts-ignore + handleClickOutside = () => this.ref?.current?.handleClickOutside() + + render() { + // @ts-ignore + return {this.props.children} + } +} + +export const Flyout2: React.ComponentClass = onClickOutside(OutsideClickAdapter); +Flyout2.displayName = 'Flyout2'; diff --git a/src/components/index.ts b/src/components/index.ts index 88b68fd0..cc252f99 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -33,6 +33,7 @@ export * from './FileSelect'; export * from './FileUploader'; export * from './FileUploaderDetails'; export * from './Flyout'; +export * from './Flyout2'; export * from './Form'; export * from './GradientContainer'; export * from './Grid'; From 82b1326898de6f09235a31bd3dccb5d9d7745ea7 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Fri, 10 Sep 2021 19:28:25 +0300 Subject: [PATCH 02/17] Clean up and refactor --- src/components/Flyout2/index.tsx | 175 ++++++++++++++----------------- 1 file changed, 81 insertions(+), 94 deletions(-) diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx index 86a06208..c5bef0b6 100644 --- a/src/components/Flyout2/index.tsx +++ b/src/components/Flyout2/index.tsx @@ -4,12 +4,9 @@ import { Placement } from '@popperjs/core' import onClickOutside, { AdditionalProps } from 'react-onclickoutside'; import styled, { css, themed } from '../../utils/styled'; import { getFontStyle } from '../../textStyles'; -import { StandardProps } from '../../common'; -import { ResponsiveComponentProps } from '../../hoc/withResponsive'; import { distance } from '../../distance'; import { FlyoutPosition, FlyoutProps } from '../Flyout/Flyout.types.part'; -import { forwardRef } from 'react'; -const { useState, useEffect, useImperativeHandle } = React; +const { useState, useEffect } = React; const toolTipArrowSize = 18; @@ -20,33 +17,12 @@ display: inline-block; width: fit-content; `; -export interface FlyoutWindowProps extends ResponsiveComponentProps, StandardProps { - targetRect: ClientRect; - children: React.ReactNode; - position?: FlyoutPosition; - defaultPosition?: FlyoutPosition; - offset?: number; -} - -export interface Position { - top?: number; - right?: number; - bottom?: number; - left?: number; -} - const StyledTargetWrapper = styled.div``; -interface StyledFlyoutArrowProps extends Position { - rotate?: number; - popperStyles: any; - show: boolean; -} -const StyledFlyoutArrow = styled('div')( - themed( - ({ theme, show }) => { +const StyledFlyoutArrow = styled('div')( + themed( + ({ theme }) => { return css` - visibility: ${show ? 'visible' : 'hidden'}; pointer-events: none; position: absolute; box-sizing: border-box; @@ -77,21 +53,15 @@ const StyledFlyoutArrow = styled('div')( `})); export interface StyledFlyoutWindowProps { - size?: { - width?: number; - height?: number; - }; - position?: Position; + visibility: boolean; noGutter?: boolean; - open?: boolean; - popperStyles: any; } const StyledFlyoutWindow = styled('div')( themed( - ({ theme, open, noGutter }) => css` + ({ theme, visibility, noGutter }) => css` ${getFontStyle({ size: 'medium' })} - visibility: ${open ? 'visible' : 'hidden'}; + visibility: ${visibility ? 'visible' : 'hidden'}; z-index: 100; box-sizing: border-box; box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); @@ -125,45 +95,8 @@ const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): Placemen return 'auto'; } -const FlyoutInt2: React.FC = forwardRef((props, ref) => { - const [controlled ] = useState(props.open !== undefined); - const [open, setOpenInt] = useState(Boolean(props.open)) - const [referenceElement, setReferenceElement] = useState(null); - const [popperElement, setPopperElement] = useState(null); - const [arrowElement, setArrowElement] = useState(null); - const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement: mapFlyoutPositionToPopperPlacement(props.position), - modifiers: [ - { name: 'hide' }, - { name: 'arrow', options: { - element: arrowElement, - padding: 10 - } }, - { name: 'offset', options: { offset: [0,20]}} - ], - }); - - useImperativeHandle(ref, () => ({ - handleClickOutside: () => { - setOpen(false) - } - })); - - useEffect(() => setOpenInt(Boolean(props.open)), [props.open]) - const onClick = () => { - setOpen(!open); - } - - const setOpen = (nextOpen: boolean) => { - if (controlled || nextOpen === open) { - return - } - typeof props.onChange === 'function' && props.onChange({ open: nextOpen }); - setOpenInt(nextOpen); - } - - const placement = ((attributes?.popper || {})['data-popper-placement'] ?? 'bottom').split('-')[0] - +const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties): React.CSSProperties => { + const placement = (popperAttributes['data-popper-placement'] ?? 'top').split('-')[0] const rotate = { left: -90, right: 90, @@ -171,55 +104,109 @@ const FlyoutInt2: React.FC = forwardRef((props, r bottom: 180, } [placement]; - const { transform, ...arrowStylesWithoutTransform } = styles.arrow; - const popperArrowStyles: any = { + const { transform, ...arrowStylesWithoutTransform } = arrowStyles; + const placementDependentStyles: React.CSSProperties = { transform: transform + ` rotate(${rotate}deg)`, } switch (placement) { case 'top': { - popperArrowStyles.bottom = '0px'; + placementDependentStyles.bottom = '0px'; break; }; case 'bottom': { - popperArrowStyles.top = '0px'; + placementDependentStyles.top = '0px'; break; }; case 'left': { - popperArrowStyles.right = '0px'; + placementDependentStyles.right = '0px'; break; }; case 'right': { - popperArrowStyles.left = '0px'; + placementDependentStyles.left = '0px'; break; } } - const arrowStyles = {...arrowStylesWithoutTransform, ...popperArrowStyles} + return {...arrowStylesWithoutTransform, ...placementDependentStyles} +} + +interface OutsideClickProps { + outsideClickEvent: React.SyntheticEvent | null; +} + +const FlyoutInt: React.FC = (props) => { + const [controlled ] = useState(props.open !== undefined); + const [visibility, setVisibility] = useState(Boolean(props.open)) + const [referenceElement, setReferenceElement] = useState(null); + const [popperElement, setPopperElement] = useState(null); + const [arrowElement, setArrowElement] = useState(null); + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: mapFlyoutPositionToPopperPlacement(props.position), + modifiers: [ + { name: 'hide' }, + { name: 'arrow', options: { + element: arrowElement, + padding: 10 + } }, + { name: 'offset', options: { offset: [0,20]}} + ], + }); + + useEffect(() => setVisibility(Boolean(props.open)), [props.open]) + useEffect(() => changeVisibility(false), [props.outsideClickEvent]) + + const onClick = () => changeVisibility(!visibility); + + const changeVisibility = (nextVisibility: boolean) => { + if (controlled || nextVisibility === visibility) { + return + } + typeof props.onChange === 'function' && props.onChange({ open: nextVisibility }); + setVisibility(nextVisibility); + } +FlyoutInt.displayName = 'FlyoutInt'; return ( {props.children}
- + {props.content} - +
); -}) +} -class OutsideClickAdapter extends React.Component { - private ref = React.createRef() +function withClickOutsideFunctionalAdapter

(Component: React.FC

) { + class OutsideClickAdapter extends React.Component { + static displayName: string; + constructor(props: P) { + super(props); + this.state = { + outsideClickEvent: null, + } + } - // @ts-ignore - handleClickOutside = () => this.ref?.current?.handleClickOutside() + handleClickOutside = (e: React.SyntheticEvent) => { + this.setState({ outsideClickEvent: e }) + } - render() { - // @ts-ignore - return {this.props.children} + render() { + const { children, ...otherProps } = this.props; + return {children} + } } + + OutsideClickAdapter.displayName = 'withClickOutsideWrapper'; + + return OutsideClickAdapter +} + +interface OutSideClickAdapterState { + outsideClickEvent: React.SyntheticEvent | null; } -export const Flyout2: React.ComponentClass = onClickOutside(OutsideClickAdapter); +export const Flyout2: React.ComponentClass = onClickOutside(withClickOutsideFunctionalAdapter(FlyoutInt)); Flyout2.displayName = 'Flyout2'; From 09a57a9edb7f49a3b71180541d2dae8c97e5c019 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Fri, 10 Sep 2021 23:16:25 +0300 Subject: [PATCH 03/17] Extract outside click HOC and helpers --- src/components/Flyout2/helpers.tsx | 49 +++++++ src/components/Flyout2/index.tsx | 218 +++++++++-------------------- src/hoc/index.ts | 1 + src/hoc/withClickOutsideFC.tsx | 41 ++++++ 4 files changed, 158 insertions(+), 151 deletions(-) create mode 100644 src/components/Flyout2/helpers.tsx create mode 100644 src/hoc/withClickOutsideFC.tsx diff --git a/src/components/Flyout2/helpers.tsx b/src/components/Flyout2/helpers.tsx new file mode 100644 index 00000000..c12ddd5b --- /dev/null +++ b/src/components/Flyout2/helpers.tsx @@ -0,0 +1,49 @@ +import { FlyoutPosition } from '../Flyout/Flyout.types.part'; +import { Placement } from '@popperjs/core' + +export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): Placement => { + if (position) { + return ({ + 'top': 'top', + 'right': 'right', + 'bottom': 'bottom', + 'left': 'left', + 'top-left': 'top-start', + 'top-right': 'top-end', + 'bottom-left': 'bottom-start', + 'bottom-right': 'bottom-end', + 'right-top': 'right-start', + 'right-bottom': 'right-end', + 'left-top': 'left-start', + 'left-bottom': 'left-end', + } as const)[position] + } + + return 'auto'; +} + +export const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties): React.CSSProperties => { + const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement).split('-')[0] + + const { transform, ...arrowStylesWithoutTransform } = arrowStyles; + const placementDependentStyles: React.CSSProperties = { + top: { + bottom: 0, + transform: transform + ` rotate(${0}deg)`, + }, + bottom: { + top: 0, + transform: transform + ` rotate(${180}deg)`, + }, + left: { + right: 0, + transform: transform + ` rotate(${-90}deg)`, + }, + right: { + left: 0, + transform: transform + ` rotate(${90}deg)`, + } + }[primaryPlacement as 'top' | 'bottom' | 'left' | 'right'] + + return {...arrowStylesWithoutTransform, ...placementDependentStyles} +} diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx index c5bef0b6..69fd42b7 100644 --- a/src/components/Flyout2/index.tsx +++ b/src/components/Flyout2/index.tsx @@ -1,140 +1,86 @@ import * as React from 'react'; import { usePopper } from 'react-popper' -import { Placement } from '@popperjs/core' -import onClickOutside, { AdditionalProps } from 'react-onclickoutside'; +import { withClickOutsideFC, WithClickOutsideFCProps } from '../../hoc/withClickOutsideFC'; import styled, { css, themed } from '../../utils/styled'; import { getFontStyle } from '../../textStyles'; import { distance } from '../../distance'; -import { FlyoutPosition, FlyoutProps } from '../Flyout/Flyout.types.part'; +import { FlyoutProps } from '../Flyout/Flyout.types.part'; +import { mapFlyoutPositionToPopperPlacement, calculateArrowStyleOverrides } from './helpers' const { useState, useEffect } = React; const toolTipArrowSize = 18; const FlyoutContainer = styled.div` -z-index: 100; -position: relative; -display: inline-block; -width: fit-content; + z-index: 100; + position: relative; + display: inline-block; + width: fit-content; `; +FlyoutContainer.displayName = 'FlyoutContainer'; -const StyledTargetWrapper = styled.div``; +const FlyoutTargetWrapper = styled.div``; +FlyoutTargetWrapper.displayName = 'FlyoutTargetWrapper'; -const StyledFlyoutArrow = styled('div')( +const FlyoutArrow = styled('div')( themed( ({ theme }) => { return css` - pointer-events: none; - position: absolute; - box-sizing: border-box; - z-index: 101; - width: ${toolTipArrowSize}px; - height: ${toolTipArrowSize}px; - - :before { - content: ' '; + pointer-events: none; position: absolute; - top: ${toolTipArrowSize - 1}px; - left: 0; - border-style: solid; - border-width: ${toolTipArrowSize / 2}px; - border-color: ${theme.ui4} transparent transparent transparent; - } - - :after { - content: ' '; - position: absolute; - top: ${toolTipArrowSize - 1}px; - left: 0; - border-style: solid; - border-width: ${toolTipArrowSize / 2 - 1}px; - margin-left: 1px; - border-color: ${theme.flyout.background} transparent transparent transparent; - } -`})); - -export interface StyledFlyoutWindowProps { + box-sizing: border-box; + z-index: 101; + width: ${toolTipArrowSize}px; + height: ${toolTipArrowSize}px; + + :before { + content: ' '; + position: absolute; + top: ${toolTipArrowSize - 1}px; + left: 0; + border-style: solid; + border-width: ${toolTipArrowSize / 2}px; + border-color: ${theme.ui4} transparent transparent transparent; + } + + :after { + content: ' '; + position: absolute; + top: ${toolTipArrowSize - 1}px; + left: 0; + border-style: solid; + border-width: ${toolTipArrowSize / 2 - 1}px; + margin-left: 1px; + border-color: ${theme.flyout.background} transparent transparent transparent; + } + `}) +); +FlyoutArrow.displayName = 'FlyoutArrow'; + +interface FlyoutBodyProps { visibility: boolean; noGutter?: boolean; } -const StyledFlyoutWindow = styled('div')( - themed( +const FlyoutBody = styled('div')( + themed( ({ theme, visibility, noGutter }) => css` - ${getFontStyle({ size: 'medium' })} - visibility: ${visibility ? 'visible' : 'hidden'}; - z-index: 100; - box-sizing: border-box; - box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); - border: 1px solid ${theme.ui4}; - background: ${theme.flyout.background}; - color: ${theme.flyout.textColor}; - max-width: ${theme.flyout.maxWidth}; - max-height: ${theme.flyout.maxHeight}; - ${!noGutter ? `padding: ${distance.small} ${distance.medium};` : ''} box-sizing: border-box; - overflow: auto; -`)); - -const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): Placement => { - if (position) { - return ({ - 'top': 'top', - 'right': 'right', - 'bottom': 'bottom', - 'left': 'left', - 'top-left': 'top-start', - 'top-right': 'top-end', - 'bottom-left': 'bottom-start', - 'bottom-right': 'bottom-end', - 'right-top': 'right-start', - 'right-bottom': 'right-end', - 'left-top': 'left-start', - 'left-bottom': 'left-end', - } as const)[position] - } - - return 'auto'; -} - -const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties): React.CSSProperties => { - const placement = (popperAttributes['data-popper-placement'] ?? 'top').split('-')[0] - const rotate = { - left: -90, - right: 90, - top: 0, - bottom: 180, - } [placement]; - - const { transform, ...arrowStylesWithoutTransform } = arrowStyles; - const placementDependentStyles: React.CSSProperties = { - transform: transform + ` rotate(${rotate}deg)`, - } - switch (placement) { - case 'top': { - placementDependentStyles.bottom = '0px'; - break; - }; - case 'bottom': { - placementDependentStyles.top = '0px'; - break; - }; - case 'left': { - placementDependentStyles.right = '0px'; - break; - }; - case 'right': { - placementDependentStyles.left = '0px'; - break; - } - } - - return {...arrowStylesWithoutTransform, ...placementDependentStyles} -} - -interface OutsideClickProps { - outsideClickEvent: React.SyntheticEvent | null; -} - -const FlyoutInt: React.FC = (props) => { + ${getFontStyle({ size: 'medium' })} + visibility: ${visibility ? 'visible' : 'hidden'}; + z-index: 100; + box-sizing: border-box; + box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); + border: 1px solid ${theme.ui4}; + background: ${theme.flyout.background}; + color: ${theme.flyout.textColor}; + max-width: ${theme.flyout.maxWidth}; + max-height: ${theme.flyout.maxHeight}; + ${!noGutter ? `padding: ${distance.small} ${distance.medium};` : ''} box-sizing: border-box; + overflow: auto; + `) +); +FlyoutBody.displayName = 'FlyoutBody'; + +const FlyoutInt: React.FC = (props) => { const [controlled ] = useState(props.open !== undefined); const [visibility, setVisibility] = useState(Boolean(props.open)) const [referenceElement, setReferenceElement] = useState(null); @@ -146,7 +92,6 @@ const FlyoutInt: React.FC = ( { name: 'hide' }, { name: 'arrow', options: { element: arrowElement, - padding: 10 } }, { name: 'offset', options: { offset: [0,20]}} ], @@ -164,49 +109,20 @@ const FlyoutInt: React.FC = ( typeof props.onChange === 'function' && props.onChange({ open: nextVisibility }); setVisibility(nextVisibility); } -FlyoutInt.displayName = 'FlyoutInt'; return ( - {props.children} + {props.children}

- + {props.content} - - + +
); } +FlyoutInt.displayName = 'FlyoutInt'; -function withClickOutsideFunctionalAdapter

(Component: React.FC

) { - class OutsideClickAdapter extends React.Component { - static displayName: string; - constructor(props: P) { - super(props); - this.state = { - outsideClickEvent: null, - } - } - - handleClickOutside = (e: React.SyntheticEvent) => { - this.setState({ outsideClickEvent: e }) - } - - render() { - const { children, ...otherProps } = this.props; - return {children} - } - } - - OutsideClickAdapter.displayName = 'withClickOutsideWrapper'; - - return OutsideClickAdapter -} - -interface OutSideClickAdapterState { - outsideClickEvent: React.SyntheticEvent | null; -} - -export const Flyout2: React.ComponentClass = onClickOutside(withClickOutsideFunctionalAdapter(FlyoutInt)); +export const Flyout2 = withClickOutsideFC(FlyoutInt); Flyout2.displayName = 'Flyout2'; diff --git a/src/hoc/index.ts b/src/hoc/index.ts index 8b4b71c1..e20ebff5 100644 --- a/src/hoc/index.ts +++ b/src/hoc/index.ts @@ -3,3 +3,4 @@ export * from './withResponsiveMode'; export * from './withValidation'; export * from './withOutsideClick'; export * from './withFormContext'; +export * from './withClickOutsideFC'; diff --git a/src/hoc/withClickOutsideFC.tsx b/src/hoc/withClickOutsideFC.tsx new file mode 100644 index 00000000..744254ac --- /dev/null +++ b/src/hoc/withClickOutsideFC.tsx @@ -0,0 +1,41 @@ +// This HOC is an adapter allowing functional components to use 'react-clickoutside' package. +// The problem with 'react-clickoutside' is that it only supports class components, and +// the workaround, described in its README does not always work and is ugly. +// This HOC hides imperative nature of 'react-onclickoutside' and notifies the wrapped component +// about outside clicks by passing it the click event as a prop. As each event object is unique, +// the wrapped component may react on clicks with the help of 'useEffect' hook set up to listen +// changes of `props.outsideClickEvent`. + +import onClickOutside, { AdditionalProps } from 'react-onclickoutside'; +import * as React from 'react'; + +interface OutsideClickProps { + outsideClickEvent: React.SyntheticEvent | null; +} + +export function withClickOutsideFC

(Component: React.FC

) { + class OutsideClickAdapter extends React.Component { + static displayName: string; + constructor(props: P) { + super(props); + this.state = { + outsideClickEvent: null, + } + } + + handleClickOutside = (e: React.SyntheticEvent) => { + this.setState({ outsideClickEvent: e }) + } + + render() { + const { children, ...otherProps } = this.props; + return {children} + } + } + + OutsideClickAdapter.displayName = 'withClickOutsideWrapper'; + + return onClickOutside(OutsideClickAdapter) +} + +export interface WithClickOutsideFCProps extends AdditionalProps, OutsideClickProps {}; From 2af75a9b097769d32db60f50d93c0304635b33ed Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Fri, 10 Sep 2021 23:43:36 +0300 Subject: [PATCH 04/17] Fix z-order stacking issues --- src/components/Flyout2/helpers.tsx | 6 +++--- src/components/Flyout2/index.tsx | 27 ++++++++++++--------------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/components/Flyout2/helpers.tsx b/src/components/Flyout2/helpers.tsx index c12ddd5b..d2a4d3f8 100644 --- a/src/components/Flyout2/helpers.tsx +++ b/src/components/Flyout2/helpers.tsx @@ -22,10 +22,10 @@ export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): P return 'auto'; } -export const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties): React.CSSProperties => { - const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement).split('-')[0] +export const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties = {}): React.CSSProperties => { + const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement ?? 'top').split('-')[0] - const { transform, ...arrowStylesWithoutTransform } = arrowStyles; + const { transform = '', ...arrowStylesWithoutTransform } = arrowStyles; const placementDependentStyles: React.CSSProperties = { top: { bottom: 0, diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx index 69fd42b7..1146c5d6 100644 --- a/src/components/Flyout2/index.tsx +++ b/src/components/Flyout2/index.tsx @@ -11,7 +11,6 @@ const { useState, useEffect } = React; const toolTipArrowSize = 18; const FlyoutContainer = styled.div` - z-index: 100; position: relative; display: inline-block; width: fit-content; @@ -57,15 +56,13 @@ const FlyoutArrow = styled('div')( FlyoutArrow.displayName = 'FlyoutArrow'; interface FlyoutBodyProps { - visibility: boolean; noGutter?: boolean; } const FlyoutBody = styled('div')( themed( - ({ theme, visibility, noGutter }) => css` + ({ theme, noGutter }) => css` ${getFontStyle({ size: 'medium' })} - visibility: ${visibility ? 'visible' : 'hidden'}; z-index: 100; box-sizing: border-box; box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); @@ -82,7 +79,7 @@ FlyoutBody.displayName = 'FlyoutBody'; const FlyoutInt: React.FC = (props) => { const [controlled ] = useState(props.open !== undefined); - const [visibility, setVisibility] = useState(Boolean(props.open)) + const [visible, setVisible] = useState(Boolean(props.open)) const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const [arrowElement, setArrowElement] = useState(null); @@ -97,28 +94,28 @@ const FlyoutInt: React.FC = (props) => { ], }); - useEffect(() => setVisibility(Boolean(props.open)), [props.open]) + useEffect(() => setVisible(Boolean(props.open)), [props.open]) useEffect(() => changeVisibility(false), [props.outsideClickEvent]) - const onClick = () => changeVisibility(!visibility); + const onClick = () => changeVisibility(!visible); const changeVisibility = (nextVisibility: boolean) => { - if (controlled || nextVisibility === visibility) { + if (controlled || nextVisibility === visible) { return } typeof props.onChange === 'function' && props.onChange({ open: nextVisibility }); - setVisibility(nextVisibility); + setVisible(nextVisibility); } return ( {props.children} -

- - {props.content} - - -
+ {visible && props.content && + + {props.content} + + + } ); } From d2eb1c5894888807114a0fc2828fea8f5a121a30 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Fri, 10 Sep 2021 23:50:46 +0300 Subject: [PATCH 05/17] Move Flyout arrow size param to the theme --- src/common.ts | 4 ++++ src/components/Flyout2/index.tsx | 14 ++++++-------- src/themes.ts | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/common.ts b/src/common.ts index b413fdfa..1b2fc488 100644 --- a/src/common.ts +++ b/src/common.ts @@ -80,6 +80,10 @@ export interface FlyoutStyling { * Flyout text font size. */ fontSize: string; + /** + * Flyout arrow size + */ + arrowSize: number; } export interface ButtonThemeSettings { diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx index 1146c5d6..ebf468ec 100644 --- a/src/components/Flyout2/index.tsx +++ b/src/components/Flyout2/index.tsx @@ -8,8 +8,6 @@ import { FlyoutProps } from '../Flyout/Flyout.types.part'; import { mapFlyoutPositionToPopperPlacement, calculateArrowStyleOverrides } from './helpers' const { useState, useEffect } = React; -const toolTipArrowSize = 18; - const FlyoutContainer = styled.div` position: relative; display: inline-block; @@ -28,26 +26,26 @@ const FlyoutArrow = styled('div')( position: absolute; box-sizing: border-box; z-index: 101; - width: ${toolTipArrowSize}px; - height: ${toolTipArrowSize}px; + width: ${theme.flyout.arrowSize}px; + height: ${theme.flyout.arrowSize}px; :before { content: ' '; position: absolute; - top: ${toolTipArrowSize - 1}px; + top: ${theme.flyout.arrowSize - 1}px; left: 0; border-style: solid; - border-width: ${toolTipArrowSize / 2}px; + border-width: ${theme.flyout.arrowSize / 2}px; border-color: ${theme.ui4} transparent transparent transparent; } :after { content: ' '; position: absolute; - top: ${toolTipArrowSize - 1}px; + top: ${theme.flyout.arrowSize - 1}px; left: 0; border-style: solid; - border-width: ${toolTipArrowSize / 2 - 1}px; + border-width: ${theme.flyout.arrowSize / 2 - 1}px; margin-left: 1px; border-color: ${theme.flyout.background} transparent transparent transparent; } diff --git a/src/themes.ts b/src/themes.ts index 864e816c..8f06a2ff 100644 --- a/src/themes.ts +++ b/src/themes.ts @@ -77,6 +77,7 @@ export const flyout: FlyoutStyling = { background: colors.white, textColor: colors.black, fontSize: remCalc('16px'), + arrowSize: 18, }; export const metroInfoTile: MetroInfoTileStyling = { From b6069bd6a921ec72f88d7b711d4f6eff738dfc01 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Sat, 11 Sep 2021 13:01:45 +0300 Subject: [PATCH 06/17] Improve compatibility with the original Flyout --- src/components/Flyout2/helpers.tsx | 9 ++- src/components/Flyout2/index.tsx | 106 +++++++++++++++++------------ 2 files changed, 71 insertions(+), 44 deletions(-) diff --git a/src/components/Flyout2/helpers.tsx b/src/components/Flyout2/helpers.tsx index d2a4d3f8..d882bfd6 100644 --- a/src/components/Flyout2/helpers.tsx +++ b/src/components/Flyout2/helpers.tsx @@ -23,9 +23,14 @@ export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): P } export const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties = {}): React.CSSProperties => { - const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement ?? 'top').split('-')[0] + const { transform, ...arrowStylesWithoutTransform } = arrowStyles; + + if (!transform || !popperAttributes['data-popper-placement']) { + return arrowStyles; + } + + const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement).split('-')[0] - const { transform = '', ...arrowStylesWithoutTransform } = arrowStyles; const placementDependentStyles: React.CSSProperties = { top: { bottom: 0, diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx index ebf468ec..0ab84f70 100644 --- a/src/components/Flyout2/index.tsx +++ b/src/components/Flyout2/index.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { usePopper } from 'react-popper' +import { usePopper, Modifier } from 'react-popper' import { withClickOutsideFC, WithClickOutsideFCProps } from '../../hoc/withClickOutsideFC'; import styled, { css, themed } from '../../utils/styled'; import { getFontStyle } from '../../textStyles'; @@ -7,6 +7,8 @@ import { distance } from '../../distance'; import { FlyoutProps } from '../Flyout/Flyout.types.part'; import { mapFlyoutPositionToPopperPlacement, calculateArrowStyleOverrides } from './helpers' const { useState, useEffect } = React; +import { flyout } from '../../themes'; +import { flip } from '@popperjs/core'; const FlyoutContainer = styled.div` position: relative; @@ -15,16 +17,31 @@ const FlyoutContainer = styled.div` `; FlyoutContainer.displayName = 'FlyoutContainer'; -const FlyoutTargetWrapper = styled.div``; -FlyoutTargetWrapper.displayName = 'FlyoutTargetWrapper'; +const FlyoutTarget = styled.div``; +FlyoutTarget.displayName = 'FlyoutTarget'; -const FlyoutArrow = styled('div')( +const FlyoutBody = styled.div( themed( - ({ theme }) => { - return css` + ({ theme }) => css` + ${getFontStyle({ size: 'medium' })} + z-index: 100; + position: absolute; + width: fit-conent; + box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); + border: 1px solid ${theme.ui4}; + overflow: visible; + &[data-popper-reference-hidden='true'] { + visibility: hidden; + } + `) +); +FlyoutBody.displayName = 'FlyoutBody'; + +const FlyoutArrow = styled.div( + themed( + ({ theme }) => css` pointer-events: none; position: absolute; - box-sizing: border-box; z-index: 101; width: ${theme.flyout.arrowSize}px; height: ${theme.flyout.arrowSize}px; @@ -32,7 +49,7 @@ const FlyoutArrow = styled('div')( :before { content: ' '; position: absolute; - top: ${theme.flyout.arrowSize - 1}px; + top: ${theme.flyout.arrowSize}px; left: 0; border-style: solid; border-width: ${theme.flyout.arrowSize / 2}px; @@ -42,54 +59,59 @@ const FlyoutArrow = styled('div')( :after { content: ' '; position: absolute; - top: ${theme.flyout.arrowSize - 1}px; + top: ${theme.flyout.arrowSize}px; left: 0; border-style: solid; border-width: ${theme.flyout.arrowSize / 2 - 1}px; margin-left: 1px; border-color: ${theme.flyout.background} transparent transparent transparent; } - `}) + `) ); FlyoutArrow.displayName = 'FlyoutArrow'; -interface FlyoutBodyProps { +interface FlyoutContentProps { noGutter?: boolean; } -const FlyoutBody = styled('div')( - themed( - ({ theme, noGutter }) => css` - ${getFontStyle({ size: 'medium' })} - z-index: 100; - box-sizing: border-box; - box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); - border: 1px solid ${theme.ui4}; - background: ${theme.flyout.background}; - color: ${theme.flyout.textColor}; - max-width: ${theme.flyout.maxWidth}; - max-height: ${theme.flyout.maxHeight}; - ${!noGutter ? `padding: ${distance.small} ${distance.medium};` : ''} box-sizing: border-box; - overflow: auto; +const FlyoutContent = styled.div( + themed(({ theme, noGutter }) => css` + overflow: auto; + background: ${theme.flyout.background}; + color: ${theme.flyout.textColor}; + max-width: ${theme.flyout.maxWidth}; + max-height: ${theme.flyout.maxHeight}; + ${noGutter ? '' : `padding: ${distance.small} ${distance.medium};`} `) ); -FlyoutBody.displayName = 'FlyoutBody'; +FlyoutContent.displayName = 'FlyoutContent' + const FlyoutInt: React.FC = (props) => { const [controlled ] = useState(props.open !== undefined); - const [visible, setVisible] = useState(Boolean(props.open)) + const [visible, setVisible] = useState(Boolean(props.open)); const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const [arrowElement, setArrowElement] = useState(null); + + const popperModifiers: Array> = [ + { name: 'hide' }, + { name: 'flip', enabled: !props.position }, + { name: 'arrow', options: { element: arrowElement } }, + { name: 'offset', options: { offset: [0, props.offset ?? 4 + flyout.arrowSize / 2] } }, + ]; + + if (!props.position) { + popperModifiers.push({ name: 'preventOverflow', + options: { + altAxis: true, + }, + }); + } + const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement: mapFlyoutPositionToPopperPlacement(props.position), - modifiers: [ - { name: 'hide' }, - { name: 'arrow', options: { - element: arrowElement, - } }, - { name: 'offset', options: { offset: [0,20]}} - ], + placement: mapFlyoutPositionToPopperPlacement(props.position || props.defaultPosition), + modifiers: popperModifiers, }); useEffect(() => setVisible(Boolean(props.open)), [props.open]) @@ -107,13 +129,13 @@ const FlyoutInt: React.FC = (props) => { return ( - {props.children} - {visible && props.content && - - {props.content} - - - } + {props.children} + {visible && props.content && + + {props.content} + + + } ); } From 76e0e81af0367e10e9bd2796c094516b66a21ed9 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Sat, 11 Sep 2021 13:06:22 +0300 Subject: [PATCH 07/17] Fix foramtting and linter errors --- src/components/Flyout2/helpers.tsx | 29 +++---- src/components/Flyout2/index.tsx | 117 +++++++++++++++-------------- src/hoc/withClickOutsideFC.tsx | 20 +++-- 3 files changed, 89 insertions(+), 77 deletions(-) diff --git a/src/components/Flyout2/helpers.tsx b/src/components/Flyout2/helpers.tsx index d882bfd6..61d170e1 100644 --- a/src/components/Flyout2/helpers.tsx +++ b/src/components/Flyout2/helpers.tsx @@ -1,13 +1,13 @@ import { FlyoutPosition } from '../Flyout/Flyout.types.part'; -import { Placement } from '@popperjs/core' +import { Placement } from '@popperjs/core'; export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): Placement => { if (position) { return ({ - 'top': 'top', - 'right': 'right', - 'bottom': 'bottom', - 'left': 'left', + top: 'top', + right: 'right', + bottom: 'bottom', + left: 'left', 'top-left': 'top-start', 'top-right': 'top-end', 'bottom-left': 'bottom-start', @@ -16,20 +16,23 @@ export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): P 'right-bottom': 'right-end', 'left-top': 'left-start', 'left-bottom': 'left-end', - } as const)[position] + } as const)[position]; } return 'auto'; -} +}; -export const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: React.CSSProperties = {}): React.CSSProperties => { +export const calculateArrowStyleOverrides = ( + popperAttributes = {}, + arrowStyles: React.CSSProperties = {}, +): React.CSSProperties => { const { transform, ...arrowStylesWithoutTransform } = arrowStyles; if (!transform || !popperAttributes['data-popper-placement']) { return arrowStyles; } - const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement).split('-')[0] + const primaryPlacement = (popperAttributes['data-popper-placement'] as Placement).split('-')[0]; const placementDependentStyles: React.CSSProperties = { top: { @@ -47,8 +50,8 @@ export const calculateArrowStyleOverrides = (popperAttributes = {}, arrowStyles: right: { left: 0, transform: transform + ` rotate(${90}deg)`, - } - }[primaryPlacement as 'top' | 'bottom' | 'left' | 'right'] + }, + }[primaryPlacement as 'top' | 'bottom' | 'left' | 'right']; - return {...arrowStylesWithoutTransform, ...placementDependentStyles} -} + return { ...arrowStylesWithoutTransform, ...placementDependentStyles }; +}; diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx index 0ab84f70..725e3edb 100644 --- a/src/components/Flyout2/index.tsx +++ b/src/components/Flyout2/index.tsx @@ -1,14 +1,13 @@ import * as React from 'react'; -import { usePopper, Modifier } from 'react-popper' +import { usePopper, Modifier } from 'react-popper'; import { withClickOutsideFC, WithClickOutsideFCProps } from '../../hoc/withClickOutsideFC'; import styled, { css, themed } from '../../utils/styled'; import { getFontStyle } from '../../textStyles'; import { distance } from '../../distance'; import { FlyoutProps } from '../Flyout/Flyout.types.part'; -import { mapFlyoutPositionToPopperPlacement, calculateArrowStyleOverrides } from './helpers' +import { mapFlyoutPositionToPopperPlacement, calculateArrowStyleOverrides } from './helpers'; const { useState, useEffect } = React; import { flyout } from '../../themes'; -import { flip } from '@popperjs/core'; const FlyoutContainer = styled.div` position: relative; @@ -33,40 +32,42 @@ const FlyoutBody = styled.div( &[data-popper-reference-hidden='true'] { visibility: hidden; } - `) + `, + ), ); FlyoutBody.displayName = 'FlyoutBody'; const FlyoutArrow = styled.div( themed( ({ theme }) => css` - pointer-events: none; + pointer-events: none; + position: absolute; + z-index: 101; + width: ${theme.flyout.arrowSize}px; + height: ${theme.flyout.arrowSize}px; + + :before { + content: ' '; + position: absolute; + top: ${theme.flyout.arrowSize}px; + left: 0; + border-style: solid; + border-width: ${theme.flyout.arrowSize / 2}px; + border-color: ${theme.ui4} transparent transparent transparent; + } + + :after { + content: ' '; position: absolute; - z-index: 101; - width: ${theme.flyout.arrowSize}px; - height: ${theme.flyout.arrowSize}px; - - :before { - content: ' '; - position: absolute; - top: ${theme.flyout.arrowSize}px; - left: 0; - border-style: solid; - border-width: ${theme.flyout.arrowSize / 2}px; - border-color: ${theme.ui4} transparent transparent transparent; - } - - :after { - content: ' '; - position: absolute; - top: ${theme.flyout.arrowSize}px; - left: 0; - border-style: solid; - border-width: ${theme.flyout.arrowSize / 2 - 1}px; - margin-left: 1px; - border-color: ${theme.flyout.background} transparent transparent transparent; - } - `) + top: ${theme.flyout.arrowSize}px; + left: 0; + border-style: solid; + border-width: ${theme.flyout.arrowSize / 2 - 1}px; + margin-left: 1px; + border-color: ${theme.flyout.background} transparent transparent transparent; + } + `, + ), ); FlyoutArrow.displayName = 'FlyoutArrow'; @@ -75,34 +76,36 @@ interface FlyoutContentProps { } const FlyoutContent = styled.div( - themed(({ theme, noGutter }) => css` - overflow: auto; - background: ${theme.flyout.background}; - color: ${theme.flyout.textColor}; - max-width: ${theme.flyout.maxWidth}; - max-height: ${theme.flyout.maxHeight}; - ${noGutter ? '' : `padding: ${distance.small} ${distance.medium};`} - `) + themed( + ({ theme, noGutter }) => css` + overflow: auto; + background: ${theme.flyout.background}; + color: ${theme.flyout.textColor}; + max-width: ${theme.flyout.maxWidth}; + max-height: ${theme.flyout.maxHeight}; + ${noGutter ? '' : `padding: ${distance.small} ${distance.medium};`} + `, + ), ); -FlyoutContent.displayName = 'FlyoutContent' - +FlyoutContent.displayName = 'FlyoutContent'; -const FlyoutInt: React.FC = (props) => { - const [controlled ] = useState(props.open !== undefined); +const FlyoutInt: React.FC = props => { + const [controlled] = useState(props.open !== undefined); const [visible, setVisible] = useState(Boolean(props.open)); - const [referenceElement, setReferenceElement] = useState(null); - const [popperElement, setPopperElement] = useState(null); - const [arrowElement, setArrowElement] = useState(null); + const [referenceElement, setReferenceElement] = useState(); + const [popperElement, setPopperElement] = useState(); + const [arrowElement, setArrowElement] = useState(); const popperModifiers: Array> = [ { name: 'hide' }, { name: 'flip', enabled: !props.position }, { name: 'arrow', options: { element: arrowElement } }, - { name: 'offset', options: { offset: [0, props.offset ?? 4 + flyout.arrowSize / 2] } }, + { name: 'offset', options: { offset: [0, props.offset || 4 + flyout.arrowSize / 2] } }, ]; if (!props.position) { - popperModifiers.push({ name: 'preventOverflow', + popperModifiers.push({ + name: 'preventOverflow', options: { altAxis: true, }, @@ -114,31 +117,33 @@ const FlyoutInt: React.FC = (props) => { modifiers: popperModifiers, }); - useEffect(() => setVisible(Boolean(props.open)), [props.open]) - useEffect(() => changeVisibility(false), [props.outsideClickEvent]) + useEffect(() => setVisible(Boolean(props.open)), [props.open]); + useEffect(() => changeVisibility(false), [props.outsideClickEvent]); const onClick = () => changeVisibility(!visible); const changeVisibility = (nextVisibility: boolean) => { if (controlled || nextVisibility === visible) { - return + return; } typeof props.onChange === 'function' && props.onChange({ open: nextVisibility }); setVisible(nextVisibility); - } + }; return ( - {props.children} - {visible && props.content && + + {props.children} + + {visible && props.content && ( {props.content} - + - } + )} ); -} +}; FlyoutInt.displayName = 'FlyoutInt'; export const Flyout2 = withClickOutsideFC(FlyoutInt); diff --git a/src/hoc/withClickOutsideFC.tsx b/src/hoc/withClickOutsideFC.tsx index 744254ac..bc8c1c01 100644 --- a/src/hoc/withClickOutsideFC.tsx +++ b/src/hoc/withClickOutsideFC.tsx @@ -10,7 +10,7 @@ import onClickOutside, { AdditionalProps } from 'react-onclickoutside'; import * as React from 'react'; interface OutsideClickProps { - outsideClickEvent: React.SyntheticEvent | null; + outsideClickEvent: React.SyntheticEvent | undefined; } export function withClickOutsideFC

(Component: React.FC

) { @@ -19,23 +19,27 @@ export function withClickOutsideFC

(Component: React.FC

) { constructor(props: P) { super(props); this.state = { - outsideClickEvent: null, - } + outsideClickEvent: undefined, + }; } handleClickOutside = (e: React.SyntheticEvent) => { - this.setState({ outsideClickEvent: e }) - } + this.setState({ outsideClickEvent: e }); + }; render() { const { children, ...otherProps } = this.props; - return {children} + return ( + + {children} + + ); } } OutsideClickAdapter.displayName = 'withClickOutsideWrapper'; - return onClickOutside(OutsideClickAdapter) + return onClickOutside(OutsideClickAdapter); } -export interface WithClickOutsideFCProps extends AdditionalProps, OutsideClickProps {}; +export interface WithClickOutsideFCProps extends AdditionalProps, OutsideClickProps {} From 751b13b8f8df832821da60fb8f6a6f86899bdac5 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Mon, 13 Sep 2021 13:36:48 +0300 Subject: [PATCH 08/17] Fix react-datepicker compatibility issues --- package-lock.json | 6 +- package.json | 2 +- .../DateField/CustomReactDatepicker.part.tsx | 17 +++--- .../DateField/DateFieldInt.part.tsx | 5 ++ src/components/DateField/Example.md | 60 ++++++++++--------- 5 files changed, 48 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index 52fd8314..591d210c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7158,9 +7158,9 @@ } }, "date-fns": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.3.0.tgz", - "integrity": "sha512-A8o+iXBVqQayl9Z39BHgb7m/zLOfhF7LK82t+n9Fq1adds1vaUn8ByVoADqWLe4OTc6BZYc/FdbdTwufNYqkJw==" + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.23.0.tgz", + "integrity": "sha512-5ycpauovVyAk0kXNZz6ZoB9AYMZB4DObse7P3BPWmyEjXNORTI8EJ6X0uaSAq4sCHzM1uajzrkr6HnsLQpxGXA==" }, "date-now": { "version": "0.1.4", diff --git a/package.json b/package.json index 24c241ac..da6cccaa 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ }, "dependencies": { "@popperjs/core": "^2.10.1", - "date-fns": "^2.3.0", + "date-fns": "^2.23.0", "memoize-one": "^5.1.0", "react-datepicker": "^4.2.1", "react-onclickoutside": "^6.7.1", diff --git a/src/components/DateField/CustomReactDatepicker.part.tsx b/src/components/DateField/CustomReactDatepicker.part.tsx index f88c0d58..8b946255 100644 --- a/src/components/DateField/CustomReactDatepicker.part.tsx +++ b/src/components/DateField/CustomReactDatepicker.part.tsx @@ -19,20 +19,19 @@ function patchLocale(locale: Locale, inputWeekDays = defaultWeekDays, months = d return { ...locale, localize: { - // @TODO Find out if these methods can indeed be missing in the runtime, change Locale type accordingly - // ordinalNumber() {}, - // era() {}, - // quarter() {}, - // dayPeriod() {}, + ordinalNumber() {}, + era() {}, + quarter() {}, + dayPeriod() {}, ...locale.localize, month: (month: number) => months[month], day: (day: number) => weekDays[day], }, match: { - // ordinalNumber() {}, - // era() {}, - // quarter() {}, - // dayPeriod() {}, + ordinalNumber() {}, + era() {}, + quarter() {}, + dayPeriod() {}, ...locale.match, month: (month: string) => months.indexOf(month), day: (day: string) => weekDays.indexOf(day), diff --git a/src/components/DateField/DateFieldInt.part.tsx b/src/components/DateField/DateFieldInt.part.tsx index b7e27775..e95a6d7b 100644 --- a/src/components/DateField/DateFieldInt.part.tsx +++ b/src/components/DateField/DateFieldInt.part.tsx @@ -244,6 +244,11 @@ class DateFieldInt extends React.Component { }; private changeInput = (e: React.FocusEvent) => { + // in the latest version of React-datepicker changeRaw() is called on each change, + // not only on raw input change. In that case it may not have value + if (!e.target.value) { + return; + } const { onChangeRaw } = this.props; const { value } = e.target; this.change(this.parseDate(value), value); diff --git a/src/components/DateField/Example.md b/src/components/DateField/Example.md index b2a839e5..cadf713f 100644 --- a/src/components/DateField/Example.md +++ b/src/components/DateField/Example.md @@ -1,6 +1,6 @@ **Elementary** -The `DateField` component provides an easy way for selecting a date. As all inputs it can be used in a controlled and managed mode. +The `DateField` component provides an easy way for selecting a date. As all inputs it can be used in a controlled and managed mode. `DateField` is based on [ReactJS Datepicker](https://reactdatepicker.com/). Please refer to ReactJS Datepicker Docs for the detailed props description. @@ -31,11 +31,11 @@ Select time ```jsx { "props": { "data-skip": true } } const { DateField } = require('precise-ui'); - console.log(e)} placeholder="Select time" /> ``` @@ -130,8 +130,8 @@ const { subDays } = require('date-fns'); console.log(e)} + placeholder="Select a date other than today or yesterday" + onChange={(e) => console.log(e)} placeholder="Exluded times" /> ``` @@ -157,7 +157,7 @@ const { subDays, addDays } = require('date-fns'); console.log(e)} /> ``` @@ -169,7 +169,7 @@ const { addDays } = require('date-fns'); console.log(e)} /> ``` @@ -194,7 +194,7 @@ const DateRange = () => { placeholder="Select start date" /> - + console.log(e)} /> ``` @@ -243,17 +243,19 @@ const { DateField } = require('precise-ui'); console.log(e)} placeholder="With changed popup position" /> @@ -329,7 +331,7 @@ const Example = () => { return (

- console.log(open)} placeholder="Click show button"/> + console.log(open)} placeholder="Click show button"/>
) @@ -401,10 +403,10 @@ const Example = () => { const validate = (value) => setError(value.match(/^\d{2}-\d{2}-\d{4}$/) ? undefined : 'Invalid date format') return ( - validate(e.target.value)} + validate(e.target.value)} onChange={({value}) => validate(value)} - error={error} + error={error} placeholder="Type aaaa" /> ) @@ -428,13 +430,13 @@ Inline version time ```jsx const { DateField } = require('precise-ui'); - console.log(e)} /> ``` @@ -469,4 +471,4 @@ const MyDateField = withValidation(({ value }) => { placeholder="Shows an error when a date before than today is selected" /> -``` \ No newline at end of file +``` From 2b2bc61e334589d90af0b7d7ecd9f4f875338eb2 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Mon, 13 Sep 2021 17:51:58 +0300 Subject: [PATCH 09/17] Fix compatibility issues --- .../__image_snapshots__/Flyout_3-snap.png | Bin 7457 -> 5497 bytes src/components/Flyout/FlyoutWindow.part.tsx | 521 ------------------ src/components/Flyout/index.tsx | 234 ++++---- src/components/Flyout2/Example.md | 319 ----------- src/components/Flyout2/index.tsx | 150 ----- src/components/index.ts | 1 - src/hoc/withClickOutsideFC.tsx | 2 +- .../flyoutCompatibilityHelpers.tsx} | 4 +- 8 files changed, 144 insertions(+), 1087 deletions(-) delete mode 100644 src/components/Flyout/FlyoutWindow.part.tsx delete mode 100644 src/components/Flyout2/Example.md delete mode 100644 src/components/Flyout2/index.tsx rename src/{components/Flyout2/helpers.tsx => utils/flyoutCompatibilityHelpers.tsx} (94%) diff --git a/integration/__image_snapshots__/Flyout_3-snap.png b/integration/__image_snapshots__/Flyout_3-snap.png index 9d887ce57c5fd0785906eb780342da98a3500391..2c363c976d5e3a99468f65739c2106096aebc5fe 100644 GIT binary patch literal 5497 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 literal 7457 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 diff --git a/src/components/Flyout/FlyoutWindow.part.tsx b/src/components/Flyout/FlyoutWindow.part.tsx deleted file mode 100644 index ec409d4e..00000000 --- a/src/components/Flyout/FlyoutWindow.part.tsx +++ /dev/null @@ -1,521 +0,0 @@ -import * as React from 'react'; -import styled, { css, themed } from '../../utils/styled'; -import { withResponsive, ResponsiveComponentProps } from '../../hoc/withResponsive'; -import { FlyoutPosition, BasicPosition } from './Flyout.types.part'; -import { distancePx, distance } from '../../distance'; -import { StandardProps } from '../../common'; -import { getFontStyle } from '../../textStyles'; - -const toolTipArrowSize = 18; - -export interface Dimensions { - width: number; - height: number; -} - -interface InvertedPosition { - top: 'bottom'; - left: 'right'; - right: 'left'; - bottom: 'top'; -} - -export interface FlyoutWindowProps extends ResponsiveComponentProps, StandardProps { - targetRect: ClientRect; - children: React.ReactNode; - position?: FlyoutPosition; - defaultPosition?: FlyoutPosition; - offset?: number; -} - -export interface Position { - top?: number; - right?: number; - bottom?: number; - left?: number; -} - -function getFlyoutArrowPosition({ - mainPosition, - secondaryPosition: origSecondaryPosition, - offset, - targetRect, - flyoutRect, -}: { - mainPosition: BasicPosition; - secondaryPosition?: BasicPosition; - targetRect: ClientRect; - flyoutRect: ClientRect; - offset: number; -}) { - if (mainPosition && flyoutRect.width) { - const vertical = isVertical(mainPosition); - const mainAxisTargetSize = vertical ? targetRect.height : targetRect.width; - const secondaryAxisTargetSize = vertical ? targetRect.width : targetRect.height; - const secondaryAxisContnetSize = vertical ? flyoutRect.width : flyoutRect.height; - const mainPositionValue = mainAxisTargetSize + offset + 1 - toolTipArrowSize / 2; - - let secondaryPosition: string; - let secondaryPositionValue: number; - if (!origSecondaryPosition) { - secondaryPosition = vertical ? 'left' : 'top'; - secondaryPositionValue = (secondaryAxisTargetSize - toolTipArrowSize) / 2; - } else { - secondaryPosition = origSecondaryPosition; - secondaryPositionValue = - secondaryAxisTargetSize / 4 > secondaryAxisContnetSize - toolTipArrowSize - ? secondaryAxisContnetSize - toolTipArrowSize - : secondaryAxisTargetSize / 4; - } - - const rotate = { - top: 0, - left: -90, - right: 90, - bottom: 180, - }; - - return { - rotate: rotate[mainPosition], - [invertPosition(mainPosition)]: mainPositionValue, - [secondaryPosition]: secondaryPositionValue, - }; - } - - return {}; -} - -function getDetailedPositionInfo({ - targetRect, - dimensions, - flyoutRect, -}: { - targetRect: ClientRect; - dimensions: Dimensions; - flyoutRect: ClientRect; -}) { - const viewportPosition = { - left: targetRect.left, - right: dimensions.width - targetRect.right, - top: targetRect.top, - bottom: dimensions.height - targetRect.bottom, - }; - - const hasEnoughSpace = {}; - const hasMoreSpace = {}; - - for (const position of Object.keys(viewportPosition)) { - if (isVertical(position as BasicPosition)) { - // There is always enought space for top/bottom flyout when it lays outside of the viewport. - hasEnoughSpace[position] = targetRect.top > dimensions.height || viewportPosition[position] > flyoutRect.height; - } else { - hasEnoughSpace[position] = viewportPosition[position] > flyoutRect.width; - } - hasMoreSpace[position] = viewportPosition[position] > viewportPosition[invertPosition(position as BasicPosition)]; - } - - return { - viewportPosition, - hasEnoughSpace, - hasMoreSpace, - }; -} - -function getMainAxisFlyoutPositionAndSize({ - direction, - targetRect, - offset, - dimensions, - vertical, - flyoutRect, -}: { - direction: BasicPosition; - targetRect: ClientRect; - flyoutRect: ClientRect; - offset: number; - dimensions: Dimensions; - vertical: boolean; -}) { - const targetSize = vertical ? targetRect.height : targetRect.width; - const flyoutSize = vertical ? flyoutRect.height : flyoutRect.width; - const { hasEnoughSpace, viewportPosition } = getDetailedPositionInfo({ - targetRect, - dimensions, - flyoutRect, - }); - const sizeValue = hasEnoughSpace[direction] - ? flyoutSize - : viewportPosition[direction] - (offset + toolTipArrowSize / 2) - distancePx.medium; - - return { - position: { - name: invertPosition(direction), - value: targetSize + (offset + toolTipArrowSize / 2), - }, - size: { - name: vertical ? 'height' : 'width', - value: sizeValue, - }, - }; -} - -function getSecondaryAxisFlyoutPositionAndSize({ - direction, - targetRect, - dimensions, - vertical, - flyoutRect, -}: { - direction: BasicPosition; - targetRect: ClientRect; - dimensions: Dimensions; - vertical: boolean; - flyoutRect: ClientRect; -}) { - let positionValue: number; - let positionName: BasicPosition; - const sizeName = vertical ? 'width' : 'height'; - const targetSize = vertical ? targetRect.width : targetRect.height; - const targetPosition = vertical ? targetRect.left : targetRect.top; - const flyoutSize = vertical ? flyoutRect.width : flyoutRect.height; - const windowSize = vertical ? dimensions.width : dimensions.height; - const { viewportPosition } = getDetailedPositionInfo({ - targetRect, - dimensions, - flyoutRect, - }); - - let sizeValue = flyoutSize; - - if (direction === undefined) { - positionName = vertical ? 'left' : 'top'; - const centeredValue = (targetSize - flyoutSize) / 2; - if (viewportPosition[positionName] > 0 && viewportPosition[positionName] + centeredValue < 0) { - positionValue = -viewportPosition[positionName] + distancePx.medium; - sizeValue = flyoutSize > windowSize ? windowSize - distancePx.xlarge : flyoutSize; - } else { - positionValue = centeredValue; - } - } else { - positionName = direction; - positionValue = - positionName === 'top' || positionName === 'left' - ? 0 - : targetPosition - flyoutSize > 0 - ? targetSize - flyoutSize - : -targetPosition + distancePx.medium; - - if (positionName === 'top' || positionName === 'left') { - sizeValue = - targetPosition + positionValue + flyoutSize > windowSize - ? windowSize - targetPosition + positionValue - distancePx.medium - : flyoutSize; - } else { - sizeValue = - flyoutSize > targetPosition + targetSize ? targetPosition + targetSize - distancePx.medium : flyoutSize; - - positionName = invertPosition(positionName); - } - } - return { - position: { - name: positionName, - value: positionValue, - }, - size: { - name: sizeName, - value: sizeValue, - }, - }; -} - -function invertPosition(position: BasicPosition) { - const invertedPosition: InvertedPosition = { - top: 'bottom', - left: 'right', - right: 'left', - bottom: 'top', - }; - - return invertedPosition[position]; -} - -function isVertical(position: BasicPosition) { - return position === 'top' || position === 'bottom' ? true : false; -} - -export interface StyledFlyoutWindowProps { - size?: { - width?: number; - height?: number; - }; - position?: Position; - noGutter?: boolean; -} - -const StyledFlyoutWindow = styled('div')( - themed( - ({ theme, size, position, noGutter }) => css` - ${getFontStyle({ size: 'medium' })} - - position: absolute; - z-index: 100; - box-sizing: border-box; - box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); - border: 1px solid ${theme.ui4}; - background: ${theme.flyout.background}; - color: ${theme.flyout.textColor}; - max-width: ${theme.flyout.maxWidth}; - max-height: ${theme.flyout.maxHeight}; - ${!noGutter ? `padding: ${distance.small} ${distance.medium};` : ''} box-sizing: border-box; - - ${size && size.width !== undefined ? `width: ${size.width}px` : ''}; - ${size && size.height !== undefined ? `height: ${size.height}px` : ''}; - ${position && position.top !== undefined ? `top: ${position.top}px` : ''}; - ${position && position.left !== undefined ? `left: ${position.left}px` : ''}; - ${position && position.bottom !== undefined ? `bottom: ${position.bottom}px` : ''}; - ${position && position.right !== undefined ? `right: ${position.right}px` : ''}; - overflow: auto; - `, - ), -); - -interface StyledFlyoutArrowProps extends Position { - rotate?: number; -} - -const StyledFlyoutArrow = styled('div')( - themed( - ({ top, left, bottom, right, rotate, theme }) => css` - pointer-events: none; - position: absolute; - z-index: 101; - width: ${toolTipArrowSize}px; - height: ${toolTipArrowSize}px; - - ${top !== undefined ? `top: ${top}px` : ''}; - ${left !== undefined ? `left: ${left}px` : ''}; - ${bottom !== undefined ? `bottom: ${bottom}px` : ''}; - ${right !== undefined ? `right: ${right}px` : ''}; - ${rotate !== undefined ? `transform: rotate(${rotate}deg)` : ''}; - - :before { - content: ' '; - position: absolute; - top: 0; - left: 0; - border-style: solid; - border-width: ${toolTipArrowSize / 2}px; - border-color: ${theme.ui4} transparent transparent transparent; - } - - :after { - content: ' '; - position: absolute; - top: 0; - left: 0; - border-style: solid; - border-width: ${toolTipArrowSize / 2 - 1}px; - margin-left: 1px; - border-color: ${theme.flyout.background} transparent transparent transparent; - } - `, - ), -); - -export interface FlyoutWindowState { - flyoutRect?: ClientRect; - children?: React.ReactNode; -} - -interface ScrollPosition { - top: number; - left: number; -} - -export class FlyoutWindowInt extends React.Component { - private flyoutContainer: HTMLDivElement | null; - private scrollPosition: ScrollPosition = { top: 0, left: 0 }; - - constructor(props: FlyoutWindowProps) { - super(props); - this.state = { - flyoutRect: undefined, - children: undefined, - }; - } - - static getDerivedStateFromProps(nextProps: FlyoutWindowProps, prevState: FlyoutWindowState) { - if (nextProps.children !== prevState.children) { - return { - flyoutRect: undefined, - children: nextProps.children, - }; - } - - return { - children: nextProps.children, - }; - } - - private setFlyoutRef = (el: HTMLDivElement) => { - if (this.flyoutContainer) { - this.flyoutContainer.removeEventListener('scroll', this.onScroll); - } - - if (el) { - el.addEventListener('scroll', this.onScroll); - } - - this.flyoutContainer = el; - }; - - private onScroll = (e: UIEvent) => { - if (e.target && e.target instanceof HTMLElement) { - this.scrollPosition = { - top: e.target.scrollTop, - left: e.target.scrollLeft, - }; - } - }; - - componentDidMount() { - if (this.flyoutContainer) { - this.updateMeasurements(); - } - } - - componentWillUnmount() { - if (this.flyoutContainer) { - this.flyoutContainer.removeEventListener('scroll', this.onScroll); - } - } - - componentDidUpdate() { - if (this.flyoutContainer) { - if (!this.state.flyoutRect) { - this.updateMeasurements(); - } - - if (this.flyoutContainer.scroll) { - this.flyoutContainer.scroll({ - top: this.scrollPosition.top, - left: this.scrollPosition.left, - }); - } - } - } - - private updateMeasurements() { - if (this.flyoutContainer) { - const flyoutRect = this.flyoutContainer.getBoundingClientRect(); - this.setState({ - flyoutRect, - }); - } - } - - private getFlyoutDimensions(): { - size?: { - width?: number; - height?: number; - }; - position?: Position; - arrowPosition?: Position & { rotate?: number }; - } { - const { flyoutRect } = this.state; - if (!flyoutRect) { - return {}; - } - const { position, defaultPosition = 'bottom', offset = 4, dimensions, targetRect } = this.props; - const [mainPosition, secondaryPosition] = (position || defaultPosition).split('-') as Array; - if (!dimensions || !mainPosition) { - return {}; - } - - const vertical = isVertical(mainPosition); - - const { hasMoreSpace, hasEnoughSpace } = getDetailedPositionInfo({ - targetRect, - dimensions, - flyoutRect, - }); - - let mainDirection = mainPosition; - let main = getMainAxisFlyoutPositionAndSize({ - direction: mainDirection, - targetRect, - offset, - dimensions, - vertical, - flyoutRect, - }); - - if (!position && !hasEnoughSpace[mainDirection] && !hasMoreSpace[mainDirection]) { - mainDirection = invertPosition(mainDirection); - main = getMainAxisFlyoutPositionAndSize({ - direction: mainDirection, - targetRect, - offset, - dimensions, - vertical, - flyoutRect, - }); - } - - let secondaryDirection = secondaryPosition; - let secondary = getSecondaryAxisFlyoutPositionAndSize({ - direction: secondaryDirection, - targetRect, - dimensions, - vertical, - flyoutRect, - }); - - if (!position && secondaryDirection && !hasEnoughSpace[secondaryDirection] && hasMoreSpace[secondaryDirection]) { - secondaryDirection = invertPosition(secondaryDirection); - secondary = getSecondaryAxisFlyoutPositionAndSize({ - direction: secondaryDirection, - targetRect, - dimensions, - vertical, - flyoutRect, - }); - } - - return { - position: { - [main.position.name]: main.position.value, - [secondary.position.name]: secondary.position.value, - }, - size: { - [main.size.name]: main.size.value, - [secondary.size.name]: secondary.size.value, - }, - arrowPosition: getFlyoutArrowPosition({ - mainPosition: mainDirection, - secondaryPosition: secondaryDirection, - offset, - targetRect, - flyoutRect, - }), - }; - } - - render() { - const { children, targetRect: _0, position: _1, offset: _2, dimensions: _3, innerRef: _4, ...props } = this.props; - const { arrowPosition, ...flyoutDimensions } = this.getFlyoutDimensions(); - return ( - children && ( - <> - - - {children} - - - ) - ); - } -} - -export const FlyoutWindow = withResponsive(FlyoutWindowInt); diff --git a/src/components/Flyout/index.tsx b/src/components/Flyout/index.tsx index 98a1b2cd..9e070c42 100644 --- a/src/components/Flyout/index.tsx +++ b/src/components/Flyout/index.tsx @@ -1,10 +1,17 @@ import * as React from 'react'; -import onClickOutside, { AdditionalProps } from 'react-onclickoutside'; -import styled from '../../utils/styled'; -import { FlyoutWindow } from './FlyoutWindow.part'; +import { usePopper, Modifier } from 'react-popper'; +import { withClickOutsideFC, WithClickOutsideFCProps } from '../../hoc/withClickOutsideFC'; +import styled, { css, themed } from '../../utils/styled'; +import { getFontStyle } from '../../textStyles'; +import { distance } from '../../distance'; +import { + mapFlyoutPositionToPopperPlacement, + calculateArrowStyleOverrides, +} from '../../utils/flyoutCompatibilityHelpers'; +const { useState, useEffect } = React; +import { flyout } from '../../themes'; import { FlyoutProps } from './Flyout.types.part'; - -export { FlyoutPosition, FlyoutChangeEvent, FlyoutProps } from './Flyout.types.part'; +export { FlyoutProps } from './Flyout.types.part'; const FlyoutContainer = styled.div` position: relative; @@ -12,102 +19,143 @@ const FlyoutContainer = styled.div` width: fit-content; `; -const StyledTargetWrapper = styled.div``; - -export interface FlyoutState { - controlled: boolean; - targetRect?: ClientRect; - dirtyFlag: boolean; - open: boolean; +FlyoutContainer.displayName = 'FlyoutContainer'; + +const FlyoutTarget = styled.div``; +FlyoutTarget.displayName = 'FlyoutTarget'; + +const FlyoutBody = styled.div( + themed( + ({ theme }) => css` + ${getFontStyle({ size: 'medium' })} + z-index: 100; + position: absolute; + width: fit-conent; + box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); + border: 1px solid ${theme.ui4}; + overflow: visible; + &[data-popper-reference-hidden='true'] { + visibility: hidden; + } + `, + ), +); +FlyoutBody.displayName = 'FlyoutBody'; + +const FlyoutArrow = styled.div( + themed( + ({ theme }) => css` + pointer-events: none; + position: absolute; + z-index: 101; + width: ${theme.flyout.arrowSize}px; + height: ${theme.flyout.arrowSize}px; + + :before { + content: ' '; + position: absolute; + top: ${theme.flyout.arrowSize}px; + left: 0; + border-style: solid; + border-width: ${theme.flyout.arrowSize / 2}px; + border-color: ${theme.ui4} transparent transparent transparent; + } + + :after { + content: ' '; + position: absolute; + top: ${theme.flyout.arrowSize}px; + left: 0; + border-style: solid; + border-width: ${theme.flyout.arrowSize / 2 - 1}px; + margin-left: 1px; + border-color: ${theme.flyout.background} transparent transparent transparent; + } + `, + ), +); +FlyoutArrow.displayName = 'FlyoutArrow'; + +interface FlyoutContentProps { + noGutter?: boolean; } -class FlyoutInt extends React.Component { - private targetContainer: HTMLDivElement | null; - - constructor(props: FlyoutProps) { - super(props); - this.state = { - controlled: props.open !== undefined, - open: props.open || false, - targetRect: undefined, - dirtyFlag: false, - }; - } - - handleClickOutside = () => { - this.state.open && !this.state.controlled && this.setOpen(false); - }; - - componentDidMount() { - this.updateMeasurements(); - } - - UNSAFE_componentWillReceiveProps(nextProps: FlyoutProps) { - if (this.state.controlled && nextProps.open !== undefined) { - this.setOpen(nextProps.open); - } - } - - componentDidUpdate() { - const { dirtyFlag } = this.state; - - if (dirtyFlag) { - this.updateMeasurements(); - this.setState({ - dirtyFlag: false, - }); - } - } - - private updateMeasurements() { - if (this.targetContainer) { - const targetRect = this.targetContainer.getBoundingClientRect(); - this.setState({ - targetRect, - }); - } +const FlyoutContent = styled.div( + themed( + ({ theme, noGutter }) => css` + overflow: auto; + background: ${theme.flyout.background}; + color: ${theme.flyout.textColor}; + max-width: ${theme.flyout.maxWidth}; + max-height: ${theme.flyout.maxHeight}; + ${noGutter ? '' : `padding: ${distance.small} ${distance.medium};`} + `, + ), +); +FlyoutContent.displayName = 'FlyoutContent'; + +const FlyoutInt: React.FC = props => { + const [controlled] = useState(props.open !== undefined); + const [visible, setVisible] = useState(Boolean(props.open)); + const [referenceElement, setReferenceElement] = useState(); + const [popperElement, setPopperElement] = useState(); + const [arrowElement, setArrowElement] = useState(); + + const popperModifiers: Array> = [ + { name: 'hide' }, + { name: 'flip', enabled: !props.position }, + { name: 'arrow', options: { element: arrowElement } }, + { name: 'offset', options: { offset: [0, props.offset || 4 + flyout.arrowSize / 2] } }, + ]; + + if (!props.position) { + popperModifiers.push({ + name: 'preventOverflow', + options: { + altAxis: true, + }, + }); } - private setTargetRef = (el: HTMLDivElement | null) => { - this.targetContainer = el; - }; - - private setOpen(open: boolean) { - const { onChange } = this.props; + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement: mapFlyoutPositionToPopperPlacement(props.position || props.defaultPosition), + modifiers: popperModifiers, + }); - if (typeof onChange === 'function') { - onChange({ open }); - } + useEffect(() => setVisible(Boolean(props.open)), [props.open]); + useEffect(() => changeVisibility(false), [props.outsideClickEvent]); - this.setState({ - open, - dirtyFlag: open === true, - }); - } + const onClick = () => changeVisibility(!visible); - private onClick = () => { - if (!this.state.controlled) { - this.setOpen(!this.state.open); + const changeVisibility = (nextVisibility: boolean) => { + if (controlled || nextVisibility === visible) { + return; } + typeof props.onChange === 'function' && props.onChange({ open: nextVisibility }); + setVisible(nextVisibility); }; - render() { - const { children, content, open: _0, onChange: _1, ...props } = this.props; - const { targetRect } = this.state; - const { open } = this.state; - - return ( - - {children} - {!!content && targetRect && open && ( - - {content} - - )} - - ); - } -} - -export const Flyout: React.ComponentClass = onClickOutside(FlyoutInt); + return ( + + + {props.children} + + {visible && props.content && ( + + {/* Normally a styled component gets the theme from context. But some other component + may pass a customized theme as a prop. See example at Tooltip component */} + {props.content} + + + )} + + ); +}; +FlyoutInt.displayName = 'FlyoutInt'; + +export const Flyout = withClickOutsideFC(FlyoutInt); Flyout.displayName = 'Flyout'; diff --git a/src/components/Flyout2/Example.md b/src/components/Flyout2/Example.md deleted file mode 100644 index 9394e95f..00000000 --- a/src/components/Flyout2/Example.md +++ /dev/null @@ -1,319 +0,0 @@ -**Elementary** - -We can use the (default) auto placement to just show a simple flyout on its ideal position. The component supports two modes - managed and controlled. By default, the managed mode is selected. - -```jsx -const { Flyout2 } = require('precise-ui'); - - - Wrapped element - -``` - -**Controlled Mode** - -Flyout different placements example in controlled mode. The controlled mode is triggered by explicitly setting the `open` prop. - -```jsx -const { Tag, Flyout2 } = require('precise-ui'); - -
- - Element - - - - Element - - - - Element - - - - Element - -
-``` - -In this example a combination of `Button` and `Flyout` is used. - -```jsx -const { Flyout2, Button } = require('precise-ui'); - -const positions = ['top', 'right', 'bottom', 'left', 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'right-top', 'right-bottom', 'left-top', 'left-bottom']; - -class RandomFlyout extends React.Component { - constructor(props) { - super(props); - this.state = { - open: false, - }; - - this.change = e => this.setState({ - open: !this.state.open, - }); - } - - render() { - const { open } = this.state; - return ( -
- -

Note

- Flyout appears in random position -
- }> - - - - ); - } -} - - -``` - -**Interactive List** - -Example of `InteractiveList` component usage together with flyout. - -```jsx -const { Flyout2, Button, InteractiveList } = require('precise-ui'); - -const CustomWrapper = function (props) { - const { children, ...rest } = props; - return
{children}
; -}; - -class ListFlyout extends React.Component { - constructor(props) { - super(props); - this.state = { - open: false, - }; - - this.change = e => this.setState({ - open: !this.state.open, - }); - } - - render() { - const { open } = this.state; - return ( -
- - } - position="right-top"> - - -
- ); - } - -} - - -``` - -Likewise the next example. - -```jsx -const { Flyout2, Button, Avatar, InteractiveList } = require('precise-ui'); - -const CustomWrapper = function (props) { - const { children, ...rest } = props; - return
{children}
; -}; - -class ListFlyout extends React.Component { - constructor(props) { - super(props); - this.state = { - open: false, - }; - - this.change = e => this.setState({ - open: !this.state.open, - }); - } - - render() { - const { open } = this.state; - return ( -
- - } - position="right-top"> - - -
- ); - } - -} - - -``` - -**Playground** - -A little playground for the various options. - -```jsx -const { Flyout2, RadioButton, StackPanel, StackItem } = require('precise-ui'); - -const positions = [ - 'top', - 'right', - 'bottom', - 'left', - 'top-left', - 'top-right', - 'bottom-left', - 'bottom-right', - 'right-top', - 'right-bottom', - 'left-top', - 'left-bottom', -]; - -class LongExample extends React.Component { - constructor(props) { - super(props); - this.state = { - open: false, - position: 'left', - contentSize: 'long', - positionType: 'position', - }; - } - - switch() { - this.setState({ - open: !this.state.open, - }); - } - - changeContentType(type) { - this.setState({ - contentSize: type, - }); - } - - changePositionType(positionType) { - this.setState({ - positionType - }); - } - - getShortContent() { - return
Flyout
; - } - - getMiddleContent() { - return ( -
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit
-
Lorem ipsum dolor sit amet, consectetur adipiscing elit
-
- ); - } - - getLongContent() { - return ( -
- {Array.from({ length: 100 }, (value, index) => ( -
- Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et - dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex - ea commodo consequat. -
- ))} -
- ); - } - - render() { - const { open } = this.state; - return ( -
- - - - - - Position: -
- {positions.map(position => ( -
- this.setState({ position })}> - {position} - -
- ))} -
-
- - Content length: - {['short', 'middle', 'long'].map(size => ( -
- this.changeContentType(size)}> - {size} - -
- ))} -
- - Position type: - {[{ - positionType: 'position', - description: 'enforces defined position', - }, { - positionType: 'defaultPosition', - description: 'flyout takes the position only if enough space', - }].map(({positionType, description}) => ( -
- this.changePositionType(positionType)}> - {positionType} ({description}) - -
- ))} -
-
-
- ); - } -} - -; -``` diff --git a/src/components/Flyout2/index.tsx b/src/components/Flyout2/index.tsx deleted file mode 100644 index 725e3edb..00000000 --- a/src/components/Flyout2/index.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import * as React from 'react'; -import { usePopper, Modifier } from 'react-popper'; -import { withClickOutsideFC, WithClickOutsideFCProps } from '../../hoc/withClickOutsideFC'; -import styled, { css, themed } from '../../utils/styled'; -import { getFontStyle } from '../../textStyles'; -import { distance } from '../../distance'; -import { FlyoutProps } from '../Flyout/Flyout.types.part'; -import { mapFlyoutPositionToPopperPlacement, calculateArrowStyleOverrides } from './helpers'; -const { useState, useEffect } = React; -import { flyout } from '../../themes'; - -const FlyoutContainer = styled.div` - position: relative; - display: inline-block; - width: fit-content; -`; -FlyoutContainer.displayName = 'FlyoutContainer'; - -const FlyoutTarget = styled.div``; -FlyoutTarget.displayName = 'FlyoutTarget'; - -const FlyoutBody = styled.div( - themed( - ({ theme }) => css` - ${getFontStyle({ size: 'medium' })} - z-index: 100; - position: absolute; - width: fit-conent; - box-shadow: 0 2px 6px 0 rgba(75, 78, 82, 0.2); - border: 1px solid ${theme.ui4}; - overflow: visible; - &[data-popper-reference-hidden='true'] { - visibility: hidden; - } - `, - ), -); -FlyoutBody.displayName = 'FlyoutBody'; - -const FlyoutArrow = styled.div( - themed( - ({ theme }) => css` - pointer-events: none; - position: absolute; - z-index: 101; - width: ${theme.flyout.arrowSize}px; - height: ${theme.flyout.arrowSize}px; - - :before { - content: ' '; - position: absolute; - top: ${theme.flyout.arrowSize}px; - left: 0; - border-style: solid; - border-width: ${theme.flyout.arrowSize / 2}px; - border-color: ${theme.ui4} transparent transparent transparent; - } - - :after { - content: ' '; - position: absolute; - top: ${theme.flyout.arrowSize}px; - left: 0; - border-style: solid; - border-width: ${theme.flyout.arrowSize / 2 - 1}px; - margin-left: 1px; - border-color: ${theme.flyout.background} transparent transparent transparent; - } - `, - ), -); -FlyoutArrow.displayName = 'FlyoutArrow'; - -interface FlyoutContentProps { - noGutter?: boolean; -} - -const FlyoutContent = styled.div( - themed( - ({ theme, noGutter }) => css` - overflow: auto; - background: ${theme.flyout.background}; - color: ${theme.flyout.textColor}; - max-width: ${theme.flyout.maxWidth}; - max-height: ${theme.flyout.maxHeight}; - ${noGutter ? '' : `padding: ${distance.small} ${distance.medium};`} - `, - ), -); -FlyoutContent.displayName = 'FlyoutContent'; - -const FlyoutInt: React.FC = props => { - const [controlled] = useState(props.open !== undefined); - const [visible, setVisible] = useState(Boolean(props.open)); - const [referenceElement, setReferenceElement] = useState(); - const [popperElement, setPopperElement] = useState(); - const [arrowElement, setArrowElement] = useState(); - - const popperModifiers: Array> = [ - { name: 'hide' }, - { name: 'flip', enabled: !props.position }, - { name: 'arrow', options: { element: arrowElement } }, - { name: 'offset', options: { offset: [0, props.offset || 4 + flyout.arrowSize / 2] } }, - ]; - - if (!props.position) { - popperModifiers.push({ - name: 'preventOverflow', - options: { - altAxis: true, - }, - }); - } - - const { styles, attributes } = usePopper(referenceElement, popperElement, { - placement: mapFlyoutPositionToPopperPlacement(props.position || props.defaultPosition), - modifiers: popperModifiers, - }); - - useEffect(() => setVisible(Boolean(props.open)), [props.open]); - useEffect(() => changeVisibility(false), [props.outsideClickEvent]); - - const onClick = () => changeVisibility(!visible); - - const changeVisibility = (nextVisibility: boolean) => { - if (controlled || nextVisibility === visible) { - return; - } - typeof props.onChange === 'function' && props.onChange({ open: nextVisibility }); - setVisible(nextVisibility); - }; - - return ( - - - {props.children} - - {visible && props.content && ( - - {props.content} - - - )} - - ); -}; -FlyoutInt.displayName = 'FlyoutInt'; - -export const Flyout2 = withClickOutsideFC(FlyoutInt); -Flyout2.displayName = 'Flyout2'; diff --git a/src/components/index.ts b/src/components/index.ts index cc252f99..88b68fd0 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -33,7 +33,6 @@ export * from './FileSelect'; export * from './FileUploader'; export * from './FileUploaderDetails'; export * from './Flyout'; -export * from './Flyout2'; export * from './Form'; export * from './GradientContainer'; export * from './Grid'; diff --git a/src/hoc/withClickOutsideFC.tsx b/src/hoc/withClickOutsideFC.tsx index bc8c1c01..dd3eb21a 100644 --- a/src/hoc/withClickOutsideFC.tsx +++ b/src/hoc/withClickOutsideFC.tsx @@ -14,7 +14,7 @@ interface OutsideClickProps { } export function withClickOutsideFC

(Component: React.FC

) { - class OutsideClickAdapter extends React.Component { + class OutsideClickAdapter extends React.Component { static displayName: string; constructor(props: P) { super(props); diff --git a/src/components/Flyout2/helpers.tsx b/src/utils/flyoutCompatibilityHelpers.tsx similarity index 94% rename from src/components/Flyout2/helpers.tsx rename to src/utils/flyoutCompatibilityHelpers.tsx index 61d170e1..8e5ca764 100644 --- a/src/components/Flyout2/helpers.tsx +++ b/src/utils/flyoutCompatibilityHelpers.tsx @@ -1,4 +1,4 @@ -import { FlyoutPosition } from '../Flyout/Flyout.types.part'; +import { FlyoutPosition } from '../components/Flyout/Flyout.types.part'; import { Placement } from '@popperjs/core'; export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): Placement => { @@ -19,7 +19,7 @@ export const mapFlyoutPositionToPopperPlacement = (position?: FlyoutPosition): P } as const)[position]; } - return 'auto'; + return 'bottom'; }; export const calculateArrowStyleOverrides = ( From dad9079a77a08a2f1d0a7fe71f77035e542d39ba Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 09:21:41 +0300 Subject: [PATCH 10/17] 2.1.0 --- CHANGELOG.md | 4 ++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67a22250..acc9af95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Precise UI Changelog +## 2.1.0 + +- Flyout component reimplemented using Popper.js (556349) +- HOC for using functional components with react-onclickoutside implemented ## 2.0.1 - Fix onChangeRow() method in DateField component diff --git a/package-lock.json b/package-lock.json index 51a41e01..ee9dff93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "precise-ui", - "version": "2.0.1", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7bbfbdad..674f391c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "precise-ui", - "version": "2.0.1", + "version": "2.1.0", "description": "Precise UI React component library powered by Styled Components.", "keywords": [ "react", From af6a2fa7895eda4677cdd820ef8e20185d0898b9 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 11:25:52 +0300 Subject: [PATCH 11/17] Fix versioning and string concatenation style --- CHANGELOG.md | 2 -- src/utils/flyoutCompatibilityHelpers.tsx | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index acc9af95..f30c8497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,6 @@ - Flyout component reimplemented using Popper.js (556349) - HOC for using functional components with react-onclickoutside implemented -## 2.0.1 - - Fix onChangeRow() method in DateField component - Use modern Popper modifiers format in DateField component diff --git a/src/utils/flyoutCompatibilityHelpers.tsx b/src/utils/flyoutCompatibilityHelpers.tsx index 8e5ca764..1b5d668f 100644 --- a/src/utils/flyoutCompatibilityHelpers.tsx +++ b/src/utils/flyoutCompatibilityHelpers.tsx @@ -37,19 +37,19 @@ export const calculateArrowStyleOverrides = ( const placementDependentStyles: React.CSSProperties = { top: { bottom: 0, - transform: transform + ` rotate(${0}deg)`, + transform: `${transform} rotate(${0}deg)`, }, bottom: { top: 0, - transform: transform + ` rotate(${180}deg)`, + transform: `${transform} rotate(${180}deg)`, }, left: { right: 0, - transform: transform + ` rotate(${-90}deg)`, + transform: `${transform} rotate(${-90}deg)`, }, right: { left: 0, - transform: transform + ` rotate(${90}deg)`, + transform: `${transform} rotate(${90}deg)`, }, }[primaryPlacement as 'top' | 'bottom' | 'left' | 'right']; From b2604044d8a207940865c328635130f74e2c79ec Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 12:06:00 +0300 Subject: [PATCH 12/17] Fix Material UI icons broken link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bc169747..4199cc06 100644 --- a/README.md +++ b/README.md @@ -216,4 +216,4 @@ To find out about the currently released version you have two options. Either yo Precise UI is released using the MIT license. For more information see the [license file](LICENSE). -We are using some icons from [Material UI Icons](https://github.com/mui-org/material-ui/tree/master/packages/material-ui-icons). Their code and design is covered by the respective license of [Material UI](https://github.com/mui-org/material-ui) (MIT). +We are using some icons from [Material UI Icons](https://github.com/mui-org/material-ui/tree/master/packages/mui-icons-material). Their code and design is covered by the respective license of [Material UI](https://github.com/mui-org/material-ui) (MIT). From 06a2dd39daff793d88d86a65c75fb26d337b87fd Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 12:54:21 +0300 Subject: [PATCH 13/17] Sync dependencies --- package-lock.json | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index ee9dff93..73bd97be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3709,9 +3709,9 @@ "dev": true }, "@popperjs/core": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.10.1.tgz", - "integrity": "sha512-HnUhk1Sy9IuKrxEMdIRCxpIqPw6BFsbYSEUO9p/hNw5sMld/+3OLMWQP80F8/db9qsv3qUjs7ZR5bS/R+iinXw==" + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", + "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==" }, "@types/babel__core": { "version": "7.1.2", diff --git a/package.json b/package.json index 674f391c..e95da0f7 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ } }, "dependencies": { - "@popperjs/core": "^2.10.1", + "@popperjs/core": "^2.9.2", "date-fns": "^2.23.0", "memoize-one": "^5.1.0", "react-datepicker": "^4.2.1", From b3029db3b70a6c27c4a1247d050047f7a5a8780c Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 16:13:35 +0300 Subject: [PATCH 14/17] Implement dependency duplication in pre-push hook --- package.json | 2 +- tools/deps-duplication-detector.js | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tools/deps-duplication-detector.js diff --git a/package.json b/package.json index e95da0f7..7d751779 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ }, "husky": { "hooks": { - "pre-push": "npm run test & npm run build" + "pre-push": "node tools/deps-duplication-detector.js && npm run test & npm run build" } }, "dependencies": { diff --git a/tools/deps-duplication-detector.js b/tools/deps-duplication-detector.js new file mode 100644 index 00000000..3d6a98f9 --- /dev/null +++ b/tools/deps-duplication-detector.js @@ -0,0 +1,26 @@ +const { exec } = require('child_process'); + +const directDeps = require('../package.json').dependencies; + +async function findDuplicates(deps) { + await Promise.all(deps.map(dep => new Promise((resolve, reject) => { + const p = exec(`npm ls ${dep} --json`); + let npmOutput = ''; + p.stdout.on('data', (data) => npmOutput += data); + + p.on('exit', () => { + const versionNumbers = Object.entries(JSON.parse(npmOutput).dependencies) + .map(([name, value]) => name === dep ? value.version : value.dependencies[dep].version + ); + + if (new Set(versionNumbers).size > 1) { + console.error(`Found duplicate dependency for ${dep}: [${versionNumbers.join(', ')}]\n${npmOutput}`); + process.exit(1); + } + + resolve(); + }) + }))) +}; + +findDuplicates(Object.keys(directDeps)) From aa5105ff8af052839db6d7e0dae695e12e7b8ee1 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 16:26:49 +0300 Subject: [PATCH 15/17] Sync react-onclickoutside version --- package-lock.json | 130 +++++++++++++++++++++++----------------------- package.json | 4 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index 73bd97be..44b23ed4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9104,28 +9104,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -9136,14 +9136,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -9154,42 +9154,42 @@ }, "chownr": { "version": "1.1.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true }, "debug": { "version": "2.6.9", - "resolved": "", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "optional": true, @@ -9199,28 +9199,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -9230,14 +9230,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -9254,7 +9254,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, @@ -9269,14 +9269,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -9286,7 +9286,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -9296,7 +9296,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -9307,7 +9307,7 @@ }, "inherits": { "version": "2.0.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true @@ -9321,7 +9321,7 @@ }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -9331,14 +9331,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -9348,7 +9348,7 @@ }, "minipass": { "version": "2.3.5", - "resolved": "", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, @@ -9359,7 +9359,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, @@ -9379,14 +9379,14 @@ }, "ms": { "version": "2.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true, "optional": true }, "needle": { "version": "2.2.4", - "resolved": "", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.2.4.tgz", "integrity": "sha512-HyoqEb4wr/rsoaIDfTH2aVL9nWtQqba2/HvMv+++m8u0dz808MaagKILxtfeSN7QU7nvbQ79zk3vYOJp9zsNEA==", "dev": true, "optional": true, @@ -9398,7 +9398,7 @@ }, "node-pre-gyp": { "version": "0.10.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz", "integrity": "sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A==", "dev": true, "optional": true, @@ -9417,7 +9417,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -9428,14 +9428,14 @@ }, "npm-bundled": { "version": "1.0.5", - "resolved": "", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.5.tgz", "integrity": "sha512-m/e6jgWu8/v5niCUKQi9qQl8QdeEduFA96xHDDzFGqly0OOjI7c+60KM/2sppfnUU9JJagf+zs+yGhqSOFj71g==", "dev": true, "optional": true }, "npm-packlist": { "version": "1.2.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.2.0.tgz", "integrity": "sha512-7Mni4Z8Xkx0/oegoqlcao/JpPCPEMtUvsmB0q7mgvlMinykJLSRTYuFqoQLYgGY8biuxIeiHO+QNJKbCfljewQ==", "dev": true, "optional": true, @@ -9446,7 +9446,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -9459,21 +9459,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -9483,21 +9483,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -9508,21 +9508,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -9544,7 +9544,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -9560,7 +9560,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, @@ -9570,49 +9570,49 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true }, "semver": { "version": "5.6.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", - "resolved": "", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -9624,7 +9624,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -9634,7 +9634,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -9644,14 +9644,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "resolved": "", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, @@ -9667,14 +9667,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -9684,14 +9684,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.3", - "resolved": "", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true @@ -17352,9 +17352,9 @@ "dev": true }, "react-onclickoutside": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.8.0.tgz", - "integrity": "sha512-5Q4Rn7QLEoh7WIe66KFvYIpWJ49GeHoygP1/EtJyZjXKgrWH19Tf0Ty3lWyQzrEEDyLOwUvvmBFSE3dcDdvagA==" + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.11.2.tgz", + "integrity": "sha512-640486eSwU/t5iD6yeTlefma8dI3bxPXD93hM9JGKyYITAd0P1JFkkcDeyHZRqNpY/fv1YW0Fad9BXr44OY8wQ==" }, "react-popper": { "version": "2.2.5", diff --git a/package.json b/package.json index 7d751779..3d9a99eb 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ }, "husky": { "hooks": { - "pre-push": "node tools/deps-duplication-detector.js && npm run test & npm run build" + "pre-push": "node tools/deps-duplication-detector.js && (npm run test & npm run build)" } }, "dependencies": { @@ -47,7 +47,7 @@ "date-fns": "^2.23.0", "memoize-one": "^5.1.0", "react-datepicker": "^4.2.1", - "react-onclickoutside": "^6.7.1", + "react-onclickoutside": "^6.11.2", "react-popper": "^2.2.5", "typescript-plugin-inner-jsx": "^0.1.9" }, From 386d4ac820101c9a68b036156d6a514d3f4a1015 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 17:11:17 +0300 Subject: [PATCH 16/17] Put deps script into 'scripts' section --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d9a99eb..8f2bfc8d 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "build:demo:test": "cross-env NODE_ENV=test styleguidist build", "build:es5": "build-with-transform-jsx -t es5 --outDir dist/es5 --declaration --inlineSourceMap --inlineSources --config ./tsconfig.json", "build:es6": "build-with-transform-jsx -t es6 -m es6 --outDir dist/es6 --declaration --inlineSourceMap --inlineSources --config ./tsconfig.json", + "check-deps": "node tools/deps-duplication-detector.js", "deploy": "gh-pages -d styleguide", "icongen": "node tools/convert-icons.js", "lint": "tslint -c tslint.json 'src/**/*.{ts,tsx}' --project tsconfig.json", @@ -39,7 +40,7 @@ }, "husky": { "hooks": { - "pre-push": "node tools/deps-duplication-detector.js && (npm run test & npm run build)" + "pre-push": "npm run check-deps && (npm run test & npm run build)" } }, "dependencies": { From 6da0240e457c8f0be0d54f87248bbc6f519cd426 Mon Sep 17 00:00:00 2001 From: Illia Simenko Date: Tue, 14 Sep 2021 17:11:42 +0300 Subject: [PATCH 17/17] Remove redundant templating --- src/utils/flyoutCompatibilityHelpers.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/flyoutCompatibilityHelpers.tsx b/src/utils/flyoutCompatibilityHelpers.tsx index 1b5d668f..331aedd0 100644 --- a/src/utils/flyoutCompatibilityHelpers.tsx +++ b/src/utils/flyoutCompatibilityHelpers.tsx @@ -37,19 +37,19 @@ export const calculateArrowStyleOverrides = ( const placementDependentStyles: React.CSSProperties = { top: { bottom: 0, - transform: `${transform} rotate(${0}deg)`, + transform, }, bottom: { top: 0, - transform: `${transform} rotate(${180}deg)`, + transform: `${transform} rotate(180deg)`, }, left: { right: 0, - transform: `${transform} rotate(${-90}deg)`, + transform: `${transform} rotate(-90deg)`, }, right: { left: 0, - transform: `${transform} rotate(${90}deg)`, + transform: `${transform} rotate(90deg)`, }, }[primaryPlacement as 'top' | 'bottom' | 'left' | 'right'];