Просмотр исходного кода

Make Dropdown components consistent

`AutocompleteDropdown` and `Dropdown` components should share a lot of
the behaviour and look. So, the `Dropdown` component now exports some of
the common elements and functions.
Sergiu Miclea 8 лет назад
Родитель
Сommit
3d8558f2ae

+ 4 - 42
src/components/molecules/AutocompleteDropdown/index.jsx

@@ -20,13 +20,13 @@ import styled, { css } from 'styled-components'
 import ReactDOM from 'react-dom'
 
 import AutocompleteInput from '../../atoms/AutocompleteInput'
+import { Tip, updateTipStyle, scrollItemIntoView } from '../Dropdown'
+import tipImage from '../Dropdown/images/tip'
 
 import Palette from '../../styleUtils/Palette'
 import DomUtils from '../../../utils/DomUtils'
 import StyleProps from '../../styleUtils/StyleProps'
 
-import tipImage from './images/tip'
-
 const getWidth = props => {
   if (props.width) {
     return props.width - 2
@@ -55,22 +55,6 @@ const ListItems = styled.div`
   max-height: 400px;
   overflow: auto;
 `
-const Tip = styled.div`
-  position: absolute;
-  width: 16px;
-  height: 8px;
-  right: 8px;
-  top: -8px;
-  z-index: 11;
-  transition: all ${StyleProps.animations.swift};
-  overflow: hidden;
-  svg {
-    #path {
-      transition: all ${StyleProps.animations.swift};
-      fill: ${props => props.primary ? Palette.primary : 'white'};
-    }
-  }
-`
 const SearchNotFound = styled.div`
   padding: 8px;
   cursor: default;
@@ -307,17 +291,8 @@ class AutocompleteDropdown extends React.Component<Props, State> {
   }
 
   scrollIntoView() {
-    if (!this.listRef || !this.listItemsRef) {
-      return
-    }
-
     let itemIndex = this.state.filteredItems.findIndex(i => this.getValue(i) === this.getValue(this.props.selectedItem))
-    if (itemIndex === -1 || !this.listItemsRef.children[itemIndex]) {
-      return
-    }
-
-    // $FlowIssue
-    this.listItemsRef.children[itemIndex].parentNode.scrollTop = this.listItemsRef.children[itemIndex].offsetTop - this.listItemsRef.children[itemIndex].parentNode.offsetTop - 32
+    scrollItemIntoView(this.listRef, this.listItemsRef, itemIndex)
   }
 
   updateListPosition() {
@@ -347,20 +322,7 @@ class AutocompleteDropdown extends React.Component<Props, State> {
 
     if (this.listItemsRef) {
       this.listItemsRef.style.maxHeight = `${listHeight}px`
-      if (this.tipRef && this.firstItemRef) {
-        let svgPath = this.tipRef.querySelector('#path')
-        if (svgPath) {
-          if (this.listItemsRef.clientHeight < this.listItemsRef.scrollHeight) {
-            // $FlowIssue
-            svgPath.style.fill = 'white'
-            this.firstItemRef.style.borderTopRightRadius = '0'
-          } else {
-            // $FlowIssue
-            svgPath.style.fill = ''
-            this.firstItemRef.style.borderTopRightRadius = ''
-          }
-        }
-      }
+      updateTipStyle(this.listItemsRef, this.tipRef, this.firstItemRef)
     }
   }
 

+ 0 - 0
src/components/molecules/AutocompleteDropdown/images/tip.js → src/components/molecules/Dropdown/images/tip.js


+ 55 - 21
src/components/molecules/Dropdown/index.jsx

@@ -26,6 +26,7 @@ import DomUtils from '../../../utils/DomUtils'
 import StyleProps from '../../styleUtils/StyleProps'
 
 import checkmarkImage from './images/checkmark'
+import tipImage from './images/tip'
 
 const getWidth = props => {
   if (props.large) {
@@ -55,20 +56,21 @@ const ListItems = styled.div`
   max-height: 400px;
   overflow: auto;
 `
-const Tip = styled.div`
+export const Tip = styled.div`
   position: absolute;
-  width: 10px;
-  height: 10px;
-  background: ${props => props.primary ? Palette.primary : 'white'};
-  border-top: 1px solid ${Palette.grayscale[3]};
-  border-left: 1px solid ${Palette.grayscale[3]};
-  border-bottom: 1px solid ${props => props.primary ? Palette.primary : 'white'};
-  border-right: 1px solid ${props => props.primary ? Palette.primary : 'white'};
-  transform: rotate(45deg);
+  width: 16px;
+  height: 8px;
   right: 8px;
-  top: -6px;
+  top: -8px;
   z-index: 11;
   transition: all ${StyleProps.animations.swift};
+  overflow: hidden;
+  svg {
+    #path {
+      transition: all ${StyleProps.animations.swift};
+      fill: ${props => props.primary ? Palette.primary : 'white'};
+    }
+  }
 `
 const Checkmark = styled.div`
   ${StyleProps.exactWidth('16px')}
@@ -127,6 +129,38 @@ const Separator = styled.div`
 `
 const Labels = styled.div``
 
+export const updateTipStyle = (listItemsRef: HTMLElement, tipRef: HTMLElement, firstItemRef: HTMLElement) => {
+  if (tipRef && firstItemRef) {
+    let svgPath = tipRef.querySelector('#path')
+    if (svgPath) {
+      if (listItemsRef.clientHeight < listItemsRef.scrollHeight) {
+        // $FlowIssue
+        svgPath.style.fill = 'white'
+        firstItemRef.style.borderTopRightRadius = '0'
+      } else {
+        // $FlowIssue
+        svgPath.style.fill = ''
+        firstItemRef.style.borderTopRightRadius = ''
+      }
+    }
+  }
+}
+
+export const scrollItemIntoView = (
+  listRef: HTMLElement,
+  listItemsRef: HTMLElement,
+  itemIndex: number
+) => {
+  if (!listRef || !listItemsRef) {
+    return
+  }
+  if (itemIndex === -1 || !listItemsRef.children[itemIndex]) {
+    return
+  }
+  // $FlowIssue
+  listItemsRef.children[itemIndex].parentNode.scrollTop = listItemsRef.children[itemIndex].offsetTop - listItemsRef.children[itemIndex].parentNode.offsetTop - 32
+}
+
 type Props = {
   selectedItem: any,
   items: any[],
@@ -158,6 +192,7 @@ class Dropdown extends React.Component<Props, State> {
   buttonRef: HTMLElement
   listRef: HTMLElement
   listItemsRef: HTMLElement
+  firstItemRef: HTMLElement
   tipRef: HTMLElement
   scrollableParent: HTMLElement
   buttonRect: ClientRect
@@ -297,20 +332,12 @@ class Dropdown extends React.Component<Props, State> {
     let widthDiff = this.listRef.offsetWidth - this.buttonRef.offsetWidth
     this.listRef.style.top = `${listTop + (window.pageYOffset || scrollOffset)}px`
     this.listRef.style.left = `${(this.buttonRect.left + window.pageXOffset) - widthDiff}px`
+    updateTipStyle(this.listItemsRef, this.tipRef, this.firstItemRef)
   }
 
   scrollIntoView() {
-    if (!this.listRef || !this.listItemsRef) {
-      return
-    }
-
     let itemIndex = this.props.items.findIndex(i => this.getValue(i) === this.getValue(this.props.selectedItem))
-    if (itemIndex === -1 || !this.listItemsRef.children[itemIndex]) {
-      return
-    }
-
-    // $FlowIssue
-    this.listItemsRef.children[itemIndex].parentNode.scrollTop = this.listItemsRef.children[itemIndex].offsetTop - this.listItemsRef.children[itemIndex].parentNode.offsetTop - 32
+    scrollItemIntoView(this.listRef, this.listItemsRef, itemIndex)
   }
 
   renderList() {
@@ -329,10 +356,16 @@ class Dropdown extends React.Component<Props, State> {
         }
       }
     })
+    const firstItemValue = this.props.items.length > 0 ? this.getValue(this.props.items[0]) : null
+    const isFirstItemSelected = selectedValue === firstItemValue
 
     let list = ReactDOM.createPortal((
       <List {...this.props} innerRef={ref => { this.listRef = ref }}>
-        <Tip innerRef={ref => { this.tipRef = ref }} primary={this.state.firstItemHover} />
+        <Tip
+          innerRef={ref => { this.tipRef = ref }}
+          primary={this.state.firstItemHover || isFirstItemSelected}
+          dangerouslySetInnerHTML={{ __html: tipImage }}
+        />
         <ListItems innerRef={ref => { this.listItemsRef = ref }}>
           {this.props.items.map((item, i) => {
             if (item.separator === true) {
@@ -346,6 +379,7 @@ class Dropdown extends React.Component<Props, State> {
             let listItem = (
               <ListItem
                 data-test-id="dropdownListItem"
+                innerRef={ref => { if (i === 0) { this.firstItemRef = ref } }}
                 key={value}
                 onMouseDown={() => { this.itemMouseDown = true }}
                 onMouseUp={() => { this.itemMouseDown = false }}