skyline-console/src/components/Tags/index.jsx

174 lines
4.7 KiB
JavaScript

// Copyright 2021 99cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, { useEffect, useState } from 'react';
import { Col, Input, Row, Tag, Tooltip } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { projectTagsColors } from 'src/utils/constants';
import PropTypes from 'prop-types';
const Tags = ({ tags: source, onChange, maxLength, maxCount }) => {
const [tags, setTags] = useState(source);
const [inputVisible, setInputVisible] = useState(false);
const [inputValue, setInputValue] = useState('');
const [editInputIdx, setEditInputIdx] = useState(-1);
const [editInputValue, setEditInputValue] = useState('');
const tagLength = maxLength && maxLength > 0 ? { maxLength } : {};
const tagCount = (maxCount && maxCount > 0) || -1;
function handleClose(removedTag) {
setTags(tags.filter((tag) => tag !== removedTag));
}
let editInput = null;
let saveInput = null;
const saveEditInputRef = (input) => {
editInput = input;
};
const saveInputRef = (input) => {
saveInput = input;
};
function handleEditInputChange(e) {
setEditInputValue(e.target.value);
}
function handleEditInputConfirm() {
const newTags = [...tags];
newTags[editInputIdx] = editInputValue;
setTags(newTags);
setEditInputValue('');
setEditInputIdx(-1);
}
function handleInputChange(e) {
setInputValue(e.target.value);
}
function handleInputConfirm() {
const retVal = inputValue.toLocaleLowerCase();
if (inputValue && !tags.some((tag) => tag.toLowerCase() === retVal)) {
if (tagCount !== -1 && tags.length < maxCount) {
setTags([...tags, inputValue]);
} else if (tagCount === -1) {
setTags([...tags, inputValue]);
}
}
setInputVisible(false);
setInputValue('');
}
function showInput() {
setInputVisible(true);
}
useEffect(() => {
saveInput && saveInput.focus();
}, [inputVisible]);
useEffect(() => {
editInput && editInput.focus();
}, [editInputIdx]);
useEffect(() => {
onChange(tags);
}, [tags]);
return (
<Row gutter={[0, 8]}>
{tags.map((tag, index) => {
if (editInputIdx === index) {
return (
<Input
ref={saveEditInputRef}
style={{ width: 78, marginRight: 8, verticalAlign: 'top' }}
key={tag}
size="small"
value={editInputValue}
onChange={handleEditInputChange}
onBlur={handleEditInputConfirm}
onPressEnter={handleEditInputConfirm}
{...tagLength}
/>
);
}
const isLongTag = tag.length > 20;
const tagText = isLongTag ? `${tag.slice(0, 20)}...` : tag;
const tagEl = (
<Tag
key={tag}
closable
onClose={() => handleClose(tag)}
color={projectTagsColors[index % 10]}
>
<span
style={{ whiteSpace: 'pre-wrap' }}
onDoubleClick={(e) => {
setEditInputIdx(index);
setEditInputValue(tag);
e.preventDefault();
}}
>
{tagText}
</span>
</Tag>
);
return (
<Col span={24} key={tag}>
{isLongTag ? (
<Tooltip
title={<span style={{ whiteSpace: 'pre-wrap' }}>{tag}</span>}
>
{tagEl}
</Tooltip>
) : (
tagEl
)}
</Col>
);
})}
<Col span={24}>
{inputVisible && (
<Input
ref={saveInputRef}
style={{ width: 78, marginRight: 8, verticalAlign: 'top' }}
type="text"
size="small"
value={inputValue}
onChange={handleInputChange}
onBlur={handleInputConfirm}
onPressEnter={handleInputConfirm}
{...tagLength}
/>
)}
{!inputVisible && (
<Tag onClick={showInput}>
<PlusOutlined /> New Tag
</Tag>
)}
</Col>
</Row>
);
};
Tags.propTypes = {
tags: PropTypes.array,
onChange: PropTypes.func,
maxLength: PropTypes.number,
maxCount: PropTypes.number,
};
export default Tags;