Skip to content

Commit

Permalink
feat: Add Inspector Hybrid Field (#820)
Browse files Browse the repository at this point in the history
* feat: Use new dropdown UI component

* feat: Update Option component to have a minWidth based on the options

* fix: Remove unused method

* feat: Add HybridField component

* fix: Update TextField type prop

* feat: Update dropdown styles

* fix: Option Styles

* fix: Dropdown dynamic width

* fix: Dropdown width

* fix: Dropdown disabled and empty styles
  • Loading branch information
cyaiox committed Nov 8, 2023
1 parent 092e2e1 commit bebdda2
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const PlaySoundAction: React.FC<Props> = ({ value, onUpdate }: Props) => {
}, [payload, onUpdate])

const handleChangeSrc = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLSelectElement>) => {
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
setPayload({ ...payload, src: addBase(value) })
},
[payload, setPayload]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { useArrayState } from '../../../../hooks/useArrayState'
import { useComponentsWith } from '../../../../hooks/sdk/useComponentsWith'
import { Component } from '../../../../lib/sdk/components'
import { Button } from '../../../Button'
import { Dropdown } from '../../../ui/Dropdown'
import { EntityField } from '../../../ui'
import { EntityField, Dropdown } from '../../../ui'
import { AddButton } from '../../AddButton'
import MoreOptionsMenu from '../../MoreOptionsMenu'

Expand Down
19 changes: 11 additions & 8 deletions packages/@dcl/inspector/src/components/ui/Dropdown/Dropdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,25 @@
}

.DropdownContainer .Dropdown:hover {
border: 1px solid var(--base-09);
border-color: var(--base-09);
}

.DropdownContainer .Dropdown.focused {
border: 1px solid var(--base-01);
border-color: var(--base-01);
}

.DropdownContainer .Dropdown.disabled {
background: var(--base-12);
border: 1px solid var(--base-12);
border-color: var(--base-12);
color: var(--base-09);
cursor: default;
pointer-events: none;
}

.DropdownContainer .Dropdown.error {
border-color: var(--error-main);
}

.DropdownContainer .Dropdown .DropIcon {
line-height: 100%;
}
Expand All @@ -56,10 +60,6 @@
padding: 10px;
}

.DropdownContainer .Dropdown .DropdownPlaceholder {
flex: 1;
}

.DropdownContainer .Dropdown .DropdownOptions {
position: absolute;
display: flex;
Expand All @@ -72,6 +72,9 @@
border-radius: 4px;
z-index: 5;
user-select: none;
max-height: 132px;
overflow-y: auto;
cursor: initial;
}
.DropdownContainer .Dropdown .DropdownOptions.searchable .DropdownSearch.TextField {
padding: 0px 7px 6px 7px;
Expand All @@ -85,6 +88,6 @@
.Dropdown
.DropdownOptions.searchable
.DropdownSearch.TextField
.input-container:not(.hovered):not(.focused) {
.InputContainer:not(.hovered):not(.focused) {
border-color: var(--base-12);
}
22 changes: 15 additions & 7 deletions packages/@dcl/inspector/src/components/ui/Dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import React, { useCallback, useMemo, useState } from 'react'
import cx from 'classnames'
import { VscChevronDown as DownArrowIcon, VscSearch as SearchIcon } from 'react-icons/vsc'
import { IoAlertCircleOutline as AlertIcon } from 'react-icons/io5'
import { useOutsideClick } from '../../../hooks/useOutsideClick'
import { TextField } from '../TextField'
import { isErrorMessage } from '../utils'
import { Option } from './Option'
import type { Props as OptionProp } from './Option/types'
import type { Props } from './types'
Expand All @@ -11,14 +13,15 @@ import './Dropdown.css'
const FONT_WIDTH = 12
const FONT_WEIGHT = 700
const WIDTH_CONST = 1200
const EMPTY_WIDTH_CONST = 1455
const ICON_SIZE = 16

function isOptionSelected(currentValue?: any, optionValue?: any) {
return currentValue?.toString() === optionValue?.toString()
}

const Dropdown: React.FC<Props> = (props) => {
const { className, disabled, empty, label, options, searchable, value, onChange, placeholder = '' } = props
const { className, disabled, empty, error, label, options, searchable, value, onChange, placeholder = '' } = props
const [showOptions, setShowOptions] = useState(false)
const [isFocused, setFocus] = useState(false)
const [search, setSearch] = useState('')
Expand Down Expand Up @@ -106,7 +109,7 @@ const Dropdown: React.FC<Props> = (props) => {
}, 0)
}

return (empty?.length ?? 0) * FONT_WIDTH
return ((empty?.length ?? 0) * FONT_WIDTH * FONT_WEIGHT) / EMPTY_WIDTH_CONST
}, [options, empty])

return (
Expand All @@ -116,16 +119,15 @@ const Dropdown: React.FC<Props> = (props) => {
className={cx('Dropdown', className, {
focused: isFocused,
disabled: !!disabled,
open: !!showOptions
open: !!showOptions,
error: !!error
})}
onClick={handleClick}
>
{selectedValue ? (
<Option {...selectedValue} className="DropdownSelection" minWidth={minWidth} />
) : (
<div className="DropdownPlaceholder" style={{ minWidth: minWidth }}>
{placeholder}
</div>
<Option className="DropdownPlaceholder" value={placeholder} minWidth={minWidth} />
)}
{showOptions ? (
<div className={cx('DropdownOptions', { searchable })}>
Expand All @@ -151,14 +153,20 @@ const Dropdown: React.FC<Props> = (props) => {
/>
))
) : (
<Option label={empty} minWidth={minWidth} />
<Option label={empty} minWidth={minWidth} disabled />
)}
</div>
) : null}
<div className="DropIcon">
<DownArrowIcon size={ICON_SIZE} />
</div>
</div>
{isErrorMessage(error) && (
<p className="error-message">
<AlertIcon />
<span>{error}</span>
</p>
)}
</div>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@
width: 100%;
padding: 4px 8px;
color: var(--base-02);
cursor: pointer;
}

.OptionContainer:last-child {
border-bottom-left-radius: 5px;
border-bottom-right-radius: 5px;
}

.OptionContainer.DropdownSelection {
.OptionContainer.DropdownSelection,
.OptionContainer.DropdownPlaceholder {
padding: 0;
}

.OptionContainer.DropdownPlaceholder {
color: var(--base-09);
}

.OptionContainer .OptionContent {
display: flex;
align-items: center;
height: 100%;
gap: 4px;
cursor: pointer;
}

.OptionContainer .Option {
Expand All @@ -32,11 +37,11 @@
text-wrap: nowrap;
}

.OptionContainer:not(.DropdownSelection):hover {
.OptionContainer:not(.DropdownSelection):not(.DropdownPlaceholder):not(.selected):hover {
background: var(--base-12);
}

.OptionContainer:not(.DropdownSelection).disabled {
.OptionContainer:not(.DropdownSelection):not(.DropdownPlaceholder).disabled {
background: var(--base-14);
color: var(--base-11);
cursor: default;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export type Props = React.SelectHTMLAttributes<HTMLSelectElement> & {
label?: string
value?: string | number | readonly string[]
searchable?: boolean
error?: string | boolean
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useRef, useState } from 'react'
import React, { useCallback, useMemo, useRef, useState } from 'react'
import { useDrop } from 'react-dnd'
import cx from 'classnames'
import { VscFolderOpened as FolderIcon } from 'react-icons/vsc'
Expand Down Expand Up @@ -29,12 +29,13 @@ const FileUploadField: React.FC<Props> = ({
disabled,
value,
isEnabledFileExplorer,
error,
onDrop,
isValidFile,
accept = EXTENSIONS
}) => {
const [path, setPath] = useState<string | undefined>(value?.toString())
const [error, setError] = useState<boolean>(false)
const [dropError, setDropError] = useState<boolean>(false)
const inputRef = useRef<HTMLInputElement>(null)
const files = useAppSelector(selectAssetCatalog)

Expand Down Expand Up @@ -76,9 +77,9 @@ const FileUploadField: React.FC<Props> = ({
const element = getNode(node, context.tree, isValid)
if (element) {
handleDrop(withAssetDir(element.asset.src))
setError(false)
setDropError(false)
} else {
setError(true)
setDropError(true)
}
},
canDrop: ({ value, context }: ProjectAssetDrop) => {
Expand All @@ -102,36 +103,40 @@ const FileUploadField: React.FC<Props> = ({
const file = event.target.files?.[0]
if (file && (isAsset(file.name) || isAudioFile(file.name))) {
setPath(file.name)
setError(false)
setDropError(false)
} else {
setError(true)
setDropError(true)
}
},
[setPath, setError]
[setPath, setDropError]
)

const handleChangeTextField = useCallback(
({ target: { value } }: React.ChangeEvent<HTMLSelectElement>) => {
({ target: { value } }: React.ChangeEvent<HTMLInputElement>) => {
if (value && (isAsset(value) || isAudioFile(value))) {
setPath(addBase(value))
setError(false)
setDropError(false)
} else {
setError(true)
setDropError(true)
}
},
[addBase, setPath, setError]
[addBase, setPath, setDropError]
)

const hasError = useMemo(() => {
return error || dropError
}, [error, dropError])

return (
<div className={cx('FileUploadFieldContainer', className)}>
<div className={cx('FileUploadInputContainer', { error, disabled, droppeable: canDrop })}>
<div className={cx('FileUploadInputContainer', { error: hasError, disabled, droppeable: canDrop })}>
<TextField
className="FileUploadFieldInput"
ref={drop}
placeholder="Path File"
onChange={handleChangeTextField}
value={removeBase(path)}
error={!!error}
error={hasError}
disabled={disabled}
drop={isHover}
/>
Expand All @@ -142,7 +147,7 @@ const FileUploadField: React.FC<Props> = ({
</button>
)}
</div>
{error && (
{hasError && (
<div className="FileUploadFieldError">
<AlertIcon size={16} />
File not valid.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.HybridField {
display: flex;
flex-direction: column;
flex: 1;
gap: 4px;
}

.HybridField .InputContainer {
display: flex;
align-items: center;
flex-grow: 1;
gap: 4px;
}

.HybridField .ErrorMessage {
display: flex;
align-items: center;
gap: 4px;
color: var(--error-main);
}
Loading

0 comments on commit bebdda2

Please sign in to comment.