mirror of
https://github.com/dzeiocom/components.git
synced 2025-04-23 19:32:14 +00:00
fix: Multiple inputs bugs
Signed-off-by: Avior <github@avior.me>
This commit is contained in:
parent
5e6d88c27e
commit
98cfcf2b95
@ -12,6 +12,9 @@ module.exports = {
|
|||||||
"addons": [
|
"addons": [
|
||||||
"@storybook/addon-essentials"
|
"@storybook/addon-essentials"
|
||||||
],
|
],
|
||||||
|
reactOptions: {
|
||||||
|
strictMode: true
|
||||||
|
},
|
||||||
typescript: {
|
typescript: {
|
||||||
check: false,
|
check: false,
|
||||||
checkOptions: {},
|
checkOptions: {},
|
||||||
|
@ -3,5 +3,6 @@ import './mockNextRouter'
|
|||||||
import './mockNextImage'
|
import './mockNextImage'
|
||||||
|
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
layout: 'centered'
|
layout: 'centered',
|
||||||
|
actions: { argTypesRegex: '^on.*' }
|
||||||
}
|
}
|
||||||
|
16527
package-lock.json
generated
16527
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -41,7 +41,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@dzeio/object-util": "^1.3.0",
|
"@dzeio/object-util": "^1.3.0",
|
||||||
"lucide-react": "^0.17.17",
|
"lucide-react": "^0.44.0",
|
||||||
"rollup": "^2.70.1",
|
"rollup": "^2.70.1",
|
||||||
"rollup-plugin-styles": "^4.0.0",
|
"rollup-plugin-styles": "^4.0.0",
|
||||||
"rollup-plugin-typescript2": "^0.31.2",
|
"rollup-plugin-typescript2": "^0.31.2",
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
@import '../config'
|
@import '../config'
|
||||||
|
|
||||||
|
.label
|
||||||
|
font-size rem(16)
|
||||||
|
display block
|
||||||
|
font-weight bold
|
||||||
|
color black
|
||||||
|
padding-left 8px
|
||||||
|
padding-bottom 4px
|
||||||
|
cursor pointer
|
||||||
|
@media (prefers-color-scheme dark)
|
||||||
|
color white
|
||||||
|
|
||||||
.parent
|
.parent
|
||||||
position relative
|
position relative
|
||||||
max-width 100%
|
max-width 100%
|
||||||
@ -10,6 +21,7 @@
|
|||||||
|
|
||||||
svg
|
svg
|
||||||
position absolute
|
position absolute
|
||||||
|
user-select none
|
||||||
color $darkGrayLight
|
color $darkGrayLight
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
color $darkGrayDark
|
color $darkGrayDark
|
||||||
@ -91,11 +103,11 @@
|
|||||||
outline none
|
outline none
|
||||||
background $lightGrayLight
|
background $lightGrayLight
|
||||||
transition all $transition
|
transition all $transition
|
||||||
border 2px solid $darkGrayLight
|
border 2px solid black
|
||||||
color black
|
color black
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
background $lightGrayDark
|
background $lightGrayDark
|
||||||
border-color $darkGrayDark
|
border-color transparent
|
||||||
color white
|
color white
|
||||||
|
|
||||||
&::placeholder
|
&::placeholder
|
||||||
@ -113,14 +125,10 @@
|
|||||||
|
|
||||||
|
|
||||||
&:disabled
|
&:disabled
|
||||||
border-color #999
|
border-color $darkGrayLight
|
||||||
|
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
border-color #444
|
border-color $darkGrayDark
|
||||||
~label
|
|
||||||
color #444
|
|
||||||
~ label
|
|
||||||
color #999
|
|
||||||
|
|
||||||
&:not(:disabled)
|
&:not(:disabled)
|
||||||
&:hover
|
&:hover
|
||||||
@ -137,9 +145,6 @@
|
|||||||
&:focus
|
&:focus
|
||||||
border-color $main
|
border-color $main
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
~ svg
|
||||||
color @border-color
|
color @border-color
|
||||||
// &::placeholder
|
// &::placeholder
|
||||||
@ -151,17 +156,11 @@
|
|||||||
&:invalid
|
&:invalid
|
||||||
border-color $errorLight
|
border-color $errorLight
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
~ svg
|
||||||
color @border-color
|
color @border-color
|
||||||
@media (prefers-color-scheme dark)
|
@media (prefers-color-scheme dark)
|
||||||
border-color $errorDark
|
border-color $errorDark
|
||||||
|
|
||||||
~ label
|
|
||||||
color @border-color
|
|
||||||
|
|
||||||
~ svg
|
~ svg
|
||||||
color @border-color
|
color @border-color
|
||||||
|
|
||||||
@ -185,7 +184,6 @@
|
|||||||
|
|
||||||
&.block
|
&.block
|
||||||
&.block input
|
&.block input
|
||||||
&.block select
|
|
||||||
&.block textarea
|
&.block textarea
|
||||||
width 100%
|
width 100%
|
||||||
display block
|
display block
|
||||||
|
@ -9,18 +9,27 @@ export default {
|
|||||||
component: Component
|
component: Component
|
||||||
} as Meta
|
} as Meta
|
||||||
|
|
||||||
export const Basic: Story<any> = (args: any) => <Component {...args} />
|
export const Input: Story<any> = (args: any) => <Component {...args} />
|
||||||
|
|
||||||
let tmp = Basic.bind({})
|
let tmp = Input.bind({})
|
||||||
tmp.args = {label: 'Label', helper: 'Helper', maxLength: 6, iconLeft: {
|
tmp.args = {
|
||||||
icon: X,
|
label: 'Label',
|
||||||
transformer: (v: string) => v + 1
|
helper: 'Helper',
|
||||||
}}
|
maxLength: 6,
|
||||||
|
// iconLeft: {
|
||||||
|
// icon: X,
|
||||||
|
// transformer: (v: string) => v + 1
|
||||||
|
// },
|
||||||
|
id: 'pouet',
|
||||||
|
type: 'number',
|
||||||
|
step: 10,
|
||||||
|
defaultValue: 'test'
|
||||||
|
}
|
||||||
|
|
||||||
export const Normal = tmp
|
export const Normal = tmp
|
||||||
|
|
||||||
tmp = Basic.bind({})
|
tmp = Input.bind({})
|
||||||
tmp.args = {label: 'Label', helper: 'Helper', choices: [
|
tmp.args = {defaultValue : 'd', label: 'Label', helper: 'Helper', choices: [
|
||||||
'a',
|
'a',
|
||||||
'a',
|
'a',
|
||||||
'a',
|
'a',
|
||||||
@ -31,7 +40,38 @@ tmp.args = {label: 'Label', helper: 'Helper', choices: [
|
|||||||
'a',
|
'a',
|
||||||
'b',
|
'b',
|
||||||
{value: 'd', display: 'D'},
|
{value: 'd', display: 'D'},
|
||||||
|
{value: '4', display: 'Mai'},
|
||||||
'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
|
'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
|
||||||
], characterCount: true, iconLeft: X}
|
], iconLeft: {
|
||||||
|
icon: X,
|
||||||
|
transformer: (v: string) => {
|
||||||
|
console.log("POUET :D")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
export const AutoComplete = tmp
|
export const AutoComplete = tmp
|
||||||
|
|
||||||
|
tmp = Input.bind({})
|
||||||
|
tmp.args = {block: true, type: 'textarea', defaultValue : 'd', label: 'Label', helper: 'Helper', choices: [
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'a',
|
||||||
|
'b',
|
||||||
|
{value: 'd', display: 'D'},
|
||||||
|
{value: '4', display: 'Mai'},
|
||||||
|
'cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'
|
||||||
|
], iconLeft: {
|
||||||
|
icon: X,
|
||||||
|
transformer: (v: string) => {
|
||||||
|
console.log("POUET :D")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
|
||||||
|
export const TextArea = tmp
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import React, { FC } from 'react'
|
import React, { FC, FocusEvent } from 'react'
|
||||||
|
|
||||||
import { ChevronDown } from 'lucide-react'
|
import { ChevronDown, MinusSquare, PlusSquare } from 'lucide-react'
|
||||||
import Text from '../Text'
|
import Text from '../Text'
|
||||||
import { Icon } from '../interfaces'
|
import { Icon } from '../interfaces'
|
||||||
import { buildClassName } from '../Util'
|
import { buildClassName } from '../Util'
|
||||||
import css from './Input.module.styl'
|
import css from './Input.module.styl'
|
||||||
import Menu from '../Menu'
|
import Menu from '../Menu'
|
||||||
import { objectClone } from '@dzeio/object-util'
|
import { objectOmit } from '@dzeio/object-util'
|
||||||
|
|
||||||
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
|
interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> {
|
||||||
id?: string
|
id?: string
|
||||||
@ -32,34 +32,77 @@ interface Props extends React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLIn
|
|||||||
* Always display every choices
|
* Always display every choices
|
||||||
*/
|
*/
|
||||||
displayAllOptions?: boolean
|
displayAllOptions?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the change event
|
||||||
|
* you will be returned the value (for choices too)
|
||||||
|
*/
|
||||||
|
onValue?: (newValue: string) => void
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the input take the whole width
|
||||||
|
*/
|
||||||
|
block?: boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if enabled value will not be sent if it is not contained in the choices
|
||||||
|
*/
|
||||||
|
strictChoices?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface States {
|
interface States {
|
||||||
textAreaHeight?: number
|
textAreaHeight?: number
|
||||||
value?: string
|
value?: string
|
||||||
|
displayedValue?: string
|
||||||
|
valueUpdate: boolean
|
||||||
isInFirstPartOfScreen?: boolean
|
isInFirstPartOfScreen?: boolean
|
||||||
|
list: Menu['props']['items']
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Input extends React.PureComponent<Props, States> {
|
export default class Input extends React.PureComponent<Props, States> {
|
||||||
|
|
||||||
public state: States = {}
|
public state: States = {
|
||||||
|
valueUpdate: false,
|
||||||
|
list: []
|
||||||
|
}
|
||||||
|
|
||||||
// any because f*ck types
|
|
||||||
private inputRef: React.RefObject<HTMLInputElement> = React.createRef()
|
private inputRef: React.RefObject<HTMLInputElement> = React.createRef()
|
||||||
private parentRef: React.RefObject<HTMLDivElement> = React.createRef()
|
private parentRef: React.RefObject<HTMLDivElement> = React.createRef()
|
||||||
|
|
||||||
public componentDidMount() {
|
public componentDidMount() {
|
||||||
|
|
||||||
|
// Handle Text Area
|
||||||
if (this.props.type === 'textarea') {
|
if (this.props.type === 'textarea') {
|
||||||
this.textareaHandler()
|
this.textareaHandler()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle choices
|
||||||
if (this.props.choices) {
|
if (this.props.choices) {
|
||||||
window.addEventListener('scroll', this.parentScroll)
|
window.addEventListener('scroll', this.parentScroll)
|
||||||
this.parentScroll()
|
this.parentScroll()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public componentDidUpdate() {
|
// Handle default Value
|
||||||
console.log(this.state)
|
if (this.props.defaultValue) {
|
||||||
|
if (!this.props.choices) {
|
||||||
|
this.setState({displayedValue: this.props.defaultValue.toString()})
|
||||||
|
} else {
|
||||||
|
const res = this.props.choices.find((it) => {
|
||||||
|
return typeof it === 'string' ? it === this.props.defaultValue : it.value === this.props.defaultValue
|
||||||
|
})
|
||||||
|
if (!res) {
|
||||||
|
if (this.props.strictChoices) {
|
||||||
|
this.setState({value: '', displayedValue: ''})
|
||||||
|
} else {
|
||||||
|
this.setState({displayedValue: this.props.defaultValue.toString()})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
displayedValue: typeof res === 'string' ? res : res.display
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
@ -68,24 +111,56 @@ export default class Input extends React.PureComponent<Props, States> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async componentDidUpdate(_: Props, prevStates: States) {
|
||||||
|
if (prevStates.value !== this.state.value) {
|
||||||
|
if (this.props.onValue) {
|
||||||
|
this.props.onValue(this.state.value ?? '')
|
||||||
|
}
|
||||||
|
// console.log(`Value updated: ${prevStates.value} -> ${this.state.value}`)
|
||||||
|
}
|
||||||
|
// if (prevStates.displayedValue !== this.state.displayedValue) {
|
||||||
|
// console.log(`Displayed Value updated: ${prevStates.displayedValue} -> ${this.state.displayedValue}`)
|
||||||
|
// }
|
||||||
|
if (
|
||||||
|
prevStates.value !== this.state.value ||
|
||||||
|
prevStates.displayedValue !== this.state.displayedValue ||
|
||||||
|
prevStates.valueUpdate !== this.state.valueUpdate
|
||||||
|
) {
|
||||||
|
this.setState({list: this.buildList()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the real value of the field (depending if you a choices and the display value)
|
||||||
|
* @returns the value of the field
|
||||||
|
*/
|
||||||
|
public value(): string | number | readonly string[] | undefined {
|
||||||
|
return this.state?.value ?? this.state.displayedValue ?? this.props.value ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const props: Props = objectClone(this.props)
|
const props: Props = objectOmit(this.props, 'iconLeft', 'iconRight', 'inputRed', 'helper', 'choices', 'onValue', 'block', 'defaultValue', 'label')
|
||||||
delete props.label
|
|
||||||
delete props.iconLeft
|
|
||||||
delete props.iconRight
|
|
||||||
delete props.inputRef
|
|
||||||
delete props.helper
|
|
||||||
delete props.choices
|
|
||||||
|
|
||||||
const baseProps: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = {
|
const baseProps: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> = {
|
||||||
placeholder: this.props.label || this.props.placeholder || ' ',
|
|
||||||
ref: this.props.inputRef || this.inputRef,
|
ref: this.props.inputRef || this.inputRef,
|
||||||
className: buildClassName(
|
className: buildClassName(
|
||||||
[css.iconLeft, this.props.iconLeft],
|
[css.iconLeft, this.props.type === 'number' || this.props.iconLeft],
|
||||||
[css.iconRight, this.props.iconRight || this.props.choices]
|
[css.iconRight, this.props.type === 'number' || this.props.iconRight || this.props.choices]
|
||||||
),
|
),
|
||||||
onInvalid: (ev: React.FormEvent<HTMLInputElement>) => ev.preventDefault(),
|
onInvalid: (ev: React.FormEvent<HTMLInputElement>) => ev.preventDefault(),
|
||||||
|
onFocus: (ev: FocusEvent<HTMLInputElement, Element>) => {
|
||||||
|
this.setState({valueUpdate: false})
|
||||||
|
|
||||||
|
if (props.onFocus) {
|
||||||
|
props.onFocus(ev)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
value: this.state.displayedValue ?? this.state.value ?? this.props.value,
|
||||||
|
onChange: this.onChange
|
||||||
|
}
|
||||||
|
|
||||||
|
let iconRight = this.props.iconRight
|
||||||
|
let iconLeft = this.props.iconLeft
|
||||||
|
|
||||||
let input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
|
let input: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>
|
||||||
|
|
||||||
@ -106,34 +181,52 @@ export default class Input extends React.PureComponent<Props, States> {
|
|||||||
break
|
break
|
||||||
case 'number':
|
case 'number':
|
||||||
baseProps.onWheel = (ev: React.WheelEvent<HTMLInputElement>) => ev.currentTarget.blur()
|
baseProps.onWheel = (ev: React.WheelEvent<HTMLInputElement>) => ev.currentTarget.blur()
|
||||||
|
iconLeft = this.props.iconLeft ?? {icon: MinusSquare, transformer: (v) => {
|
||||||
|
let value = parseFloat(v)
|
||||||
|
if (isNaN(value)) {
|
||||||
|
value = 0
|
||||||
|
}
|
||||||
|
return (value - this.ensureNumber(this.props.step, 1)).toString()
|
||||||
|
}}
|
||||||
|
iconRight = this.props.iconRight ?? {icon: PlusSquare, transformer: (v) => {
|
||||||
|
let value = parseFloat(v)
|
||||||
|
if (isNaN(value)) {
|
||||||
|
value = 0
|
||||||
|
}
|
||||||
|
return (value + this.ensureNumber(this.props.step, 1)).toString()
|
||||||
|
}}
|
||||||
default:
|
default:
|
||||||
input = (
|
input = <input
|
||||||
<input
|
|
||||||
{...props}
|
{...props}
|
||||||
{...baseProps}
|
{...baseProps}
|
||||||
/>
|
/>
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
|
{this.props.label && (
|
||||||
|
<label className={css.label} htmlFor={this.props.id}>{this.props.label}</label>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
css.parent
|
css.parent,
|
||||||
|
[css.block, this.props.block]
|
||||||
)}
|
)}
|
||||||
onChangeCapture={this.onChange}
|
|
||||||
ref={this.parentRef}
|
ref={this.parentRef}
|
||||||
>
|
>
|
||||||
|
|
||||||
{input as any}
|
{input as any}
|
||||||
|
|
||||||
{/* Left Icon */}
|
{/* Left Icon */}
|
||||||
{this.getIcon('left')}
|
{this.getIcon(iconLeft, 'left')}
|
||||||
|
|
||||||
{/* Right Icon */}
|
{/* Right Icon */}
|
||||||
{this.props.iconRight ?
|
{iconRight ?
|
||||||
this.getIcon('right') :
|
this.getIcon(iconRight, 'right') :
|
||||||
(this.props.choices && !this.props.disabled) && (
|
(this.props.choices && !this.props.disabled) && (
|
||||||
<ChevronDown size="18" className={buildClassName(css.right, css.rotate)} />
|
<ChevronDown size="18" className={buildClassName(css.right, css.rotate)} />
|
||||||
)}
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{/* Helper text */}
|
{/* Helper text */}
|
||||||
{(this.props.helper) && (
|
{(this.props.helper) && (
|
||||||
@ -142,123 +235,131 @@ export default class Input extends React.PureComponent<Props, States> {
|
|||||||
|
|
||||||
{/* List when this is an autocomplete */}
|
{/* List when this is an autocomplete */}
|
||||||
{this.props.choices && (
|
{this.props.choices && (
|
||||||
// <ul className={buildClassName(css.autocomplete, [css.reverse, !this.state.isInFirstPartOfScreen])}>
|
|
||||||
// {this.props.choices
|
|
||||||
// .map((item, index) => typeof item === 'string' ? ({item: {display: item, value: item}, index}) : {item, index})
|
|
||||||
// .filter(
|
|
||||||
// (item) => !this.getValue() || [item.item.display.toLowerCase(), item.item.value.toLowerCase()]
|
|
||||||
// .includes(this.getValue())
|
|
||||||
// )
|
|
||||||
// .map((item) => (<li key={item.index} onClick={this.onAutoCompleteClick(item.index)}><Text>{item.item.display}</Text></li>))}
|
|
||||||
// </ul>
|
|
||||||
<Menu
|
<Menu
|
||||||
outline
|
outline
|
||||||
hideWhenEmpty
|
hideWhenEmpty
|
||||||
className={buildClassName(css.autocomplete, [css.reverse, !this.state.isInFirstPartOfScreen])}
|
className={buildClassName(css.autocomplete, [css.reverse, !this.state.isInFirstPartOfScreen])}
|
||||||
items={this.buildList()}
|
items={this.state.list ?? []}
|
||||||
onClick={this.listSelection}
|
onClick={this.listSelection}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ensureNumber(item: string | number | undefined, defaultValue: number): number {
|
||||||
|
return typeof item === 'string' ? parseFloat(item) : item ?? defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* event for autocomplete to detect where on the screen it shoul display
|
* event for the menu to detect where on the screen it should be displayed
|
||||||
*/
|
*/
|
||||||
private parentScroll = async () => {
|
private parentScroll = async () => {
|
||||||
const div = this.parentRef.current
|
const div = this.parentRef.current
|
||||||
if (!div) {return}
|
if (!div) {return}
|
||||||
const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2)
|
const result = !(div.offsetTop - window.scrollY >= window.innerHeight / 2)
|
||||||
// console.log(result, div, this.state.isInFirstPartOfScreen)
|
|
||||||
if (this.state.isInFirstPartOfScreen !== result) {
|
if (this.state.isInFirstPartOfScreen !== result) {
|
||||||
this.setState({isInFirstPartOfScreen: result})
|
this.setState({isInFirstPartOfScreen: result})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the interactive list for the item
|
||||||
|
* @returns the list
|
||||||
|
*/
|
||||||
private buildList(): Menu['props']['items'] {
|
private buildList(): Menu['props']['items'] {
|
||||||
if (!this.props.choices) {
|
if (!this.props.choices) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
const v = this.getValue().toLowerCase()
|
const v = this.state.displayedValue?.toLowerCase()
|
||||||
return this.props.choices
|
return this.props.choices
|
||||||
.map((item, index) => typeof item === 'string' ? ({item: {display: item, value: item}, index}) : {item, index})
|
.map((item, index) => typeof item === 'string' ? ({item: {display: item, value: item}, index}) : {item, index})
|
||||||
.filter(
|
.filter(
|
||||||
(item) => this.props.displayAllOptions || !v || item.item.display.toLowerCase().includes(v) || item.item.display.toLowerCase().toLowerCase().includes(v)
|
(item) => this.props.displayAllOptions || !this.state.valueUpdate || !v || item.item.display.toLowerCase().includes(v) || item.item.display.toLowerCase().toLowerCase().includes(v)
|
||||||
)
|
)
|
||||||
.map((item) => ({display: item.item.display, value: item.index}))
|
.map((item) => item.item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private listSelection: Menu['props']['onClick'] = async (value: number, key) => {
|
/**
|
||||||
const newValue = this.props.choices?.[value]
|
* handle when an item is selected
|
||||||
|
* @param key the index of the selected item
|
||||||
|
*/
|
||||||
|
private listSelection: Menu['props']['onClick'] = async (_, key) => {
|
||||||
|
const newValue = this.state.list[key]
|
||||||
|
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof newValue === 'string') {
|
if (typeof newValue === 'string') {
|
||||||
return this.setValue(newValue)
|
this.setState({value: newValue, displayedValue: newValue, valueUpdate: true})
|
||||||
}
|
return
|
||||||
await this.setValue(newValue.display)
|
|
||||||
this.setState({value: newValue.value})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getIcon(icon: 'left' | 'right') {
|
this.setState({displayedValue: newValue.display, value: newValue.value, valueUpdate: true})
|
||||||
const Icon = icon === 'left' ? this.props.iconLeft : this.props.iconRight
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the icon duh
|
||||||
|
* @param icon the icon
|
||||||
|
* @returns the icon
|
||||||
|
*/
|
||||||
|
private getIcon(Icon: Icon | {
|
||||||
|
icon: Icon;
|
||||||
|
transformer: (value: string) => string;
|
||||||
|
} | undefined, position: 'left' | 'right') {
|
||||||
if (!Icon) {
|
if (!Icon) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('icon' in Icon) {
|
if ('icon' in Icon) {
|
||||||
return <Icon.icon size="18" className={buildClassName(css[icon], css.iconClickable)} onClick={() => {
|
return <Icon.icon size="18" className={buildClassName(css[position], css.iconClickable)} onClick={async () => {
|
||||||
const el = this.getElement()
|
const value = Icon.transformer(this.state.value ?? this.state.displayedValue ?? '')
|
||||||
console.log(el, 'pouet')
|
this.setState({ value: value, displayedValue: value, valueUpdate: true })
|
||||||
if (!el) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
el.value = Icon.transformer(el.value)
|
|
||||||
}} />
|
}} />
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Icon size="18" className={css[icon]} />
|
return <Icon size="18" className={css[position]} />
|
||||||
}
|
}
|
||||||
|
|
||||||
private getValue(): string {
|
/**
|
||||||
return this.state?.value?.toLowerCase() ?? this.props.value?.toString().toLowerCase() ?? ''
|
* Handle textarea height changes
|
||||||
}
|
*/
|
||||||
|
private textareaHandler = async () => {
|
||||||
private getElement(): undefined | HTMLInputElement {
|
|
||||||
const item = this.props.inputRef || this.inputRef
|
|
||||||
if (!item || !item.current) {return}
|
|
||||||
return item.current
|
|
||||||
}
|
|
||||||
|
|
||||||
private textareaHandler = async () =>
|
|
||||||
this.setState({textAreaHeight: undefined}, () => {
|
this.setState({textAreaHeight: undefined}, () => {
|
||||||
if (!this.inputRef.current) {return}
|
if (!this.inputRef.current) {return}
|
||||||
this.setState({textAreaHeight: this.inputRef.current.scrollHeight})
|
this.setState({ textAreaHeight: this.inputRef.current.scrollHeight })
|
||||||
})
|
})
|
||||||
|
|
||||||
private async setValue(value: string) {
|
|
||||||
const item = this.getElement()
|
|
||||||
if (!item) {return}
|
|
||||||
const valueSetter = Object.getOwnPropertyDescriptor(item, 'value')?.set
|
|
||||||
const prototype = Object.getPrototypeOf(item)
|
|
||||||
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set
|
|
||||||
if (valueSetter && valueSetter !== prototypeValueSetter) {
|
|
||||||
// @ts-expect-error IDK why
|
|
||||||
prototypeValueSetter.call(item, value)
|
|
||||||
} else {
|
|
||||||
// @ts-expect-error IDK why
|
|
||||||
valueSetter.call(item, value)
|
|
||||||
}
|
|
||||||
item.dispatchEvent(new Event('input', {bubbles: true}))
|
|
||||||
if (this.props.type === 'textarea') {
|
|
||||||
await this.parentScroll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onChange = async (event?: React.FormEvent<HTMLDivElement>) => {
|
/**
|
||||||
if (event) {
|
* handle the change event of the input
|
||||||
this.setState({value: (event.target as HTMLInputElement).value })
|
* @param event the event
|
||||||
|
*/
|
||||||
|
private onChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
// get the input
|
||||||
|
const value = event.currentTarget.value
|
||||||
|
// console.log("onChange", value)
|
||||||
|
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.onChange) {
|
||||||
|
this.props.onChange(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.strictChoices) {
|
||||||
|
this.setState({ displayedValue: value, valueUpdate: true })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({ value: value, displayedValue: value, valueUpdate: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user