Skip to content

Commit

Permalink
extract restriction entry into seperate component
Browse files Browse the repository at this point in the history
  • Loading branch information
matthias-luger committed Apr 24, 2024
1 parent 5d492cf commit 65a7f36
Show file tree
Hide file tree
Showing 8 changed files with 322 additions and 259 deletions.
Expand Up @@ -17,37 +17,6 @@
}
}

.restrictionContainer {
padding-right: 24px;
padding-top: 8px;
padding-bottom: 8px;
}

.removeFilter {
cursor: pointer;
margin-left: 10px;
}

.cancelEditButton {
cursor: pointer;
}

.editButton {
cursor: pointer;
margin-left: 10px;
}

.restrictionMarkedAsEdit {
border-width: 3px;
border-color: cornflowerblue;
}

.label {
width: 300px;
float: left;
font-weight: bold;
}

.searchBarContainer{
display: flex;
justify-content: space-between;
Expand All @@ -69,14 +38,7 @@
gap: 5px;
}

.restriction {
max-height: 100%;
}

@media all and (max-width: 1024px) {
.restriction {
width: 100%;
}

.searchBarContainer{
flex-wrap: wrap;
Expand All @@ -91,8 +53,6 @@
flex: auto;
}



.refreshAndDeleteButtonContainer{
flex-wrap: wrap;
}
Expand Down
259 changes: 60 additions & 199 deletions components/Flipper/FlipRestrictionList/FlipRestrictionList.tsx

Large diffs are not rendered by default.

@@ -0,0 +1,40 @@
.restrictionContainer {
padding-right: 24px;
padding-top: 8px;
padding-bottom: 8px;
}

.restrictionMarkedAsEdit {
border-width: 3px;
border-color: cornflowerblue;
}

.label {
width: 300px;
float: left;
font-weight: bold;
}

.removeFilter {
cursor: pointer;
margin-left: 10px;
}

.cancelEditButton {
cursor: pointer;
}

.editButton {
cursor: pointer;
margin-left: 10px;
}

.restriction {
max-height: 100%;
}

@media all and (max-width: 1024px) {
.restriction {
width: 100%;
}
}
@@ -0,0 +1,195 @@
import { Badge, Card, Image, ToggleButton, ToggleButtonGroup } from 'react-bootstrap'
import styles from './FlipRestrictionEntry.module.css'
import ApiSearchField from '../../../Search/ApiSearchField'
import { getStyleForTier } from '../../../../utils/Formatter'
import Tooltip from '../../../Tooltip/Tooltip'
import SaveIcon from '@mui/icons-material/Save'
import ItemFilterPropertiesDisplay from '../../../ItemFilter/ItemFilterPropertiesDisplay'
import { memo, useEffect, useState } from 'react'
import api from '../../../../api/ApiHelper'
import EditIcon from '@mui/icons-material/Edit'
import DuplicateIcon from '@mui/icons-material/ControlPointDuplicate'
import DeleteIcon from '@mui/icons-material/Delete'
import { areEqual } from 'react-window'

interface Props {
restriction: FlipRestriction
isAnyRestrictionInEditMode: boolean
onSaveClick(): void
onEditClick(): void
onCreateDuplicateClick(): void
onDeleteClick(): void
onRemoveFilterClick(restriction: FlipRestriction): void
onRestrictionChange(restriction: FlipRestriction): void
style: React.CSSProperties
}

function FlipRestrictionListEntry(props: Props) {
return (
<div className={styles.restrictionContainer} style={props.style}>
<Card className={`${styles.restriction} ${props.restriction.isEdited ? styles.restrictionMarkedAsEdit : ''}`}>
<Card.Header
style={{
padding: '10px',
display: 'flex',
justifyContent: 'space-between'
}}
>
{props.restriction.isEdited ? (
<ToggleButtonGroup
style={{ maxWidth: '200px', marginBottom: '5px' }}
type="radio"
name="options"
value={props.restriction.type}
onChange={newValue => {
let newRestriction = { ...props.restriction }
newRestriction.type = newValue as 'blacklist' | 'whitelist'
props.onRestrictionChange(newRestriction)
}}
>
<ToggleButton
id={'blacklistToggleButton-' + props.restriction.itemKey}
value={'blacklist'}
variant={props.restriction.type === 'blacklist' ? 'primary' : 'secondary'}
size="sm"
>
Blacklist
</ToggleButton>
<ToggleButton
id={'whitelistToggleButton-' + props.restriction.itemKey}
value={'whitelist'}
variant={props.restriction.type === 'whitelist' ? 'primary' : 'secondary'}
size="sm"
>
Whitelist
</ToggleButton>
</ToggleButtonGroup>
) : (
<Badge style={{ marginRight: '10px' }} bg={props.restriction.type === 'blacklist' ? 'danger' : 'success'}>
{props.restriction.type.toUpperCase()}
</Badge>
)}
<div
style={{
width: '-webkit-fill-available',
float: 'left'
}}
>
{props.restriction.isEdited ? (
<ApiSearchField
multiple={false}
className={styles.multiSearch}
onChange={(items, text) => {
if (items.length === 0 && text != '') return

let newItem: Item | undefined =
items && items.length > 0
? {
tag: items[0].id,
name: items[0].dataItem.name,
iconUrl: items[0].dataItem.iconUrl
}
: undefined
let newRestriction = { ...props.restriction }
newRestriction.item = newItem
props.onRestrictionChange(newRestriction)
}}
searchFunction={api.itemSearch}
defaultSelected={
props.restriction.item
? [
{
dataItem: {
iconUrl: props.restriction.item.iconUrl || '',
name: props.restriction.item.name || '-'
},
id: props.restriction.item.tag || '',
label: props.restriction.item.name || '-'
} as unknown as SearchResultItem
]
: undefined
}
/>
) : (
props.restriction.item && (
<>
<Image
crossOrigin="anonymous"
src={props.restriction.item?.iconUrl || ''}
height="24"
width="24"
alt=""
style={{
marginRight: '5px'
}}
loading="lazy"
/>
<span style={getStyleForTier(props.restriction.item?.tier)}>{props.restriction.item?.name}</span>
</>
)
)}
</div>
<div
style={{
display: 'flex'
}}
>
{props.restriction.isEdited ? (
<div
className={styles.cancelEditButton}
onClick={() => {
props.onSaveClick()
}}
>
<Tooltip type="hover" content={<SaveIcon />} tooltipContent={<p>Save</p>} />
</div>
) : (
<div className={styles.editButton} onClick={props.onEditClick}>
<Tooltip type="hover" content={<EditIcon />} tooltipContent={<p>Edit restriction</p>} />
</div>
)}
{!props.isAnyRestrictionInEditMode ? (
<>
<div className={styles.removeFilter} onClick={props.onCreateDuplicateClick}>
<Tooltip type="hover" content={<DuplicateIcon />} tooltipContent={<p>Create duplicate</p>} />
</div>
<div className={styles.removeFilter} onClick={props.onDeleteClick}>
<Tooltip type="hover" content={<DeleteIcon color="error" />} tooltipContent={<p>Remove restriction</p>} />
</div>
</>
) : null}
</div>
</Card.Header>
<Card.Body>
{props.restriction.itemFilter ? (
<ItemFilterPropertiesDisplay
key={props.restriction.itemKey}
filter={props.restriction.itemFilter}
isEditable={props.restriction.isEdited}
onAfterEdit={filter => {
let newRestriction = { ...props.restriction }
newRestriction.itemFilter = filter
props.onRestrictionChange(newRestriction)
}}
/>
) : null}
<div className="ellipse">
{props.restriction.tags?.map(tag => (
<Badge
key={tag}
bg="dark"
style={{
marginRight: '5px'
}}
>
{tag}
</Badge>
))}
</div>
</Card.Body>
</Card>
</div>
)
}

export default memo(FlipRestrictionListEntry, areEqual)
3 changes: 2 additions & 1 deletion components/ItemFilter/ItemFilterPropertiesDisplay.tsx
Expand Up @@ -10,6 +10,7 @@ import { useForceUpdate } from '../../utils/Hooks'
interface Props {
filter?: ItemFilter
onAfterEdit?(filter: ItemFilter)
isEditable?: boolean
}

const DATE_FORMAT_FILTER = ['EndBefore', 'EndAfter']
Expand Down Expand Up @@ -113,7 +114,7 @@ function ItemFilterPropertiesDisplay(props: Props) {
return (
<p key={key}>
{camelCaseToSentenceCase(key)}: {display}
{props.onAfterEdit ? (
{props.isEditable ? (
<span
style={{ color: 'red', cursor: 'pointer' }}
onClick={() => {
Expand Down
13 changes: 8 additions & 5 deletions components/Search/ApiSearchField.tsx
@@ -1,5 +1,5 @@
'use client'
import { forwardRef, Ref, useState } from 'react'
import { forwardRef, Ref, useRef, useState } from 'react'
import { AsyncTypeahead } from 'react-bootstrap-typeahead'
import { v4 as generateUUID } from 'uuid'
import Typeahead from 'react-bootstrap-typeahead/types/core/Typeahead'
Expand All @@ -8,9 +8,10 @@ import { Option } from 'react-bootstrap-typeahead/types/types'
import styles from './Search.module.css'
import Image from 'next/image'
import { getStyleForTier } from '../../utils/Formatter'
import { Form } from 'react-bootstrap'

interface Props {
onChange(selected: SearchResultItem[])
onChange(selected: SearchResultItem[], searchText?: string)
disabled?: boolean
placeholder?: string
defaultValue?: string
Expand All @@ -21,13 +22,14 @@ interface Props {
multiple: boolean
}

export default forwardRef((props: Props, ref: Ref<Typeahead>) => {
export default (props: Props) => {
let [uuid] = useState(generateUUID())
let [results, setResults] = useState<SearchResultItem[]>([])
let [isLoading, setIsLoading] = useState(false)
let ref = useRef<Typeahead>(null)

function _onChange(selected: Option[]) {
props.onChange(selected as SearchResultItem[])
props.onChange(selected as SearchResultItem[], ref?.current?.getInput()?.value)
}

function handleSearch(query) {
Expand Down Expand Up @@ -56,6 +58,7 @@ export default forwardRef((props: Props, ref: Ref<Typeahead>) => {
inputProps={{ className: styles.multiInputfield }}
filterBy={() => true}
isLoading={isLoading}
key={uuid}
labelKey="label"
renderMenuItemChildren={(option, { text }) => {
let o: any = option
Expand Down Expand Up @@ -86,4 +89,4 @@ export default forwardRef((props: Props, ref: Ref<Typeahead>) => {
multiple={props.multiple}
/>
)
})
}

0 comments on commit 65a7f36

Please sign in to comment.