forked from kubesphere/console
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: shared improvement (kubesphere#751)
* feat: add interval hooks Signed-off-by: elichen95 <[email protected]> * fix: fix use actions bugs Signed-off-by: elichen95 <[email protected]> * feat: add Object input and properties input component Signed-off-by: elichen95 <[email protected]> * feat: add TimeSelector component Signed-off-by: elichen95 <[email protected]> * fix: fix detail left side styles Signed-off-by: elichen95 <[email protected]> * feat: export Inputs and TimeSelector components Signed-off-by: elichen95 <[email protected]> --------- Signed-off-by: elichen95 <[email protected]>
- Loading branch information
Showing
17 changed files
with
1,020 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
72 changes: 72 additions & 0 deletions
72
packages/shared/src/components/Inputs/ObjectInput/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
import React, { | ||
Children, | ||
cloneElement, | ||
isValidElement, | ||
PropsWithChildren, | ||
useEffect, | ||
useState, | ||
} from 'react'; | ||
import classNames from 'classnames'; | ||
import { get, isUndefined } from 'lodash'; | ||
import { Wrapper } from './styles'; | ||
|
||
interface Props { | ||
name?: string; | ||
value?: Record<string, any>; | ||
defaultValue?: Record<string, any>; | ||
onChange?: (value: Record<string, any>) => void; | ||
} | ||
|
||
function ObjectInput({ | ||
value = {}, | ||
defaultValue = {}, | ||
onChange, | ||
children, | ||
}: PropsWithChildren<Props>) { | ||
const [selfValue, setSelfValue] = useState<Record<string, any>>(defaultValue ?? {}); | ||
|
||
useEffect(() => { | ||
setSelfValue(value); | ||
}, [value]); | ||
|
||
const getValue = (name: string, childDefaultValue: any) => { | ||
if (!isUndefined(selfValue[name])) { | ||
return selfValue[name]; | ||
} | ||
|
||
if (!isUndefined(childDefaultValue)) { | ||
setSelfValue({ | ||
...selfValue, | ||
[name]: childDefaultValue, | ||
}); | ||
} | ||
|
||
return childDefaultValue; | ||
}; | ||
|
||
const childNodes = Children.map(children, child => | ||
isValidElement(child) | ||
? cloneElement(child, { | ||
...child.props, | ||
className: classNames(child.props.className, 'item'), | ||
value: isUndefined(selfValue) | ||
? child.props.value | ||
: getValue(child.props.name, child.props.defaultValue), | ||
onChange: (val: any) => { | ||
const childValue = get(val, 'currentTarget.value', val); | ||
const newValue = { | ||
...selfValue, | ||
[child.props.name]: childValue, | ||
}; | ||
setSelfValue(newValue); | ||
onChange?.(newValue); | ||
child.props?.onChange?.(childValue); | ||
}, | ||
}) | ||
: child, | ||
); | ||
|
||
return <Wrapper>{childNodes}</Wrapper>; | ||
} | ||
|
||
export default ObjectInput; |
11 changes: 11 additions & 0 deletions
11
packages/shared/src/components/Inputs/ObjectInput/styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import styled from 'styled-components'; | ||
|
||
export const Wrapper = styled.div` | ||
display: flex; | ||
.item { | ||
& + .item { | ||
margin-left: 12px; | ||
} | ||
} | ||
`; |
168 changes: 168 additions & 0 deletions
168
packages/shared/src/components/Inputs/PropertiesInput/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import { has, isEmpty } from 'lodash'; | ||
import React, { ReactNode, useEffect, useMemo, useState } from 'react'; | ||
import PropertyItem, { Props as ItemProps } from './item'; | ||
import { AddButton, Wrapper } from './styles'; | ||
|
||
interface Props { | ||
name?: string; | ||
value?: Record<string, any>; | ||
hiddenKeys?: string[]; | ||
readOnlyKeys?: string[]; | ||
onChange?: (value: Record<string, any>) => void; | ||
onError?: (error?: { message?: string }) => void; | ||
itemProps?: Partial<ItemProps>; | ||
addText?: ReactNode; | ||
} | ||
|
||
interface ValueType { | ||
key: string; | ||
value?: any; | ||
} | ||
|
||
function PropertiesInput({ | ||
value, | ||
hiddenKeys = [], | ||
readOnlyKeys = [], | ||
onChange, | ||
onError, | ||
itemProps, | ||
addText = t('ADD'), | ||
}: Props) { | ||
const [selfValue, setSelfValue] = useState<Record<string, any>>(value ?? {}); | ||
const [hiddenValues, setHiddenValues] = useState<ValueType[]>([]); | ||
const [readOnlyValues, setReadOnlyValues] = useState<ValueType[]>([]); | ||
const [arrayValues, setArrayValues] = useState<ValueType[]>([]); | ||
|
||
useEffect(() => { | ||
if (value) { | ||
setSelfValue(value); | ||
} | ||
}, [value]); | ||
|
||
useEffect(() => { | ||
const newHiddenValues: ValueType[] = []; | ||
const newReadOnlyValues: ValueType[] = []; | ||
const newArrayValues: ValueType[] = []; | ||
|
||
Object.keys(selfValue).forEach(key => { | ||
if (hiddenKeys.some(hiddenKey => new RegExp(hiddenKey).test(key))) { | ||
newHiddenValues.push({ | ||
key, | ||
value: selfValue[key], | ||
}); | ||
} else if (readOnlyKeys.some(readOnlyKey => new RegExp(readOnlyKey).test(key))) { | ||
newReadOnlyValues.push({ | ||
key, | ||
value: selfValue[key], | ||
}); | ||
} else { | ||
newArrayValues.push({ | ||
key, | ||
value: selfValue[key], | ||
}); | ||
} | ||
}); | ||
|
||
if (isEmpty(newArrayValues) && isEmpty(newReadOnlyValues)) { | ||
newArrayValues.push({ key: '' }); | ||
} | ||
|
||
setArrayValues(newArrayValues); | ||
setHiddenValues(newHiddenValues); | ||
setReadOnlyValues(newReadOnlyValues); | ||
}, [selfValue]); | ||
|
||
const handleAdd = () => { | ||
setArrayValues([...arrayValues, { key: '' }]); | ||
}; | ||
|
||
const triggerChange = (values: ValueType[]) => { | ||
let existedKey = false; | ||
let emptyKeyValue = false; | ||
|
||
onError?.(); | ||
const valuePairs = [...hiddenValues, ...readOnlyValues, ...values]; | ||
const newValue = valuePairs.reduce((prev, cur) => { | ||
cur.key = cur.key || ''; | ||
|
||
// when add new line, do not change value | ||
if (isEmpty(cur.key) && isEmpty(cur.value)) { | ||
emptyKeyValue = true; | ||
} | ||
|
||
// has duplicate keys | ||
if (has(prev, cur.key)) { | ||
existedKey = true; | ||
return prev; | ||
} | ||
|
||
return { | ||
...prev, | ||
[cur.key]: cur.value || '', | ||
}; | ||
}, {}); | ||
|
||
setArrayValues(values); | ||
|
||
const hasEmptyKey = Object.keys(newValue).some(key => isEmpty(key)); | ||
if (hasEmptyKey) { | ||
onError?.({ message: t('EMPTY_KEY') }); | ||
} else if (existedKey) { | ||
onError?.({ message: t('DUPLICATE_KEYS') }); | ||
} else { | ||
onError?.(); | ||
} | ||
|
||
if (emptyKeyValue) { | ||
return; | ||
} | ||
if (!existedKey) { | ||
onChange?.(newValue); | ||
} | ||
}; | ||
|
||
const handleItemChange = (index: number, val: ValueType) => { | ||
arrayValues[index] = val; | ||
triggerChange([...arrayValues]); | ||
}; | ||
|
||
const handleItemDelete = (index: number) => { | ||
triggerChange([...arrayValues.filter((_, _index) => _index !== index)]); | ||
}; | ||
|
||
const isAddEnable = useMemo( | ||
() => arrayValues.every(item => !(isEmpty(item) || (!item.key && !item.value))), | ||
[arrayValues], | ||
); | ||
|
||
return ( | ||
<Wrapper> | ||
{readOnlyValues.map((item, index) => ( | ||
<PropertyItem | ||
index={index} | ||
key={`readonly-${item.key}`} | ||
value={item} | ||
readOnly | ||
{...itemProps} | ||
></PropertyItem> | ||
))} | ||
{arrayValues.map((item, index) => ( | ||
<PropertyItem | ||
key={`array-${index}`} | ||
index={index} | ||
value={item || {}} | ||
onChange={handleItemChange} | ||
onDelete={handleItemDelete} | ||
{...itemProps} | ||
/> | ||
))} | ||
<div style={{ textAlign: 'right' }}> | ||
<AddButton onClick={handleAdd} disabled={!isAddEnable}> | ||
{addText} | ||
</AddButton> | ||
</div> | ||
</Wrapper> | ||
); | ||
} | ||
|
||
export default PropertiesInput; |
53 changes: 53 additions & 0 deletions
53
packages/shared/src/components/Inputs/PropertiesInput/item.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
import React, { ComponentType } from 'react'; | ||
import { Input } from '@kubed/components'; | ||
import { Trash } from '@kubed/icons'; | ||
import ObjectInput from '../ObjectInput'; | ||
import { DeleteButton, Item } from './styles'; | ||
|
||
interface ValueType { | ||
key: string; | ||
value?: any; | ||
} | ||
|
||
export interface Props { | ||
index: number; | ||
value?: ValueType; | ||
readOnly?: boolean; | ||
keyProps?: { component?: ComponentType; [key: string]: any }; | ||
valueProps?: { component?: ComponentType; [key: string]: any }; | ||
onChange?: (index: number, value: any) => void; | ||
onDelete?: (index: number) => void; | ||
} | ||
|
||
function PropertyItem({ | ||
index, | ||
readOnly, | ||
keyProps = {}, | ||
valueProps = {}, | ||
onChange, | ||
onDelete, | ||
...rest | ||
}: Props) { | ||
const { component: KeyInput = Input, ...keyInputProps } = keyProps; | ||
const { component: ValueInput = Input, ...valueInputProps } = valueProps; | ||
return ( | ||
<Item> | ||
<ObjectInput {...rest} onChange={value => onChange?.(index, value)}> | ||
<KeyInput name="key" placeholder={t('KEY')} readOnly={readOnly} {...keyInputProps} /> | ||
<ValueInput | ||
name="value" | ||
placeholder={t('VALUE')} | ||
readOnly={readOnly} | ||
{...valueInputProps} | ||
/> | ||
</ObjectInput> | ||
{!readOnly && ( | ||
<DeleteButton onClick={() => onDelete?.(index)}> | ||
<Trash /> | ||
</DeleteButton> | ||
)} | ||
</Item> | ||
); | ||
} | ||
|
||
export default PropertyItem; |
40 changes: 40 additions & 0 deletions
40
packages/shared/src/components/Inputs/PropertiesInput/styles.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { Button } from '@kubed/components'; | ||
import styled from 'styled-components'; | ||
|
||
export const Wrapper = styled.div` | ||
position: relative; | ||
`; | ||
|
||
export const Item = styled.div` | ||
position: relative; | ||
padding: 6px 68px 6px 17px; | ||
border-radius: 60px; | ||
background-color: ${({ theme }) => theme.palette.background}; | ||
border: solid 1px ${({ theme }) => theme.palette.border}; | ||
& + .item { | ||
margin-top: 8px; | ||
} | ||
& > input { | ||
max-width: none !important; | ||
} | ||
`; | ||
|
||
export const DeleteButton = styled(Button)` | ||
right: 6px; | ||
position: absolute; | ||
top: 50%; | ||
transform: translateY(-50%); | ||
`; | ||
|
||
export const AddButton = styled(Button)` | ||
margin-top: 12px; | ||
`; | ||
|
||
export const Desc = styled.div` | ||
position: absolute; | ||
left: 0; | ||
bottom: 14px; | ||
color: ${({ theme }) => theme.palette.accents_5}; | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as ObjectInput } from './ObjectInput'; | ||
export { default as PropertiesInput } from './PropertiesInput'; |
Oops, something went wrong.