@ant-design/icons#EyeOutlined TypeScript Examples
The following examples show how to use
@ant-design/icons#EyeOutlined.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: GlobalMenu.tsx From ant-extensions with MIT License | 6 votes |
GlobalMenu: React.FC<{ disabled: boolean }> = React.memo(({ disabled }) => {
const { t } = useTranslation(I18nKey);
const { filters, enableAll, toggleExclude, removeAll } = useContext(Context);
const menu = useMemo(() => {
const someEnabled = filters.some((f) => !f.required && f.active);
const someDisabled = filters.some((f) => !f.required && !f.active);
const hasNotRequired = filters.some((f) => !f.required);
return (
<Menu>
<h4 style={{ padding: "0 16px" }}>{t("label.all_filters")}</h4>
<Menu.Item disabled={!someDisabled} onClick={() => enableAll(true)}>
<EyeOutlined /> {t("label.enable_all")}
</Menu.Item>
<Menu.Item disabled={!someEnabled} onClick={() => enableAll(false)}>
<EyeInvisibleOutlined /> {t("label.disable_all")}
</Menu.Item>
<Menu.Item disabled={!hasNotRequired} onClick={toggleExclude}>
{<Icon component={TwoTone} />} {t("label.invert")}
</Menu.Item>
<Menu.Item
className="ant-typography ant-typography-danger"
disabled={!hasNotRequired}
onClick={removeAll}
>
<DeleteOutlined /> {t("label.remove_all")}
</Menu.Item>
</Menu>
);
}, [filters, enableAll, removeAll, t, toggleExclude]);
return (
<Dropdown overlay={menu} trigger={["click"]} disabled={disabled || filters.length === 0}>
<Button type="link" icon={<SettingOutlined />} />
</Dropdown>
);
})
Example #2
Source File: editor-operate.tsx From electron-playground with MIT License | 6 votes |
WrapMenu = function ({ layout, setLayout }: MProp) {
const visibleMosaics = getVisibleMosaics(layout)
const menuItem = ['main.js', 'renderer.js', 'index.html', 'preload.js']
const onItemClick = (str: string) => {
return () => {
let newMosaicsArrangement: Array<EditorId>
if (visibleMosaics.includes(str as EditorId)) {
newMosaicsArrangement = visibleMosaics.filter(i => i !== str)
} else {
newMosaicsArrangement = [...visibleMosaics, str as EditorId]
}
setLayout(createMosaicArrangement(newMosaicsArrangement))
}
}
return (
<Menu>
{menuItem.map(item => (
<Menu.Item
key={item}
onClick={onItemClick(item)}
id={item}
icon={
visibleMosaics.includes(item as EditorId) ? <EyeOutlined /> : <EyeInvisibleOutlined />
}>
{item}
</Menu.Item>
))}
</Menu>
)
}
Example #3
Source File: preview.tsx From visual-layout with MIT License | 5 votes |
Preview: React.FC<{ projectService: ProjectService }> = ({
projectService,
}) => {
const page = projectService.getCurrentPage();
const options = page?.options;
return (
<Popover
content={
<Checkbox.Group
value={options?.previewStyle
.filter(({ isCanUse }) => isCanUse)
.map(({ key }) => key)}
style={{ width: '100%' }}
onChange={(checkedValue: CheckboxValueType[]) => {
page.setOptions({
previewStyle: options?.previewStyle.map(option => {
return {
...option,
isCanUse: checkedValue.includes(option.key) ? true : false,
};
}),
});
}}
>
{options?.previewStyle.map(style => {
return (
<Row key={style.key}>
<Checkbox value={style.key}>{style?.title}</Checkbox>
</Row>
);
})}
</Checkbox.Group>
}
title="预览样式设置"
placement="bottom"
>
<EyeOutlined style={{ fontSize: 20 }} />
</Popover>
);
}
Example #4
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Screen = (props: Props) => {
// const [categoryList, setCategoryList] = useState([]);
const [dataList, setDataList] = useState({
data: [],
pageIndex: 0,
total: 0,
pageSize: 0,
});
const [id, setId] = useState('');
const [url, setUrl] = useState('');
const [saveVisible, setSaveVisible] = useState(false);
const [editVisible, setEditVisible] = useState(false);
const [copyVisible, setCopyVisible] = useState(false);
const [param, setParam] = useState({});
const [searchParam, setSearchParam] = useState({
pageSize: 8,
pageIndex: 0,
terms: { type: 'vis_configuration' },
});
const token = getAccessToken();
const handleSearch = (params: any) => {
setSearchParam(params);
api.screen.query(encodeQueryParam(params)).then(res => {
if (res.status === 200) {
setDataList(res.result);
}
});
};
let delConfirm = (id: string) => {
confirm({
title: '删除组态',
icon: <ExclamationCircleOutlined />,
content: '确认删除该组态?',
onOk() {
api.screen.remove(id).then(res => {
if (res.status === 200) {
handleSearch(searchParam);
}
});
},
onCancel() {
message.info('已取消');
},
});
};
let updateState = (state: string, id: string) => {
confirm({
title: `${state === 'enabled' ? '禁用' : '启用'}组态`,
icon: <ExclamationCircleOutlined />,
content: `确认${state === 'enabled' ? '禁用' : '启用'}该组态`,
onOk() {
api.screen
.update(id, {
state: {
value: state === 'enabled' ? 'disabled' : 'enabled',
},
})
.then(res => {
if (res.status === 200) {
handleSearch(searchParam);
}
});
},
onCancel() {
message.info('已取消');
},
});
};
const uploadProps = (item: any) => {
api.screen.save(item).then(res => {
if (res.status === 200) {
message.success('导入成功');
handleSearch(searchParam);
}
});
};
const onListChange = (page: number, pageSize: number) => {
console.log(page, pageSize, 'jahaha');
handleSearch({
pageIndex: page - 1,
pageSize,
terms: searchParam.terms,
});
};
let getView = (view: any) => {
let children = [];
if (view.children && view.children.length > 0) {
children = view.children.map((i: any) => {
return getView(i);
});
return {
id: view.id,
children: children,
pId: view.parentId,
value: view.id,
title: view.name,
};
} else {
return {
id: view.id,
pId: view.parentId,
value: view.id,
title: view.name,
};
}
};
useEffect(() => {
//获取跳转url
api.screen.getUrl().then(res => {
if (res.status === 200) {
if (
res.result.urls['vis-configuration'] !== '' &&
res.result.urls['vis-configuration'] !== undefined
) {
setUrl(res.result.urls['vis-configuration']);
} else {
message.error('配置错误,请联系管理员');
// setUrl('http://localhost:8000/')
}
}
});
// api.categoty.queryNoPaging({}).then(res => {
// if (res.status === 200) {
// setCategoryList(res.result);
// }
// });
handleSearch(searchParam);
}, []);
// const findCategory = (id:string)=>{
// const category: Partial<CategoryItem> =
// categoryList.find((i:any) => i.id === id) || {};
// return category.name;
// };
return (
<PageHeaderWrapper title="组态管理">
<Card bordered={false}>
<div className={styles.tableList}>
<div>
<SearchForm
search={(params: any) => {
handleSearch({
terms: { ...params, type: 'vis_configuration' },
pageSize: 8,
});
}}
formItems={[
{
label: '组态名称',
key: 'name$LIKE',
type: 'string',
},
// {
// label: '组态分类',
// key: 'classifiedId$LIKE',
// type: 'list',
// props: {
// data: categoryList,
// mode: 'default'
// }
// }
]}
/>
</div>
<div className={styles.tableListOperator}>
<Button icon="plus" type="primary" onClick={() => setSaveVisible(true)}>
新建组态
</Button>
<Divider type="vertical" />
<Upload
action="/jetlinks/file/static"
headers={{
'X-Access-Token': getAccessToken(),
}}
showUploadList={false}
accept=".json"
beforeUpload={file => {
const reader = new FileReader();
reader.readAsText(file);
reader.onload = result => {
try {
uploadProps(JSON.parse(result.target.result));
} catch (error) {
message.error('文件格式错误');
}
};
}}
>
<Button>
<Icon type="upload" />
快速导入
</Button>
</Upload>
</div>
</div>
</Card>
<div style={{ marginBottom: '30px' }}>
<div className={styles.cardList}>
<List<any>
rowKey="id"
grid={{ gutter: 24, xl: 4, lg: 3, md: 2, sm: 2, xs: 1 }}
dataSource={dataList.data || []}
pagination={{
current: dataList.pageIndex + 1,
total: dataList.total,
pageSize: dataList.pageSize,
onChange: (page, pageSize) => {
onListChange(page, pageSize || 8);
},
onShowSizeChange: (page, size) => {
onListChange(page, size);
},
showQuickJumper: true,
showSizeChanger: true,
// hideOnSinglePage: true,
pageSizeOptions: ['8', '16', '40', '80'],
style: { marginTop: -20 },
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${dataList.pageIndex + 1}/${Math.ceil(
dataList.total / dataList.pageSize,
)}页`,
}}
renderItem={item => {
if (item && item.id) {
let metadata =
item.metadata != undefined && item.metadata != ''
? JSON.parse(item.metadata)
: {};
return (
<List.Item key={item.id}>
<Card
hoverable
bodyStyle={{ paddingBottom: 20 }}
onMouseEnter={() => setId(item.id)}
onMouseLeave={() => setId('')}
actions={[
<Tooltip placement="bottom" title="编辑">
<EditOutlined
onClick={() => {
setEditVisible(true);
setParam({
id: item.id,
name: item.name,
description: item.description,
catalogId: item.catalogId,
url: url,
});
}}
/>
</Tooltip>,
<Tooltip placement="bottom" title="预览">
<EyeOutlined
onClick={() => {
url != ''
? window.open(url + `#/?id=${id}&token=${token}`, '_blank')
: message.error('配置错误,请联系管理员');
}}
/>
</Tooltip>,
<Tooltip placement="bottom" title="复制">
<SwitcherOutlined
onClick={() => {
setCopyVisible(true);
setParam({ url: url, metadata: item.metadata });
}}
/>
</Tooltip>,
<Tooltip placement="bottom" title="下载">
<Icon
type="download"
onClick={() => {
downloadObject(item, '组态');
}}
/>
</Tooltip>,
<Tooltip key="more_actions" title="">
<Dropdown
overlay={
<Menu>
<Menu.Item key="1">
<Button
onClick={() => {
updateState(item.state.value, item.id);
}}
icon={item.state.value === 'enabled' ? 'close' : 'check'}
type="link"
>
{item.state.value === 'enabled' ? '禁用' : '启用'}
</Button>
</Menu.Item>
{item.state.value === 'disabled' && (
<Menu.Item key="2">
<Button
icon="delete"
type="link"
onClick={() => {
delConfirm(item.id);
}}
>
删除
</Button>
</Menu.Item>
)}
</Menu>
}
>
<Icon type="ellipsis" />
</Dropdown>
</Tooltip>,
]}
>
<Card.Meta
avatar={
<Avatar
size={60}
src={
metadata.visual != undefined &&
metadata.visual.backgroundUrl != undefined
? metadata.visual.backgroundUrl
: false
}
/>
}
title={
<AutoHide title={item.name} style={{ width: '95%', fontWeight: 600 }} />
}
description={<AutoHide title={item.id} style={{ width: '95%' }} />}
/>
<div className={styles.status}>
<div style={{ textAlign: 'center', minWidth: '80px' }}>
<p>
状态: <span style={{ fontWeight: 600 }}>已{item.state.text}</span>
</p>
</div>
<div style={{ textAlign: 'center', minWidth: '80px' }}>
{/* <p>分类: <span style={{fontWeight:600}}>{findCategory(item.catalogId)}</span></p> */}
</div>
</div>
<div
className={styles.edit}
style={{ display: item.id == id ? 'block' : 'none' }}
>
<div className={styles.editBtn}>
<a
onClick={() => {
url != ''
? window.open(url + `#/?id=${id}&token=${token}`, '_blank')
: message.error('配置错误,请联系管理员');
}}
>
编辑
</a>
</div>
</div>
</Card>
</List.Item>
);
}
return;
}}
/>
</div>
{saveVisible && (
<Save
data={url}
close={() => {
setSaveVisible(false);
}}
save={() => {
setSaveVisible(false);
handleSearch(searchParam);
}}
/>
)}
{copyVisible && (
<Copy
data={param}
close={() => {
setCopyVisible(false);
}}
save={() => {
setCopyVisible(false);
handleSearch(searchParam);
}}
/>
)}
{editVisible && (
<Edit
data={param}
close={() => {
setEditVisible(false);
}}
save={() => {
setEditVisible(false);
handleSearch(searchParam);
}}
/>
)}
</div>
</PageHeaderWrapper>
);
}
Example #5
Source File: index.tsx From amiya with MIT License | 4 votes |
export default function Demo() {
// 列表控制
const listRef = useRef<any>()
// 当前激活的 tab
const [activeTab, setActiveTab] = useState<string>('')
// 弹窗详情
const [detail, setDetail] = useState<AnyKeyProps>({
visible: false,
data: {}
})
/**
* 切换 tab 事件
* @param value 当前 tab 值
*/
const handleTabChange = (value: string) => {
setActiveTab(value)
listRef.current.reset()
}
/**
* 查看详情
* @param record 当前行数据
*/
const goDetail = (record: Record) => {
detailApi({ id: record.id }).then(res => {
setDetail({
visible: true,
data: res.data
})
})
}
return (
<div className="cnode-list">
{/* tab 区域 */}
<Tabs activeKey={activeTab} onChange={handleTabChange} type="card">
{options.map(option => (
<Tabs.TabPane tab={option.label} key={option.value} />
))}
</Tabs>
{/* 列表区域 */}
<AySearchList
api={listApi}
extraVisible={false}
listExtend={{ itemLayout: 'horizontal' }}
extendSearchParams={{ tab: activeTab }}
listHeader={<div style={{ height: 16 }}>{/* 占位空间 */}</div>}
ref={listRef}
onParamsChange={() => window.scrollTo({ behavior: 'smooth', top: 0 })}
renderItem={(record: Record) => {
return (
<List.Item
key={record.id}
actions={[
<AyAction icon={<MessageOutlined />} sub tooltip="最后回复时间">
<span>{moment(record.last_reply_at).fromNow()}</span>
</AyAction>,
<AyAction icon={<MessageOutlined />} sub tooltip="回复数">
<span>{record.reply_count}</span>
</AyAction>,
<AyAction icon={<EyeOutlined />} sub tooltip="阅读数">
<span>{record.visit_count}</span>
</AyAction>
]}
>
<List.Item.Meta
avatar={<Avatar src={record.author.avatar_url} />}
title={
<Space>
<a onClick={() => goDetail(record)}>{record.title}</a>
<span>
{record.good && <Tag color="green">精品</Tag>}
{record.top && <Tag color="blue">置顶</Tag>}
</span>
</Space>
}
description={moment(record.create_at).fromNow()}
/>
</List.Item>
)
}}
/>
{/* 弹窗详情 */}
<AyDialog
drawer
width={800}
footer={false}
destroyOnClose
visible={detail.visible}
setVisible={() => setDetail({ ...detail, visible: false })}
title="文章详情"
>
<div dangerouslySetInnerHTML={{ __html: detail.data.content }}></div>
<List
itemLayout="horizontal"
header={`${detail?.data?.replies?.length || 0} 条回复`}
dataSource={detail?.data?.replies || []}
renderItem={(comment: Record) => (
<Comment
key={comment.id}
author={comment.author.loginname}
avatar={<Avatar src={comment.author.avatar_url} alt={comment.author.avatar_url} />}
content={<div dangerouslySetInnerHTML={{ __html: comment.content }}></div>}
datetime={
<Tooltip title={moment(comment.create_at).format('YYYY-MM-DD HH:mm:ss')}>
<span>{moment(comment.create_at).fromNow()}</span>
</Tooltip>
}
/>
)}
/>
</AyDialog>
</div>
)
}
Example #6
Source File: Results.tsx From datart with Apache License 2.0 | 4 votes |
Results = memo(({ height = 0, width = 0 }: ResultsProps) => {
const { actions } = useViewSlice();
const dispatch = useDispatch();
const viewId = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'id' }),
) as string;
const model = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'model' }),
) as HierarchyModel;
const columnPermissions = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'columnPermissions' }),
) as ColumnPermission[];
const stage = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'stage' }),
) as ViewViewModelStages;
const previewResults = useSelector(state =>
selectCurrentEditingViewAttr(state, { name: 'previewResults' }),
) as ViewViewModel['previewResults'];
const roles = useSelector(selectRoles);
const t = useI18NPrefix('view');
const dataSource = useMemo(
() => previewResults.map(o => ({ ...o, [ROW_KEY]: uuidv4() })),
[previewResults],
);
const modelChange = useCallback(
(columnName: string, column: Omit<Column, 'name'>) =>
({ key }) => {
let value;
if (key.includes('category')) {
const category = key.split('-')[1];
value = { ...column, category };
} else {
value = { ...column, type: key };
}
const clonedHierarchyModel = CloneValueDeep(model.hierarchy || {});
if (columnName in clonedHierarchyModel) {
clonedHierarchyModel[columnName] = value;
} else {
Object.values(clonedHierarchyModel)
.filter(col => !isEmptyArray(col.children))
.forEach(col => {
const targetChildColumnIndex = col.children!.findIndex(
child => child.name === columnName,
);
if (targetChildColumnIndex > -1) {
col.children![targetChildColumnIndex] = value;
}
});
}
dispatch(
actions.changeCurrentEditingView({
model: {
...model,
hierarchy: clonedHierarchyModel,
version: APP_CURRENT_VERSION,
},
}),
);
},
[dispatch, actions, model],
);
const roleDropdownData = useMemo(
() =>
roles.map(({ id, name }) => ({
key: id,
title: name,
value: id,
isLeaf: true,
})),
[roles],
);
const checkRoleColumnPermission = useCallback(
columnName => checkedKeys => {
const fullPermissions = Object.keys(model?.columns || {});
dispatch(
actions.changeCurrentEditingView({
columnPermissions: roleDropdownData.reduce<ColumnPermission[]>(
(updated, { key }) => {
const permission = columnPermissions.find(
({ subjectId }) => subjectId === key,
);
const checkOnCurrentRole = checkedKeys.includes(key);
if (permission) {
if (checkOnCurrentRole) {
const updatedColumnPermission = Array.from(
new Set(permission.columnPermission.concat(columnName)),
);
return fullPermissions.sort().join(',') !==
updatedColumnPermission.sort().join(',')
? updated.concat({
...permission,
columnPermission: updatedColumnPermission,
})
: updated;
} else {
return updated.concat({
...permission,
columnPermission: permission.columnPermission.filter(
c => c !== columnName,
),
});
}
} else {
return !checkOnCurrentRole
? updated.concat({
id: uuidv4(),
viewId,
subjectId: key,
subjectType: SubjectTypes.Role,
columnPermission: fullPermissions.filter(
c => c !== columnName,
),
})
: updated;
}
},
[],
),
}),
);
},
[dispatch, actions, viewId, model, columnPermissions, roleDropdownData],
);
const getExtraHeaderActions = useCallback(
(name: string, column: Omit<Column, 'name'>) => {
// 没有记录相当于对所有字段都有权限
const checkedKeys =
columnPermissions.length > 0
? roleDropdownData.reduce<string[]>((selected, { key }) => {
const permission = columnPermissions.find(
({ subjectId }) => subjectId === key,
);
if (permission) {
return permission.columnPermission.includes(name)
? selected.concat(key)
: selected;
} else {
return selected.concat(key);
}
}, [])
: roleDropdownData.map(({ key }) => key);
return [
<Popup
key={`${name}_columnpermission`}
trigger={['click']}
placement="bottomRight"
content={
<Tree
className="dropdown"
treeData={roleDropdownData}
checkedKeys={checkedKeys}
loading={false}
selectable={false}
onCheck={checkRoleColumnPermission(name)}
blockNode
checkable
/>
}
>
<Tooltip title={t('columnPermission.title')}>
<ToolbarButton
size="small"
iconSize={FONT_SIZE_BASE}
icon={
checkedKeys.length > 0 ? (
<EyeOutlined
className={classnames({
partial: checkedKeys.length !== roleDropdownData.length,
})}
/>
) : (
<EyeInvisibleOutlined />
)
}
/>
</Tooltip>
</Popup>,
];
},
[columnPermissions, roleDropdownData, checkRoleColumnPermission, t],
);
const pagination = useMemo(
() => ({
defaultPageSize: 100,
pageSizeOptions: ['100', '200', '500', '1000'],
}),
[],
);
return stage > ViewViewModelStages.Fresh ? (
<TableWrapper>
<SchemaTable
height={height ? height - 96 : 0}
width={width}
model={model.columns || {}}
hierarchy={model.hierarchy || {}}
dataSource={dataSource}
pagination={pagination}
getExtraHeaderActions={getExtraHeaderActions}
onSchemaTypeChange={modelChange}
hasCategory
/>
</TableWrapper>
) : (
<InitialDesc>
<p>
{t('resultEmpty1')}
<CaretRightOutlined />
{t('resultEmpty2')}
</p>
</InitialDesc>
);
})
Example #7
Source File: index.tsx From sidebar with Apache License 2.0 | 4 votes |
MaterialListCard: React.FC<MaterialListProps> = (props) => {
const {setKeyword, loading, items, allTags} = props;
const [filterVisible, setFilterVisible] = useState(false);
const [tagKeyword, setTagKeyword] = useState("");
const [filteredTags, setFilteredTags] = useState<MaterialLibraryTag[]>([])
const [selectedTags, setSelectedTags] = useState<MaterialLibraryTag[]>([])
useEffect(() => {
setFilteredTags(allTags.filter((tag) => tag.name.includes(tagKeyword)))
}, [tagKeyword, allTags])
return <>
<div className={styles.searchBar}>
<Input.Search placeholder="输入关键词" loading={loading} style={{marginRight: 12}} allowClear onSearch={(value) => {
setKeyword(value);
}}/>
<Dropdown
visible={filterVisible}
overlay={
<div className={styles.overlay}>
<div style={{padding: '0 8px'}}>
<div className={styles.overlayTitle}>素材标签 ( {allTags.length} )</div>
<Form
layout={'horizontal'}
>
<Input
allowClear={true}
placeholder={'输入关键词搜索标签'}
value={tagKeyword}
onChange={(e) => {
setTagKeyword(e.currentTarget.value)
}}
/>
<div style={{padding: "12px 0"}}>
{filteredTags?.map((tag) => {
const isSelected = selectedTags.map((selectedTag) => selectedTag.id)?.includes(tag?.id);
return (
<Space>
<Tag
style={{cursor: 'pointer'}}
className={`tag-item ${isSelected ? ' selected-tag-item' : ''}`}
key={tag.id}
onClick={() => {
if (tag?.id && isSelected) {
setSelectedTags(selectedTags.filter((selectedTag) => {
return selectedTag.id !== tag?.id
}))
} else {
setSelectedTags([...selectedTags, tag])
}
}}
>
{tag.name}
{isSelected && (
<CloseOutlined style={{marginLeft: 6, fontSize: 11}}/>
)}
</Tag>
</Space>
)
})}
</div>
{allTags?.length === 0 && <Empty style={{marginTop: 36, marginBottom: 36}}/>}
<div style={{display: 'flex', justifyContent: 'flex-end'}}>
<Button onClick={() => setFilterVisible(false)}>取消</Button>
<Button
style={{marginLeft: 6}}
type='primary'
htmlType="submit"
onClick={() => {
setFilterVisible(false)
if (props.setSelectedTags) {
props.setSelectedTags(selectedTags || [])
}
}}>完成</Button>
</div>
</Form>
</div>
</div>
} trigger={['click']}>
<div>
<Button
onClick={() => {
setFilterVisible(!filterVisible)
}}>筛选</Button>
</div>
</Dropdown>
</div>
{selectedTags.length > 0 && (
<div className={styles.filterBar}>
<div className={styles.tagList}>
{selectedTags?.map((tag) => (
<span
key={tag.id}
className={styles.tag}
style={{cursor: 'pointer'}}
onClick={() => {
setSelectedTags(selectedTags.filter((item) => item.id !== tag.id))
}}
>
{tag.name}
<CloseOutlined style={{marginLeft: 6, fontSize: 11}}/>
</span>
))}
</div>
{selectedTags.length > 0 && (
<Button onClick={() => setSelectedTags([])} icon={<ClearOutlined/>} type={'link'}>清空筛选</Button>
)}
</div>
)}
<div>
{items.length === 0 && (
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE}/>
)}
{items.length > 0 && (
<Space direction={'vertical'} className={styles.materialList}>
{items.map((item) => (
<Space direction={'horizontal'} className={styles.materialItem} key={item.id}>
<Space>
<span className={styles.sendButtonWrap}>
<Button
// 发送一组话术
onClick={async () => {
await SendMaterial(item);
}}
type={'link'}
className={styles.sendButton}
icon={getIcon('icon-fasong')}
/>
</span>
</Space>
<Space className={styles.materialPreview}>
<div className={styles.leftPart}>
{item.material_type === 'poster' && (
<Image src={calFileIcon(item)} fallback={fileIconImage}
preview={item.material_type === 'poster' ? {mask: <EyeOutlined/>} : false}/>
)}
{item.material_type === 'link' && (
<Image style={{cursor: 'pointer'}} onClick={() => {
window.open(item.link)
}} src={calFileIcon(item)} fallback={fileIconImage}
preview={false}/>
)}
{["video", "pdf", "ppt", "excel", "word"].includes(item.material_type) && (
<Image style={{cursor: 'pointer'}} onClick={() => {
window.open(item.url)
}} src={calFileIcon(item)} fallback={fileIconImage}
preview={false}/>
)}
</div>
<div className={styles.rightPart}>
<div className={styles.title}>{item.title}</div>
<div className={styles.desc}>{item.digest}</div>
<div className={styles.tagList}>
{item?.tags?.map((tag) => (
<span key={tag.id} className={styles.tag}>{tag.name}</span>
))}
</div>
</div>
</Space>
</Space>
))}
</Space>
)}
</div>
</>
}
Example #8
Source File: ConfigurableFieldsForm.tsx From jitsu with MIT License | 4 votes |
ConfigurableFieldsFormComponent = ({
fieldsParamsList,
form,
extraForms,
initialValues,
availableOauthBackendSecrets,
hideFields,
handleTouchAnyField,
setFormValues,
setInitialFormValues,
}: Props) => {
const [debugModalsStates, setDebugModalsStates] = useState<{ [id: string]: boolean }>({})
const [debugModalsValues, setDebugModalsValues] = useState<{ [id: string]: string }>({})
const forceUpdateAll = useForceUpdate()
const { forceUpdatedTargets, forceUpdateTheTarget } = useForceUpdateTarget()
const handleTouchField = debounce(handleTouchAnyField ?? (() => {}), 1000)
const handleChangeIntInput = useCallback(
(id: string) => (value: number) => {
form.setFieldsValue({ [id]: value })
},
[form]
)
const handleChangeTextInput = useCallback(
(id: string) => (value: string) => {
form.setFieldsValue({ [id]: value })
},
[form]
)
const handleChangeSwitch = useCallback(
(id: string) => (value: boolean) => {
form.setFieldsValue({ [id]: value })
handleTouchAnyField?.()
forceUpdateAll()
handleTouchField()
},
[form, forceUpdateAll]
)
const handleOpenDebugger = useCallback(
(id: string) => {
setDebugModalsValues({ ...debugModalsValues, [id]: form.getFieldValue(id) })
setDebugModalsStates({ ...debugModalsStates, [id]: true })
},
[form]
)
const handleJsonChange = (id: string) => (value: string) => {
const values = {
[id]: value ? value : "",
}
form.setFieldsValue(values)
setFormValues?.(form.getFieldsValue())
handleTouchField()
}
const getInitialValue = (id: string, defaultValue: any, constantValue: any, type: string) => {
const initial = get(initialValues, id)
if (typeof initial !== "undefined") {
return initial
}
let calcValue: any
if (typeof defaultValue !== "undefined") {
calcValue = defaultValue
} else if (typeof constantValue !== "undefined") {
calcValue = constantValue
} else if (type === "boolean") {
calcValue = false
} else if (type === "json") {
calcValue = {}
} else if (type === "javascript") {
calcValue = "return {}"
} else if (type === "html") {
calcValue = "<script>\n</script>"
} else if (type.indexOf("array/") === 0) {
calcValue = []
} else {
calcValue = ""
}
return type === "json" ? JSON.stringify(calcValue) : calcValue
}
const getFieldComponent = (
type: ParameterType<any>,
id: string,
defaultValue?: any,
constantValue?: any,
jsDebugger?: "object" | "string" | null,
bigField?: boolean,
displayName?: string,
codeSuggestions?: string,
documentation?: React.ReactNode,
validationRules?: FormItemProps["rules"]
) => {
const defaultValueToDisplay =
form.getFieldValue(id) ?? getInitialValue(id, defaultValue, constantValue, type?.typeName)
form.setFieldsValue({ ...form.getFieldsValue(), [id]: defaultValueToDisplay })
const className = hideFields?.some(field => field === getFieldNameById(id)) ? "hidden" : ""
const formItemWrapperProps: FormItemWrapperProps = {
type,
id,
bigField,
displayName,
documentation,
validationRules,
className,
}
switch (type?.typeName) {
case "password":
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<Input.Password
autoComplete="off"
iconRender={visible => (visible ? <EyeOutlined /> : <EyeInvisibleOutlined />)}
/>
</FormItemWrapper>
)
case "int": {
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<InputNumber autoComplete="off" inputMode="numeric" onChange={handleChangeIntInput(id)} />
</FormItemWrapper>
)
}
case "selection": {
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<Select
allowClear
mode={type.data.maxOptions > 1 ? "multiple" : undefined}
onChange={() => forceUpdateTheTarget("select")}
>
{type.data.options.map(({ id, displayName }: Option) => {
return (
<Select.Option value={id} key={id}>
{displayName}
</Select.Option>
)
})}
</Select>
</FormItemWrapper>
)
}
case "array/string":
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<EditableList initialValue={defaultValueToDisplay} />
</FormItemWrapper>
)
case "javascript":
case "html":
case "json": {
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<CodeEditor
initialValue={defaultValueToDisplay}
className={styles.codeEditor}
extraSuggestions={codeSuggestions}
language={type?.typeName}
handleChange={handleJsonChange(id)}
/>
<span className="z-50">
{jsDebugger && (
<>
{bigField ? (
<Button
size="large"
className="absolute mr-0 mt-0 top-0 right-0"
type="text"
onClick={() => handleOpenDebugger(id)}
icon={<CodeOutlined />}
>
Open Debugger
</Button>
) : (
<Tooltip title="Debug expression">
<span className="absolute top-1.5 right-3">
<BugIcon onClick={() => handleOpenDebugger(id)} className={styles.bugIcon} />
</span>
</Tooltip>
)}
</>
)}
</span>
</FormItemWrapper>
)
}
case "boolean":
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
{bigField ? (
<SwitchWithLabel
label={displayName}
id={id}
onChange={handleChangeSwitch(id)}
defaultChecked={!!defaultValueToDisplay}
/>
) : (
<Switch className={"mb-0.5"} onChange={handleChangeSwitch(id)} defaultChecked={!!defaultValueToDisplay} />
)}
</FormItemWrapper>
)
case "file":
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<InputWithUpload onChange={handleChangeTextInput(id)} value={defaultValueToDisplay} />
</FormItemWrapper>
)
case "description":
return (
<div key={id} className="ant-row ant-form-item form-field_fixed-label">
<div className="ant-col ant-col-4 ant-form-item-label">
<label>{displayName}:</label>
</div>
<div className="ant-col ant-col-20 ant-form-item-control pt-1.5">{defaultValue}</div>
</div>
)
case "oauthSecret":
case "string":
default: {
const backendSecretAvailable =
type?.typeName === "oauthSecret" &&
(availableOauthBackendSecrets === "all_from_config" ||
availableOauthBackendSecrets?.some(name => getFieldNameById(id) === name))
if (backendSecretAvailable) {
formItemWrapperProps.validationRules = validationRules.filter(value => !value["required"])
}
const placeholder = backendSecretAvailable
? "Leave this field empty to use a value provided by Jitsu"
: undefined
return (
<FormItemWrapper key={id} {...formItemWrapperProps}>
<InputWithDebug
id={id}
placeholder={placeholder}
jsDebugger={jsDebugger}
onButtonClick={() => handleOpenDebugger(id)}
/>
</FormItemWrapper>
)
}
}
}
const handleDebuggerRun = async (field: string, debuggerType: "object" | "string", values: DebuggerFormValues) => {
let transform = {}
if (field === "_transform") {
transform = {
_transform_enabled: true,
_transform: values.code,
}
}
const configForm = extraForms && extraForms[0]
const mappingForm = extraForms && extraForms[1]
const data = {
reformat: debuggerType == "string",
uid: initialValues._uid,
type: initialValues._type,
field: field,
expression: values.code,
object: JSON.parse(values.object),
config: makeObjectFromFieldsValues({
...initialValues,
...configForm?.getFieldsValue(),
...mappingForm?.getFieldsValue(),
...transform,
}),
template_variables: Object.entries((configForm || form).getFieldsValue())
.filter(v => v[0].startsWith("_formData._"))
.reduce((accumulator: any, currentValue: [string, unknown]) => {
set(accumulator, currentValue[0].replace("_formData._", ""), currentValue[1])
return accumulator
}, {}),
}
return services.backendApiClient.post(`/destinations/evaluate?project_id=${services.activeProject.id}`, data)
}
const handleCloseDebugger = id => setDebugModalsStates({ ...debugModalsStates, [id]: false })
const handleSaveDebugger = (id, value: string) => {
form.setFieldsValue({ [id]: value })
handleCloseDebugger(id)
}
/**
* Runs after every re-render caused by `Select` field change
* to pick up the values of conditionally rendered fields.
*/
useEffect(() => {
const isInitialRender = !forceUpdatedTargets["select"]
if (!isInitialRender) setFormValues?.(form.getFieldsValue())
}, [forceUpdatedTargets["select"]])
useEffect(() => {
/**
*
* 1st render:
* component creates fields, fills them with values,
* lets the `form` instance to pick them
*
*/
let formValues = {}
const formFields: Parameters<typeof form.setFields>[0] = []
fieldsParamsList.forEach((param: Parameter) => {
const initConfig = makeObjectFromFieldsValues(formValues)
const fieldNeeded = !param.omitFieldRule?.(initConfig)
const id = param.id
const constantValue = typeof param.constant === "function" ? param.constant?.(initConfig) : param.constant
const initialValue = getInitialValue(id, param.defaultValue, constantValue, param.type?.typeName)
if (fieldNeeded) {
formValues[id] = initialValue
formFields.push({
name: id,
value: initialValue,
touched: false,
})
}
})
form.setFields(formFields)
/**
* @reason
* `formValues` holds correct values for dynamically rendered fields
* @warning
* Using `form.getFieldsValue()` instead of `formValues` is not recommended because
* it needs form to re-render once to pick up values of dynamically rendered fields
*/
setInitialFormValues?.(formValues)
/**
*
* 2nd render: component removes/adds fields conditionally
* depending on the form values
*
*/
forceUpdateAll()
}, [])
return (
<>
{fieldsParamsList.map(
({
id,
documentation,
displayName,
type,
defaultValue,
required,
constant,
omitFieldRule,
jsDebugger,
bigField,
codeSuggestions,
validator,
}: Parameter) => {
const currentFormValues = form.getFieldsValue() ?? {}
const defaultFormValues = fieldsParamsList.reduce(
(result, { id, defaultValue }) => ({
...result,
[id]: defaultValue,
}),
{}
)
const formItemName = id
const formValues = {
...defaultFormValues,
...currentFormValues,
}
const parsedFormValues = makeObjectFromFieldsValues(formValues)
const constantValue = typeof constant === "function" ? constant?.(parsedFormValues) : constant
const isHidden = constantValue !== undefined
const isOmitted = omitFieldRule ? omitFieldRule(parsedFormValues) : false
const validationRules: FormItemProps["rules"] = []
if (!isHidden) {
const isRequired = typeof required === "boolean" ? required : required?.(parsedFormValues)
if (isRequired)
validationRules.push({
required: true,
message: `${displayName} field is required.`,
})
if (type?.typeName === "isoUtcDate")
validationRules.push(isoDateValidator(`${displayName} field is required.`))
}
if (validator) {
validationRules.push({ validator: validator })
}
return isOmitted ? null : !isHidden ? (
<Row key={id} className={cn(isHidden && "hidden")}>
<Col span={24}>
{jsDebugger ? (
<CodeDebuggerModal
visible={debugModalsStates[id]}
codeFieldLabelDebugger="Expression"
extraSuggestionsDebugger={codeSuggestions}
defaultCodeValueDebugger={debugModalsValues[id]}
handleCloseDebugger={() => handleCloseDebugger(id)}
runDebugger={values => handleDebuggerRun(id, jsDebugger, values)}
handleSaveCodeDebugger={value => handleSaveDebugger(id, value)}
/>
) : null}
{getFieldComponent(
type,
id,
defaultValue,
constantValue,
jsDebugger,
bigField,
displayName,
codeSuggestions,
documentation,
validationRules
)}
</Col>
</Row>
) : (
<Form.Item key={formItemName} name={formItemName} hidden={true} initialValue={constantValue} />
)
}
)}
</>
)
}
Example #9
Source File: index.tsx From foodie with MIT License | 4 votes |
Register: React.FC = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [username, setUsername] = useState('');
const [isPasswordVisible, setPasswordVisible] = useState(false);
const dispatch = useDispatch();
useDocumentTitle('Register to Foodie');
useEffect(() => {
return () => {
dispatch(setAuthErrorMessage(null));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const { error, isLoading } = useSelector((state: IRootReducer) => ({
error: state.error.authError,
isLoading: state.loading.isLoadingAuth
}));
const onEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value.trim();
setEmail(val.toLowerCase());
};
const onPasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value.trim();
setPassword(val);
};
const onUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value.trim();
setUsername(val.toLowerCase());
};
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (email && password && username) {
dispatch(registerStart({ email, password, username }));
}
};
return (
<div className="min-h-screen flex bg-white">
<div
className="relative hidden laptop:w-7/12 h-screen laptop:p-8 laptop:flex laptop:justify-start laptop:items-end !bg-cover !bg-no-repeat !bg-center"
style={{
background: `#f7f7f7 url(${bg})`
}}
>
{/* --- LOGO --- */}
<Link className="absolute left-8 top-8" to="/">
<img src={logo} alt="Foodie Logo" className="w-24" />
</Link>
{/* -- INFO --- */}
<h3 className="animate-fade text-white w-9/12 mb-14">
Create your account now and discover new ideas and connect with people.
</h3>
{/* --- CREDITS LINK --- */}
<a
className="animate-fade absolute bottom-8 left-8 text-1xs text-white underline"
target="_blank"
rel="noreferrer"
href="https://infinityrimapts.com/5-reasons-host-dinner-party/friends-enjoying-a-meal/"
>
Photo: Credits to the photo owner
</a>
</div>
<div className="relative animate-fade w-full text-center laptop:w-5/12 laptop:text-left flex items-center flex-col justify-center pt-8 laptop:pt-0">
<Link to="/">
<img
src={logo_dark}
alt="Foodie Logo"
className="w-24 relative mx-auto laptop:hidden"
/>
</Link>
{error && (
<div className="p-4 w-full text-center bg-red-100 border-red-400 absolute top-0 left-0">
<p className="text-red-500 text-sm">{error?.error?.message || 'Something went wrong :('}</p>
</div>
)}
<div className="w-full px-8 laptop:px-14">
<div>
<h2 className="mt-6 text-xl laptop:text-2xl font-extrabold text-gray-900">
Create your account
</h2>
</div>
<form className="mt-8 space-y-6" onSubmit={onSubmit}>
<div className="rounded-md shadow-sm space-y-2">
<div>
<label htmlFor="email-address" className="sr-only">Username</label>
<input
id="username"
name="username"
type="text"
className={` ${error ? 'input--error' : ''}`}
onChange={onUsernameChange}
autoComplete="username"
maxLength={30}
required
readOnly={isLoading}
placeholder="Username"
value={username}
/>
</div>
<div>
<label htmlFor="email-address" className="sr-only">Email address</label>
<input
id="email-address"
name="email"
type="email"
className={` ${error ? 'input--error' : ''}`}
onChange={onEmailChange}
autoComplete="email"
maxLength={64}
required
readOnly={isLoading}
placeholder="Email Address"
value={email}
/>
</div>
<div className="relative">
<label htmlFor="password" className="sr-only">Password</label>
<input
id="password"
name="password"
type={isPasswordVisible ? 'text' : 'password'}
className={`!pr-12 ${error ? 'input--error' : ''}`}
onChange={onPasswordChange}
autoComplete="current-password"
required
minLength={8}
maxLength={100}
readOnly={isLoading}
placeholder="Password"
value={password}
/>
<div className="absolute right-0 top-0 bottom-0 my-auto flex items-center justify-center w-12 h-12 hover:bg-gray-200 cursor-pointer rounded-tr-full rounded-br-full z-10">
{isPasswordVisible ? (
<EyeInvisibleOutlined
className="h-full w-full outline-none text-gray-500"
onClick={() => setPasswordVisible(false)}
/>
) : (
<EyeOutlined
className="h-full w-full outline-none"
onClick={() => setPasswordVisible(true)}
/>
)}
</div>
</div>
</div>
<div>
<button type="submit" className="button--stretch" disabled={isLoading}>
{isLoading ? 'Registering...' : 'Register'}
</button>
</div>
<i className="social-login-divider">OR</i>
<div className="flex justify-between space-x-2">
<SocialLogin isLoading={isLoading} />
</div>
</form>
<div className="text-center mt-8">
<Link
className="underline font-medium"
onClick={(e) => isLoading && e.preventDefault()}
to={LOGIN}
>
Login instead
</Link>
</div>
{/* --- COPYRIGHT -- */}
<span className="text-gray-400 text-xs mx-auto text-center block mt-4">
©Copyright {new Date().getFullYear()} Foodie
</span>
</div>
</div>
</div>
);
}
Example #10
Source File: index.tsx From foodie with MIT License | 4 votes |
Login: React.FC = () => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isPasswordVisible, setPasswordVisible] = useState(false);
const dispatch = useDispatch();
useDocumentTitle('Login to Foodie');
useEffect(() => {
return () => {
dispatch(setAuthErrorMessage(null));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const { error, isLoading } = useSelector((state: IRootReducer) => ({
error: state.error.authError,
isLoading: state.loading.isLoadingAuth
}));
const onUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value.trim();
setUsername(val);
};
const onPasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const val = e.target.value;
setPassword(val);
};
const onSubmit = async (e: FormEvent) => {
e.preventDefault();
if (username && password) {
dispatch(loginStart(username, password));
}
};
return (
<div className="min-h-screen flex bg-white">
<div
className="relative laptop:w-7/12 h-screen laptop:p-8 hidden laptop:justify-start laptop:items-end laptop:!bg-cover laptop:!bg-no-repeat laptop:!bg-center laptop:flex"
style={{
background: `#f7f7f7 url(${bg})`
}}
>
{/* --- LOGO --- */}
<Link className="absolute left-8 top-8" to="/">
<img src={logo} alt="Foodie Logo" className="w-24" />
</Link>
{/* -- INFO --- */}
<h3 className="animate-fade text-white w-9/12 mb-14">
Looking for a new idea for your next menu? You're in the right place.
</h3>
{/* --- CREDITS LINK --- */}
<a
className="animate-fade absolute bottom-8 left-8 text-1xs text-white underline"
target="_blank"
rel="noreferrer"
href="https://infinityrimapts.com/5-reasons-host-dinner-party/friends-enjoying-a-meal/"
>
Photo: Credits to the photo owner
</a>
</div>
<div className="animate-fade laptop:w-5/12 w-full flex items-center flex-col justify-center pt-8 laptop:pt-0 relative">
<Link to="/">
<img
src={logo_dark}
alt="Foodie Logo"
className="w-24 relative mx-auto laptop:hidden"
/>
</Link>
{error && (
<div className="py-2 w-full text-center bg-red-100 border-red-300 absolute top-0 left-0">
<p className="text-red-500">{error?.error?.message || 'Something went wrong :('}</p>
</div>
)}
<div className="w-full laptop:px-14 px-8 text-center laptop:text-left">
<div>
<h2 className="mt-6 text-xl laptop:text-2xl font-extrabold text-gray-900">
Login to Foodie
</h2>
</div>
<form className="mt-8 space-y-6" onSubmit={onSubmit}>
<div className="rounded-md shadow-sm -space-y-px">
<div className="mb-2">
<label htmlFor="username" className="sr-only">Username</label>
<input
id="username"
name="username"
type="text"
autoComplete="username"
value={username}
required
maxLength={30}
className={`text-center ${error ? 'input--error' : ''} laptop:text-left`}
placeholder="Username"
readOnly={isLoading}
onChange={onUsernameChange}
/>
</div>
<div className="relative">
<label htmlFor="password" className="sr-only">Password</label>
<input
id="password"
name="password"
type={isPasswordVisible ? 'text' : 'password'}
autoComplete="current-password"
required
className={`text-center !pr-12 ${error ? 'input--error' : ''} laptop:text-left`}
placeholder="Password"
minLength={8}
maxLength={100}
onChange={onPasswordChange}
readOnly={isLoading}
value={password}
/>
<div className="absolute right-0 top-0 bottom-0 my-auto flex items-center justify-center w-12 h-12 hover:bg-gray-200 cursor-pointer rounded-tr-full rounded-br-full z-10">
{isPasswordVisible ? (
<EyeInvisibleOutlined
className="h-full w-full outline-none text-gray-500"
onClick={() => setPasswordVisible(false)}
/>
) : (
<EyeOutlined
className="h-full w-full outline-none"
onClick={() => setPasswordVisible(true)}
/>
)}
</div>
</div>
</div>
<Link className="font-medium text-sm text-gray-400 inline-block laptop:block my-4 laptop:mb-0 hover:text-indigo-500 underline laptop:w-2/4 laptop:pl-4" to="/forgot-password">
Forgot your password?
</Link>
<button type="submit" className="button--stretch" disabled={isLoading}>
<LockFilled className="absolute left-8 top-0 bottom-0 my-auto" />
{isLoading ? 'Logging In...' : 'Login'}
</button>
{/* -- TOO HARD TO REPLICATE PSEUDO IN TAILWIND :() */}
<i className="social-login-divider">OR</i>
<div className="flex justify-between space-x-2">
<SocialLogin isLoading={isLoading} />
</div>
</form>
<div className="text-center mt-8">
<Link
className="underline font-medium"
onClick={(e) => isLoading && e.preventDefault()}
to={REGISTER}
>
I dont have an account
</Link>
</div>
{/* --- COPYRIGHT -- */}
<span className="text-gray-400 text-xs mx-auto text-center block mt-4">
©Copyright {new Date().getFullYear()} Foodie
</span>
</div>
</div>
</div>
);
}
Example #11
Source File: PerformanceWaterfall.tsx From posthog-foss with MIT License | 4 votes |
function EventsWithPerformanceTable(): JSX.Element {
const { pageViewEventsLoading, pageViewEvents, eventToDisplay } = useValues(apmLogic)
const { setEventToDisplay } = useActions(apmLogic)
const columns: LemonTableColumns<EventType> = [
{
title: 'URL / Screen',
key: 'url',
render: function RenderURL(_: any, pageViewEvent: EventType) {
let urlOrScene: { short: string; full: string }
if (pageViewEvent.properties['$current_url']) {
const currentURL = pageViewEvent.properties['$current_url']
try {
const url = new URL(currentURL)
urlOrScene = { short: `${url.origin}${url.pathname}`, full: url.toString() }
} catch (e) {
urlOrScene = { short: currentURL, full: currentURL }
}
} else {
urlOrScene = {
short: pageViewEvent.properties['$screen_name'],
full: pageViewEvent.properties['$screen_name'],
}
}
return (
<Tooltip title={urlOrScene.full}>
<Typography.Text style={{ width: 300 }} ellipsis={true}>
{urlOrScene.short}
</Typography.Text>
</Tooltip>
)
},
},
{
title: 'Time',
render: function RenderTime(_: any, pageViewEvent: EventType) {
return <TZLabel time={dayjs(pageViewEvent.timestamp)} formatString="MMMM DD, YYYY h:mm" />
},
},
{
title: 'Page Load Time',
render: function RenderPageLoad(_: any, pageViewEvent: EventType) {
const duration = pageViewEvent.properties['$performance_page_loaded']
return <span>{Math.round(duration)}ms</span>
},
},
{
render: function RenderButton(_: any, pageViewEvent: EventType) {
return (
<div>
<Button data-attr={`view-waterfall-button-${pageViewEvent.id}`} icon={<EyeOutlined />}>
View waterfall chart
</Button>
</div>
)
},
},
]
return (
<LemonTable
dataSource={pageViewEvents || []}
columns={columns}
loading={pageViewEventsLoading}
emptyState={
pageViewEventsLoading ? (
<div>Loading last ten events with performance measures</div>
) : (
<div>No events available</div>
)
}
rowClassName={(pageViewEvent) => {
return clsx({
'current-event': pageViewEvent.id === eventToDisplay?.id,
'cursor-pointer': true,
})
}}
onRow={(pageViewEvent) => ({
onClick: () => {
setEventToDisplay(pageViewEvent)
},
})}
data-attr="apm-waterfall-events-table"
/>
)
}
Example #12
Source File: index.tsx From jetlinks-ui-antd with MIT License | 4 votes |
Screen = (props: Props) => {
const [categoryList, setCategoryList] = useState([]);
const [dataList, setDataList] = useState({
data: [],
pageIndex: 0,
total: 0,
pageSize: 0
});
const [id, setId] = useState('');
const [url, setUrl] = useState('');
const [saveVisible, setSaveVisible] = useState(false);
const [editVisible, setEditVisible] = useState(false);
const [copyVisible, setCopyVisible] = useState(false);
const [param, setParam] = useState({});
const [searchParam, setSearchParam] = useState({pageSize: 12, pageIndex: 0, terms: {type: 'big_screen'}});
const token = getAccessToken();
const handleSearch = (params: any) => {
setSearchParam(params);
api.screen.query(encodeQueryParam(params)).then(res => {
if (res.status === 200) {
setDataList(res.result)
}
})
};
let delConfirm = (id: string) => {
confirm({
title: '删除大屏',
icon: <ExclamationCircleOutlined/>,
content: '确认删除该大屏?',
onOk() {
api.screen.remove(id).then(res => {
if (res.status === 200) {
handleSearch(searchParam);
}
})
},
onCancel() {
message.info('已取消')
}
})
};
let updateState = (state: string, id: string) => {
confirm({
title: `${state === 'enabled' ? '禁用' : '启用'}大屏`,
icon: <ExclamationCircleOutlined/>,
content: `确认${state === 'enabled' ? '禁用' : '启用'}该大屏`,
onOk() {
api.screen.update(id, {
state: {
value: state === 'enabled' ? 'disabled' : 'enabled'
}
}).then(res => {
if (res.status === 200) {
handleSearch(searchParam);
}
})
},
onCancel() {
message.info('已取消')
}
})
};
const uploadProps = (item: any) => {
api.screen.save(item).then(res => {
if (res.status === 200) {
message.success('导入成功');
handleSearch(searchParam);
}
})
};
const onChange = (page: number, pageSize: number) => {
handleSearch({
pageIndex: page - 1,
pageSize,
terms: searchParam.terms
});
};
let getView = (view: any) => {
let children = [];
if (view.children && view.children.length > 0) {
children = view.children.map((i: any) => {
return getView(i)
});
return {
id: view.id,
children: children,
pId: view.parentId,
value: view.id,
title: view.name
}
} else {
return {
id: view.id,
pId: view.parentId,
value: view.id,
title: view.name
}
}
};
useEffect(() => {
//获取跳转url
api.screen.getUrl().then((res) => {
if(res.status === 200){
if(res.result.urls['big-screen-path'] != ''){
setUrl(res.result.urls['big-screen-path'])
}else{
message.error('配置错误,请联系管理员');
}
}
});
api.categoty.queryNoPaging({}).then(res => {
if (res.status === 200) {
setCategoryList(res.result);
}
});
handleSearch(searchParam);
}, []);
const findCategory = (id:string)=>{
const category: Partial<CategoryItem> =
categoryList.find((i:any) => i.id === id) || {};
return category.name;
};
return (
<PageHeaderWrapper title="大屏管理">
<Card bordered={false}>
<div className={styles.tableList}>
<div>
<SearchForm
search={(params: any) => {
handleSearch({
terms: {...params, type: 'big_screen'},
pageSize: 8,
});
}}
formItems={[{
label: '大屏名称',
key: 'name$LIKE',
type: 'string',
},
{
label: '大屏分类',
key: 'catalogId$LIKE',
type: 'list',
props: {
data: categoryList,
// mode: 'default',
showSearch:true
}
}]}
/>
</div>
<div className={styles.tableListOperator}>
<Button icon="plus" type="primary" onClick={() => setSaveVisible(true)}>新建大屏</Button>
<Divider type="vertical"/>
<Upload
action="/jetlinks/file/static"
headers={{
'X-Access-Token': getAccessToken(),
}}
showUploadList={false} accept='.json' beforeUpload={(file) => {
const reader = new FileReader();
reader.readAsText(file);
reader.onload = (result) => {
try {
uploadProps(JSON.parse(result.target.result));
} catch (error) {
message.error('文件格式错误');
}
}
}}>
<Button><Icon type="upload"/>快速导入</Button>
</Upload>
</div>
</div>
</Card>
<div style={{marginBottom: '30px'}}>
<div className={styles.cardList}>
<List<any>
rowKey="id"
grid={{gutter: 24, xl: 4, lg: 3, md: 2, sm: 2, xs: 1}}
dataSource={dataList.data || []}
pagination={{
current: dataList.pageIndex + 1,
total: dataList.total,
pageSize: dataList.pageSize,
onChange,
showQuickJumper: true,
showSizeChanger: true,
hideOnSinglePage: true,
pageSizeOptions: ['8', '16', '40', '80'],
style: {marginTop: -20},
showTotal: (total: number) =>
`共 ${total} 条记录 第 ${dataList.pageIndex + 1}/${Math.ceil(
dataList.total / dataList.pageSize,
)}页`
}}
renderItem={item => {
if (item && item.id) {
let metadata = item.metadata != undefined && item.metadata != "" ? JSON.parse(item.metadata) : {};
return (
<List.Item key={item.id}>
<Card hoverable bodyStyle={{paddingBottom: 20}}
onMouseEnter={() => setId(item.id)} onMouseLeave={() => setId('')}
actions={[
<Tooltip placement="bottom" title="编辑">
<EditOutlined onClick={() => {
setEditVisible(true);
setParam({
id: item.id,
name: item.name,
description: item.description,
catalogId: item.catalogId,
url: url
})
}}/>
</Tooltip>,
<Tooltip placement="bottom" title="预览">
<EyeOutlined onClick={() => {
url != '' ? window.open(url + '#/view/' + item.id + '?token=' + token, '_blank') : message.error('配置错误,请联系管理员')
}}/>
</Tooltip>,
<Tooltip placement="bottom" title="复制">
<SwitcherOutlined onClick={() => {
setCopyVisible(true);
setParam({url: url, metadata: item.metadata})
}}/>
</Tooltip>,
<Tooltip placement="bottom" title="下载">
<Icon type="download" onClick={() => {
downloadObject(item, '大屏')
}}/>
</Tooltip>,
<Tooltip key="more_actions" title="">
<Dropdown overlay={
<Menu>
<Menu.Item key="1">
<Button onClick={() => {
updateState(item.state.value, item.id)
}} icon={item.state.value === 'enabled' ? 'close' : 'check'} type="link">
{item.state.value === 'enabled' ? '禁用' : '启用'}
</Button>
</Menu.Item>
{item.state.value === 'disabled' && (
<Menu.Item key="2">
<Button icon="delete" type="link" onClick={() => {
delConfirm(item.id)
}}>删除</Button>
</Menu.Item>
)}
</Menu>
}>
<Icon type="ellipsis"/>
</Dropdown>
</Tooltip>,
]}
>
<Card.Meta
avatar={<Avatar size={60} src={ metadata.visual != undefined && metadata.visual.backgroundUrl != undefined ? metadata.visual.backgroundUrl : false }/>}
title={<AutoHide title={item.name} style={{width: '95%',fontWeight:600}}/>}
description={<AutoHide title={item.id} style={{width: '95%'}}/>}
/>
<div className={styles.status}>
<div style={{textAlign: 'center', minWidth: '80px'}}>
<p>状态: <span style={{fontWeight:600}}>已{item.state.text}</span></p>
</div>
<div style={{textAlign: 'center', minWidth: '80px'}}>
<p>分类: <span style={{fontWeight:600}}>{findCategory(item.catalogId)}</span></p>
</div>
</div>
<div className={styles.edit} style={{display: item.id == id ? 'block' : 'none'}}>
<div className={styles.editBtn}><a onClick={() => {
url != '' ? window.open(url + `#/build/${id}?token=${token}`, '_blank') : message.error('配置错误,请联系管理员')
}}>编辑</a></div>
</div>
</Card>
</List.Item>
);
}
return;
}}
/>
</div>
{saveVisible && <Save data={url} close={() => {
setSaveVisible(false)
}} save={() => {
setSaveVisible(false);
handleSearch(searchParam);
}}/>}
{copyVisible && <Copy data={param} close={() => {
setCopyVisible(false)
}} save={() => {
setCopyVisible(false);
handleSearch(searchParam);
}}/>}
{editVisible && <Edit data={param} close={() => {
setEditVisible(false)
}} save={() => {
setEditVisible(false);
handleSearch(searchParam);
}}/>}
</div>
</PageHeaderWrapper>
)
}
Example #13
Source File: OpticalPathItem.tsx From slim with Apache License 2.0 | 4 votes |
render (): React.ReactNode {
const identifier = this.props.opticalPath.identifier
const description = this.props.opticalPath.description
const attributes: Array<{ name: string, value: string }> = []
if (this.props.opticalPath.illuminationWaveLength !== undefined) {
attributes.push(
{
name: 'Illumination wavelength',
value: `${this.props.opticalPath.illuminationWaveLength} nm`
}
)
}
if (this.props.opticalPath.illuminationColor !== undefined) {
attributes.push(
{
name: 'Illumination color',
value: this.props.opticalPath.illuminationColor.CodeMeaning
}
)
}
// TID 8001 "Specimen Preparation"
const specimenDescriptions: dmv.metadata.SpecimenDescription[] = (
this.props.metadata[0].SpecimenDescriptionSequence ?? []
)
specimenDescriptions.forEach(description => {
const specimenPreparationSteps: dmv.metadata.SpecimenPreparation[] = (
description.SpecimenPreparationSequence ?? []
)
specimenPreparationSteps.forEach(
(step: dmv.metadata.SpecimenPreparation, index: number): void => {
step.SpecimenPreparationStepContentItemSequence.forEach((
item: (
dcmjs.sr.valueTypes.CodeContentItem |
dcmjs.sr.valueTypes.TextContentItem |
dcmjs.sr.valueTypes.UIDRefContentItem |
dcmjs.sr.valueTypes.PNameContentItem |
dcmjs.sr.valueTypes.DateTimeContentItem
),
index: number
) => {
const name = new dcmjs.sr.coding.CodedConcept({
value: item.ConceptNameCodeSequence[0].CodeValue,
schemeDesignator:
item.ConceptNameCodeSequence[0].CodingSchemeDesignator,
meaning: item.ConceptNameCodeSequence[0].CodeMeaning
})
if (item.ValueType === dcmjs.sr.valueTypes.ValueTypes.CODE) {
item = item as dcmjs.sr.valueTypes.CodeContentItem
const value = new dcmjs.sr.coding.CodedConcept({
value: item.ConceptCodeSequence[0].CodeValue,
schemeDesignator:
item.ConceptCodeSequence[0].CodingSchemeDesignator,
meaning: item.ConceptCodeSequence[0].CodeMeaning
})
if (!name.equals(SpecimenPreparationStepItems.PROCESSING_TYPE)) {
if (name.equals(SpecimenPreparationStepItems.STAIN)) {
attributes.push({
name: 'Tissue stain',
value: value.CodeMeaning
})
}
}
} else if (item.ValueType === dcmjs.sr.valueTypes.ValueTypes.TEXT) {
item = item as dcmjs.sr.valueTypes.TextContentItem
if (!name.equals(SpecimenPreparationStepItems.PROCESSING_TYPE)) {
if (name.equals(SpecimenPreparationStepItems.STAIN)) {
attributes.push({
name: 'Tissue stain',
value: item.TextValue
})
}
}
}
})
}
)
})
const maxValue = Math.pow(2, this.props.metadata[0].BitsAllocated) - 1
const title = (
description != null ? `${identifier}: ${description}` : identifier
)
let settings
let item
if (this.props.opticalPath.isMonochromatic) {
// monochrome images that can be pseudo-colored
let colorSettings
if (this.state.currentStyle.color != null) {
colorSettings = (
<>
<Divider plain>
Color
</Divider>
<Row justify='center' align='middle' gutter={[8, 8]}>
<Col span={5}>
Red
</Col>
<Col span={14}>
<Slider
range={false}
min={0}
max={255}
step={1}
value={this.state.currentStyle.color[0]}
onChange={this.handleColorRChange}
/>
</Col>
<Col span={5}>
<InputNumber
min={0}
max={255}
size='small'
style={{ width: '65px' }}
value={this.state.currentStyle.color[0]}
onChange={this.handleColorRChange}
/>
</Col>
</Row>
<Row justify='center' align='middle' gutter={[8, 8]}>
<Col span={5}>
Green
</Col>
<Col span={14}>
<Slider
range={false}
min={0}
max={255}
step={1}
value={this.state.currentStyle.color[1]}
onChange={this.handleColorGChange}
/>
</Col>
<Col span={5}>
<InputNumber
min={0}
max={255}
size='small'
style={{ width: '65px' }}
value={this.state.currentStyle.color[1]}
onChange={this.handleColorGChange}
/>
</Col>
</Row>
<Row justify='center' align='middle' gutter={[8, 8]}>
<Col span={5}>
Blue
</Col>
<Col span={14}>
<Slider
range={false}
min={0}
max={255}
step={1}
value={this.state.currentStyle.color[2]}
onChange={this.handleColorBChange}
/>
</Col>
<Col span={5}>
<InputNumber
min={0}
max={255}
size='small'
style={{ width: '65px' }}
value={this.state.currentStyle.color[2]}
onChange={this.handleColorBChange}
/>
</Col>
</Row>
</>
)
}
let windowSettings
if (this.state.currentStyle.limitValues != null) {
windowSettings = (
<>
<Divider plain>
Values of interest
</Divider>
<Row justify='center' align='middle' gutter={[8, 8]}>
<Col span={6}>
<InputNumber
min={0}
max={this.state.currentStyle.limitValues[1]}
size='small'
style={{ width: '75px' }}
value={this.state.currentStyle.limitValues[0]}
onChange={this.handleLowerLimitChange}
/>
</Col>
<Col span={12}>
<Slider
range
min={0}
max={maxValue}
step={1}
value={[
this.state.currentStyle.limitValues[0],
this.state.currentStyle.limitValues[1]
]}
onChange={this.handleLimitChange}
/>
</Col>
<Col span={6}>
<InputNumber
min={this.state.currentStyle.limitValues[0]}
max={maxValue}
size='small'
style={{ width: '75px' }}
value={this.state.currentStyle.limitValues[1]}
onChange={this.handleUpperLimitChange}
/>
</Col>
</Row>
</>
)
}
settings = (
<div>
{windowSettings}
{colorSettings}
<Divider plain />
<Row justify='center' align='middle' gutter={[8, 8]}>
<Col span={6}>
Opacity
</Col>
<Col span={12}>
<Slider
range={false}
min={0}
max={1}
step={0.01}
value={this.state.currentStyle.opacity}
onChange={this.handleOpacityChange}
/>
</Col>
<Col span={6}>
<InputNumber
min={0}
max={1}
size='small'
step={0.1}
style={{ width: '65px' }}
value={this.state.currentStyle.opacity}
onChange={this.handleOpacityChange}
/>
</Col>
</Row>
</div>
)
const colors = this.getCurrentColors()
item = (
<Badge
offset={[-20, 20]}
count={' '}
style={{
borderStyle: 'solid',
borderWidth: '1px',
borderColor: 'gray',
visibility: this.state.isVisible ? 'visible' : 'hidden',
backgroundImage: `linear-gradient(to right, ${colors.toString()})`
}}
>
<Description
header={title}
attributes={attributes}
selectable
hasLongValues
/>
</Badge>
)
} else {
// color images
settings = (
<div>
<Row justify='center' align='middle' gutter={[8, 8]}>
<Col span={6}>
Opacity
</Col>
<Col span={12}>
<Slider
range={false}
min={0}
max={1}
step={0.01}
value={this.state.currentStyle.opacity}
onChange={this.handleOpacityChange}
/>
</Col>
<Col span={6}>
<InputNumber
min={0}
max={1}
size='small'
step={0.1}
style={{ width: '60px' }}
value={this.state.currentStyle.opacity}
onChange={this.handleOpacityChange}
/>
</Col>
</Row>
</div>
)
item = (
<Description
header={title}
attributes={attributes}
selectable
hasLongValues
/>
)
}
const buttons = []
if (this.props.isRemovable) {
buttons.push(
<Tooltip title='Remove Optical Path'>
<Button
type='default'
shape='circle'
icon={<DeleteOutlined />}
onClick={this.handleRemoval}
/>
</Tooltip>
)
}
const {
defaultStyle,
isRemovable,
isVisible,
metadata,
onVisibilityChange,
onStyleChange,
onRemoval,
opticalPath,
...otherProps
} = this.props
return (
<Menu.Item
style={{ height: '100%', paddingLeft: '3px' }}
key={this.props.opticalPath.identifier}
{...otherProps}
>
<Space align='start'>
<div style={{ paddingLeft: '14px' }}>
<Space direction='vertical' align='end'>
<Switch
size='small'
checked={this.state.isVisible}
onChange={this.handleVisibilityChange}
checkedChildren={<EyeOutlined />}
unCheckedChildren={<EyeInvisibleOutlined />}
/>
<Popover
placement='left'
content={settings}
overlayStyle={{ width: '350px' }}
title='Display Settings'
>
<Button
type='primary'
shape='circle'
icon={<SettingOutlined />}
/>
</Popover>
{buttons}
</Space>
</div>
{item}
</Space>
</Menu.Item>
)
}
Example #14
Source File: Responsive.tsx From yugong with MIT License | 4 votes |
Responsive: React.FC<Props> = () => {
useEffect(() => {
trackPageView('/首页');
}, []);
/**
* ----------
* 定义编辑模式
* ----------
*/
const { isEditing, auth } = useSelector(
(state: RootState) => state.controller,
);
const history = useHistory();
const appData = useSelector((state: RootState) => state.appData);
const activationItem = useSelector(
(state: RootState) => state.activationItem,
);
const stateTag = useSelector((state: RootState) => state.controller.stateTag);
const forceUpdateByStateTag =
useDispatch<Dispatch>().controller.forceUpdateByStateTag;
const setIsEditing = useDispatch<Dispatch>().controller.setIsEditing;
const updateAppData = useDispatch<Dispatch>().appData.updateAppData;
const updatePageData = useDispatch<Dispatch>().pageData.updatePage;
const setWindowHeight = useDispatch<Dispatch>().pageData.setWindowHeight;
const setWindowWidth = useDispatch<Dispatch>().pageData.setWindowWidth;
const updateActivationItem =
useDispatch<Dispatch>().activationItem.updateActivationItem;
const removeActivationItem =
useDispatch<Dispatch>().activationItem.removeActivationItem;
const setRunningTimes = useDispatch<Dispatch>().runningTimes.setRunningTimes;
const ref = useRef(null);
const pageData = useSelector((state: RootState) => state.pageData);
const runningTimes = useSelector((state: RootState) => state.runningTimes);
const setIsReady = useDispatch<Dispatch>().record.setIsReady;
const [, setLocalPageData] = useLocalStorage('pageData', null);
const [showDrawer, setShowDrawer] = useState(false);
const [showPageDrawer, setShowPageDrawer] = useState(false);
const [isCreate, setIsCreate] = useState(true);
const [showTemplateModal, setShowTemplateModal] = useState(false);
const [hideIframe, sethideIframe] = useState(true);
const [visibleQrcode, setVisibleQrcode] = useState(false);
// 创建postmessage通信 usePostMessage收集数据 redux 更新数据
const sendMessage = usePostMessage(({ tag, value }) => {
switch (tag) {
case 'setIsEditing':
setIsEditing(value);
break;
case 'updateAppData':
updateAppData(value);
// 同步更新被选模块的属性
if (activationItem.moduleId === undefined) return;
const asynAcactivationItem = (value as AppDataListTypes).find(
(item) => item.moduleId === activationItem.moduleId,
);
if (asynAcactivationItem?.moduleId) {
updateActivationItem(asynAcactivationItem);
}
break;
case 'updateRunningTimes':
setRunningTimes(value);
break;
case 'updatePage':
updatePageData(value);
break;
case 'id':
// 设置当前项正在被编辑
// 禁止重复设置当前编辑项
if (activationItem.moduleId === value) return;
for (let index = 0; index < appData.length; index++) {
const element = appData[index];
if (element.moduleId === value) {
updateActivationItem({ ...element });
break;
}
}
setShowDashboard(true);
break;
case 'record':
saveRecord(value)
break
default:
break;
}
});
// 收发处理,子窗口onload时向子窗口发送信息, 通知当前正处于编辑模式下,
const win: Window | null = ref.current
? (ref.current as any).contentWindow
: null;
useEffect(() => {
const windows = (document.getElementById('wrapiframe') as any)
?.contentWindow;
if (windows && !isCreate) {
windows.onload = () => {
sendMessage({ tag: 'setIsEditing', value: true }, windows);
setIsEditing(true);
setIsReady(true);
sethideIframe(false);
};
}
}, [sendMessage, setIsEditing, isCreate, setIsReady]);
useEffect(() => {
sendMessage({ tag: 'setIsEditing', value: true }, win);
setIsEditing(true);
}, [sendMessage, setIsEditing, win]);
const toggleEdit = useCallback(() => {
const states = !isEditing;
sendMessage({ tag: 'setIsEditing', value: states }, win);
setIsEditing(states);
}, [isEditing, sendMessage, setIsEditing, win]);
const toggleCreate = useCallback(() => {
setIsCreate(!isCreate);
}, [isCreate]);
// 收发处理,编辑完数据后通过sendMessage向子窗口发送最新数据。
useEffect(() => {
sendMessage(
{
tag: 'updateAppData',
value: appData,
},
win,
);
}, [sendMessage, win, appData]);
const onChangeRule = (
width: number,
height: number = window.innerHeight - 140,
) => {
setWindowWidth(width);
setWindowHeight(height);
const optPageData = { ...pageData };
optPageData.windowWidth = width;
optPageData.windowHeight = height;
setLocalPageData(optPageData);
if (win) {
sendMessage({ tag: 'updatePage', value: true }, win);
sendMessage({ tag: 'setIsEditing', value: isEditing }, win);
}
setIsEditing(true);
forceUpdateByStateTag();
};
const [showDashboard, setShowDashboard] = useState(false);
const [opacity, setOpacity] = useState('1');
// 无激活模块时隐藏设置面板
useEffect(() => {
if (!activationItem.moduleId) {
setShowDashboard(false);
}
}, [activationItem]);
const hideDashboard = useCallback(() => {
setShowDashboard(false);
removeActivationItem();
if (win) {
sendMessage({ tag: 'removeActivationItem', value: undefined }, win);
}
}, [removeActivationItem, sendMessage, win]);
const updateProject = useCallback(
(data: Template) => {
data.id = pageData.template?.id;
return updateTemplate(data);
},
[pageData],
);
interface TemplateAll extends Template {
pageData: string;
appData: string;
}
// 保存或更新项目
const onSaveProject = useCallback(
async ({
cove = [],
terminal,
isPublic,
describe,
tag,
title,
id,
}: TemplateInfo) => {
if (!auth?.isLogin) {
history.push('/login');
return;
}
// copy
const pageDataCopy = cloneDeep(pageData);
// template数据
const templateData: Template = {
title: title || pageData.pageTitle,
terminal,
cove: cove[0]?.thumbUrl,
describe,
tag: tag?.join(','),
isPublic: isPublic === true ? 1 : 0,
};
// 存入模板信息到pageData
if (!pageDataCopy.template) {
pageDataCopy.template = templateData;
}
// 完整数据
/**
* 完整数据包含页面数据、组件数据与模板信息三个部分,
* 页面数据同时也包含一份模板信息供页面处理
*/
const params: TemplateAll = {
pageData: JSON.stringify(pageData),
appData: JSON.stringify(appData),
id,
userId: auth.session?.id,
...templateData,
};
try {
loading.show();
// 更新
if (!!pageData.template?.id) {
await updateProject(params);
} else {
// 新增
const newId = await createTemplate(params);
pageDataCopy.template.id = newId;
}
message.success('已发布');
// 更新
updatePageData(pageDataCopy);
// 关闭弹窗
setShowTemplateModal(false);
// todo 展示二维码与模板访问链接,支持扫码访问
setVisibleQrcode(true);
loading.hide();
} catch (error) {
console.error(error);
loading.hide();
}
},
[
appData,
auth?.isLogin,
auth?.session?.id,
history,
pageData,
updatePageData,
updateProject,
],
);
const showPublishModal = useCallback(() => {
if (!auth?.isLogin) {
history.push('/login');
}
setShowTemplateModal(true);
}, [auth?.isLogin, history]);
const pageSearch = stringify({ tpl: pageData.template?.id, ...runningTimes.search });
const codeViewUrl = `${process.env.REACT_APP_SITE_PATH || ''}${pageSearch ? `?${pageSearch}` : ''}`;
const viewUrl = `${process.env.REACT_APP_SITE_PATH || ''}${window.location.search || '?isediting'
}`
return (
<>
{isCreate ? (
<CreateProject goBack={() => toggleCreate()} />
) : (
<div className={s.main}>
{showDashboard && isEditing ? (
<Draggable
axis="both"
handle={`.${s.header}`}
onDrag={() => setOpacity('0.5')}
onStop={() => setOpacity('1')}
>
<div className={s.dashboard} style={{ opacity }}>
<div className={s.header}>
<h3>设置面板</h3>
<CloseOutlined className={s.icon} onClick={hideDashboard} />
</div>
<MiniDashboard />
</div>
</Draggable>
) : null}
<div className={s.topmenu}>
<div className={s.create}>
<Button
type="primary"
onClick={toggleCreate}
icon={<FileAddOutlined />}
/>
{!isEditing ? (
<Button
type="default"
className={s.toggle}
onClick={toggleEdit}
icon={<EditOutlined />}
/>
) : null}
{isEditing ? (
<Button
type="default"
className={s.toggle}
onClick={toggleEdit}
icon={<EyeOutlined />}
/>
) : null}
<Button
type="default"
icon={<SettingOutlined />}
onClick={() => setShowPageDrawer(true)}
>
页面
</Button>
<Button
type="default"
icon={<PlusOutlined />}
onClick={() => setShowDrawer(true)}
>
组件
</Button>
<Undo />
<a href="https://github.com/eightfeet/yugong">
<Button type="default" icon={<GithubOutlined />}>
github
</Button>
</a>
</div>
<div className={s.save}>
<Button
type="primary"
icon={<CloudUploadOutlined />}
onClick={showPublishModal}
>
{pageData.template?.id ? '修改' : '发布'}
</Button>
{pageData.template?.id ? <>
<Button
icon={<QrcodeOutlined />}
onClick={() => setVisibleQrcode(true)}
/>
</> : null}
</div>
</div>
<Ruler onChange={onChangeRule} />
<Drawer
className={s.drawer}
title="页面设置"
width={580}
onClose={() => setShowPageDrawer(false)}
visible={showPageDrawer}
bodyStyle={{ padding: '0', overflow: 'auto' }}
maskStyle={{ backgroundColor: 'transparent' }}
footer={null}
>
{showPageDrawer ? <PageSetting /> : null}
</Drawer>
<Drawer
className={s.drawer}
title="组件库"
width={580}
onClose={() => setShowDrawer(false)}
visible={showDrawer}
bodyStyle={{ padding: '0px' }}
maskStyle={{ backgroundColor: 'transparent' }}
footer={null}
>
<Repository />
</Drawer>
<div className={s.box}>
<div
className={classNames({
[s.viewbg]: !isEditing,
})}
style={{ transition: 'all 0.5s' }}
/>
{!stateTag ? (
<div
className={s.iframebox}
style={{
width:
pageData.windowWidth === -1
? `100%`
: `${pageData.windowWidth}px`,
height: `${pageData.windowHeight}px`,
}}
>
<LoadingAnimate />
<iframe
ref={ref}
id="wrapiframe"
title="wrapiframe"
src={viewUrl}
style={{
border: 'none',
opacity: hideIframe ? 0 : 1,
minWidth: '100%',
minHeight: `${pageData.windowHeight}px`,
}}
/>
</div>
) : null}
</div>
</div>
)}
<TemplateInfoModal
visible={showTemplateModal}
onOk={onSaveProject}
onCancel={() => setShowTemplateModal(false)}
/>
<QrcodeModal
visible={visibleQrcode}
onCancel={() => setVisibleQrcode(false)}
sourceData={codeViewUrl}
title="请扫码访问"
info={<div className={s.viewurl}>访问地址:<a href={codeViewUrl} target={'_blank'} rel="noreferrer">{codeViewUrl}</a></div>}
options={{
width: 122,
margin: 1
}}
/>
</>
);
}
Example #15
Source File: Upload.tsx From yugong with MIT License | 4 votes |
Upload: React.FC<UploadProps> = ({ label, defaultImg, onChange }) => {
const [img, setimg] = useState<string>();
const [isloading, setIsloading] = useState(false);
const [viewImg, setViewImg] = useState(false);
const [wh, setWh] = useState(' ');
const moduleId = useSelector(
(state: RootState) => state.activationItem.moduleId,
);
const [csrfToken] = useCookie('csrfToken')
const ref = useRef(null);
// 创建临时图片文件
const createTempImg = useCallback((url: string) => {
const wrap = ref.current as any;
if (wrap) {
(ref.current as any).innerHTML = '';
const image = new Image();
image.src = url;
image.onload = () => {};
(ref.current as any).appendChild(image);
}
}, []);
// 获取文件宽高属性
const getTempImgWH = useCallback(() => {
const image = (ref.current as any)?.querySelector('img');
if (image) {
const str = `宽:${image.offsetWidth}px 高:${image.offsetHeight}px`;
setWh(str);
}
}, []);
// 删除临时文件
useEffect(() => {
setimg(defaultImg);
if (defaultImg) {
createTempImg(defaultImg);
}
}, [createTempImg, defaultImg, moduleId]);
const onChangeUpload = useCallback(
(info: UploadChangeParam) => {
if (info.file.status === 'uploading') {
setIsloading(true);
}
if (info.file.status === 'error') {
setIsloading(false);
}
if (info.file.response) {
setTimeout(() => {
setIsloading(false);
setimg(info.file.response.fileUrl);
createTempImg(info.file.response.fileUrl);
}, 1000);
if (onChange instanceof Function) {
onChange(info.file.response.fileUrl);
}
}
},
[createTempImg, onChange],
);
const hideView = useCallback(() => {
setViewImg(false);
}, []);
const showView = useCallback(() => {
getTempImgWH();
setViewImg(true);
}, [getTempImgWH]);
const deleteImage = useCallback(() => {
setimg('');
if (onChange instanceof Function) {
onChange('');
}
}, [onChange]);
const inputOnChange = useCallback(
(e) => {
const url = e.target.value;
if (onChange instanceof Function && isUrl(url)) {
onChange(url);
setimg(url);
createTempImg(url);
}
},
[createTempImg, onChange],
);
return (
<>
<Row className={s.row} gutter={4}>
<Col className={s.label} span={7}>
{label || ''}
</Col>
<Col span={14}>
<div className={s.button}>
{process.env.REACT_APP_DEMO === 'true' ? (
<Tooltip
overlayInnerStyle={{width: 400}}
title={
<Input style={{width: '100%'}} value={img} onChange={inputOnChange} placeholder="输入图片url" />
}
>
<span
className={classNames(s.uploadicon, s.empty, s.flid)}
style={{
backgroundImage: `url(${!isloading && img ? img : ''})`,
}}
>
{isloading ? antIcon : null}
{!img ? <PictureOutlined /> : null}
</span>
</Tooltip>
) : (
<UploadPic
accept=".jpg,.jpeg,.png"
action={'/api/upload'}
onChange={onChangeUpload}
showUploadList={false}
disabled={isloading}
headers={{
'x-csrf-token': csrfToken || ''
}}
>
<span
className={classNames(s.uploadicon, s.empty, s.flid)}
style={{
backgroundImage: `url(${!isloading && img ? img : ''})`,
}}
>
{isloading ? antIcon : null}
{!img ? <PictureOutlined /> : null}
</span>
</UploadPic>
)}
</div>
{!isloading && img ? (
<>
<Tooltip
placement="top"
trigger="hover"
mouseEnterDelay={2}
title="预览"
>
<Button
className={s.view}
type="link"
size={'small'}
onClick={showView}
icon={<EyeOutlined />}
/>
</Tooltip>
<Tooltip
placement="top"
trigger="hover"
mouseEnterDelay={2}
title="删除"
>
<Button
type="link"
danger
size={'small'}
onClick={deleteImage}
icon={<DeleteOutlined />}
/>
</Tooltip>
</>
) : null}
</Col>
</Row>
<Modal visible={viewImg} onCancel={hideView} title={wh} footer={null}>
<div className={s.ref}>
{img ? <img ref={ref} src={img} alt={''} /> : null}
</div>
</Modal>
{!isloading && img ? <div className={s.imgtemp} ref={ref} /> : null}
</>
);
}
Example #16
Source File: Icon.tsx From html2sketch with MIT License | 4 votes |
IconSymbol: FC = () => {
return (
<Row>
{/*<CaretUpOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/1.CaretUpOutlined'}*/}
{/*/>*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.MailOutlined'}*/}
{/*/>*/}
{/*<StepBackwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
{/*<StepForwardOutlined*/}
{/* className="icon"*/}
{/* symbolName={'1.General/2.Icons/2.StepBackwardOutlined'}*/}
{/*/>*/}
<StepForwardOutlined />
<ShrinkOutlined />
<ArrowsAltOutlined />
<DownOutlined />
<UpOutlined />
<LeftOutlined />
<RightOutlined />
<CaretUpOutlined />
<CaretDownOutlined />
<CaretLeftOutlined />
<CaretRightOutlined />
<VerticalAlignTopOutlined />
<RollbackOutlined />
<FastBackwardOutlined />
<FastForwardOutlined />
<DoubleRightOutlined />
<DoubleLeftOutlined />
<VerticalLeftOutlined />
<VerticalRightOutlined />
<VerticalAlignMiddleOutlined />
<VerticalAlignBottomOutlined />
<ForwardOutlined />
<BackwardOutlined />
<EnterOutlined />
<RetweetOutlined />
<SwapOutlined />
<SwapLeftOutlined />
<SwapRightOutlined />
<ArrowUpOutlined />
<ArrowDownOutlined />
<ArrowLeftOutlined />
<ArrowRightOutlined />
<LoginOutlined />
<LogoutOutlined />
<MenuFoldOutlined />
<MenuUnfoldOutlined />
<BorderBottomOutlined />
<BorderHorizontalOutlined />
<BorderInnerOutlined />
<BorderOuterOutlined />
<BorderLeftOutlined />
<BorderRightOutlined />
<BorderTopOutlined />
<BorderVerticleOutlined />
<PicCenterOutlined />
<PicLeftOutlined />
<PicRightOutlined />
<RadiusBottomleftOutlined />
<RadiusBottomrightOutlined />
<RadiusUpleftOutlined />
<RadiusUprightOutlined />
<FullscreenOutlined />
<FullscreenExitOutlined />
<QuestionOutlined />
<PauseOutlined />
<MinusOutlined />
<PauseCircleOutlined />
<InfoOutlined />
<CloseOutlined />
<ExclamationOutlined />
<CheckOutlined />
<WarningOutlined />
<IssuesCloseOutlined />
<StopOutlined />
<EditOutlined />
<CopyOutlined />
<ScissorOutlined />
<DeleteOutlined />
<SnippetsOutlined />
<DiffOutlined />
<HighlightOutlined />
<AlignCenterOutlined />
<AlignLeftOutlined />
<AlignRightOutlined />
<BgColorsOutlined />
<BoldOutlined />
<ItalicOutlined />
<UnderlineOutlined />
<StrikethroughOutlined />
<RedoOutlined />
<UndoOutlined />
<ZoomInOutlined />
<ZoomOutOutlined />
<FontColorsOutlined />
<FontSizeOutlined />
<LineHeightOutlined />
<SortAscendingOutlined />
<SortDescendingOutlined />
<DragOutlined />
<OrderedListOutlined />
<UnorderedListOutlined />
<RadiusSettingOutlined />
<ColumnWidthOutlined />
<ColumnHeightOutlined />
<AreaChartOutlined />
<PieChartOutlined />
<BarChartOutlined />
<DotChartOutlined />
<LineChartOutlined />
<RadarChartOutlined />
<HeatMapOutlined />
<FallOutlined />
<RiseOutlined />
<StockOutlined />
<BoxPlotOutlined />
<FundOutlined />
<SlidersOutlined />
<AndroidOutlined />
<AppleOutlined />
<WindowsOutlined />
<IeOutlined />
<ChromeOutlined />
<GithubOutlined />
<AliwangwangOutlined />
<DingdingOutlined />
<WeiboSquareOutlined />
<WeiboCircleOutlined />
<TaobaoCircleOutlined />
<Html5Outlined />
<WeiboOutlined />
<TwitterOutlined />
<WechatOutlined />
<AlipayCircleOutlined />
<TaobaoOutlined />
<SkypeOutlined />
<FacebookOutlined />
<CodepenOutlined />
<CodeSandboxOutlined />
<AmazonOutlined />
<GoogleOutlined />
<AlipayOutlined />
<AntDesignOutlined />
<AntCloudOutlined />
<ZhihuOutlined />
<SlackOutlined />
<SlackSquareOutlined />
<BehanceSquareOutlined />
<DribbbleOutlined />
<DribbbleSquareOutlined />
<InstagramOutlined />
<YuqueOutlined />
<AlibabaOutlined />
<YahooOutlined />
<RedditOutlined />
<SketchOutlined />
<AccountBookOutlined />
<AlertOutlined />
<ApartmentOutlined />
<ApiOutlined />
<QqOutlined />
<MediumWorkmarkOutlined />
<GitlabOutlined />
<MediumOutlined />
<GooglePlusOutlined />
<AppstoreAddOutlined />
<AppstoreOutlined />
<AudioOutlined />
<AudioMutedOutlined />
<AuditOutlined />
<BankOutlined />
<BarcodeOutlined />
<BarsOutlined />
<BellOutlined />
<BlockOutlined />
<BookOutlined />
<BorderOutlined />
<BranchesOutlined />
<BuildOutlined />
<BulbOutlined />
<CalculatorOutlined />
<CalendarOutlined />
<CameraOutlined />
<CarOutlined />
<CarryOutOutlined />
<CiCircleOutlined />
<CiOutlined />
<CloudOutlined />
<ClearOutlined />
<ClusterOutlined />
<CodeOutlined />
<CoffeeOutlined />
<CompassOutlined />
<CompressOutlined />
<ContactsOutlined />
<ContainerOutlined />
<ControlOutlined />
<CopyrightCircleOutlined />
<CopyrightOutlined />
<CreditCardOutlined />
<CrownOutlined />
<CustomerServiceOutlined />
<DashboardOutlined />
<DatabaseOutlined />
<DeleteColumnOutlined />
<DeleteRowOutlined />
<DisconnectOutlined />
<DislikeOutlined />
<DollarCircleOutlined />
<DollarOutlined />
<DownloadOutlined />
<EllipsisOutlined />
<EnvironmentOutlined />
<EuroCircleOutlined />
<EuroOutlined />
<ExceptionOutlined />
<ExpandAltOutlined />
<ExpandOutlined />
<ExperimentOutlined />
<ExportOutlined />
<EyeOutlined />
<FieldBinaryOutlined />
<FieldNumberOutlined />
<FieldStringOutlined />
<DesktopOutlined />
<DingtalkOutlined />
<FileAddOutlined />
<FileDoneOutlined />
<FileExcelOutlined />
<FileExclamationOutlined />
<FileOutlined />
<FileImageOutlined />
<FileJpgOutlined />
<FileMarkdownOutlined />
<FilePdfOutlined />
<FilePptOutlined />
<FileProtectOutlined />
<FileSearchOutlined />
<FileSyncOutlined />
<FileTextOutlined />
<FileUnknownOutlined />
<FileWordOutlined />
<FilterOutlined />
<FireOutlined />
<FlagOutlined />
<FolderAddOutlined />
<FolderOutlined />
<FolderOpenOutlined />
<ForkOutlined />
<FormatPainterOutlined />
<FrownOutlined />
<FunctionOutlined />
<FunnelPlotOutlined />
<GatewayOutlined />
<GifOutlined />
<GiftOutlined />
<GlobalOutlined />
<GoldOutlined />
<GroupOutlined />
<HddOutlined />
<HeartOutlined />
<HistoryOutlined />
<HomeOutlined />
<HourglassOutlined />
<IdcardOutlined />
<ImportOutlined />
<InboxOutlined />
<InsertRowAboveOutlined />
<InsertRowBelowOutlined />
<InsertRowLeftOutlined />
<InsertRowRightOutlined />
<InsuranceOutlined />
<InteractionOutlined />
<KeyOutlined />
<LaptopOutlined />
<LayoutOutlined />
<LikeOutlined />
<LineOutlined />
<LinkOutlined />
<Loading3QuartersOutlined />
<LoadingOutlined />
<LockOutlined />
<MailOutlined />
<ManOutlined />
<MedicineBoxOutlined />
<MehOutlined />
<MenuOutlined />
<MergeCellsOutlined />
<MessageOutlined />
<MobileOutlined />
<MoneyCollectOutlined />
<MonitorOutlined />
<MoreOutlined />
<NodeCollapseOutlined />
<NodeExpandOutlined />
<NodeIndexOutlined />
<NotificationOutlined />
<NumberOutlined />
<PaperClipOutlined />
<PartitionOutlined />
<PayCircleOutlined />
<PercentageOutlined />
<PhoneOutlined />
<PictureOutlined />
<PoundCircleOutlined />
<PoundOutlined />
<PoweroffOutlined />
<PrinterOutlined />
<ProfileOutlined />
<ProjectOutlined />
<PropertySafetyOutlined />
<PullRequestOutlined />
<PushpinOutlined />
<QrcodeOutlined />
<ReadOutlined />
<ReconciliationOutlined />
<RedEnvelopeOutlined />
<ReloadOutlined />
<RestOutlined />
<RobotOutlined />
<RocketOutlined />
<SafetyCertificateOutlined />
<SafetyOutlined />
<ScanOutlined />
<ScheduleOutlined />
<SearchOutlined />
<SecurityScanOutlined />
<SelectOutlined />
<SendOutlined />
<SettingOutlined />
<ShakeOutlined />
<ShareAltOutlined />
<ShopOutlined />
<ShoppingCartOutlined />
<ShoppingOutlined />
<SisternodeOutlined />
<SkinOutlined />
<SmileOutlined />
<SolutionOutlined />
<SoundOutlined />
<SplitCellsOutlined />
<StarOutlined />
<SubnodeOutlined />
<SyncOutlined />
<TableOutlined />
<TabletOutlined />
<TagOutlined />
<TagsOutlined />
<TeamOutlined />
<ThunderboltOutlined />
<ToTopOutlined />
<ToolOutlined />
<TrademarkCircleOutlined />
<TrademarkOutlined />
<TransactionOutlined />
<TrophyOutlined />
<UngroupOutlined />
<UnlockOutlined />
<UploadOutlined />
<UsbOutlined />
<UserAddOutlined />
<UserDeleteOutlined />
<UserOutlined />
<UserSwitchOutlined />
<UsergroupAddOutlined />
<UsergroupDeleteOutlined />
<VideoCameraOutlined />
<WalletOutlined />
<WifiOutlined />
<BorderlessTableOutlined />
<WomanOutlined />
<BehanceOutlined />
<DropboxOutlined />
<DeploymentUnitOutlined />
<UpCircleOutlined />
<DownCircleOutlined />
<LeftCircleOutlined />
<RightCircleOutlined />
<UpSquareOutlined />
<DownSquareOutlined />
<LeftSquareOutlined />
<RightSquareOutlined />
<PlayCircleOutlined />
<QuestionCircleOutlined />
<PlusCircleOutlined />
<PlusSquareOutlined />
<MinusSquareOutlined />
<MinusCircleOutlined />
<InfoCircleOutlined />
<ExclamationCircleOutlined />
<CloseCircleOutlined />
<CloseSquareOutlined />
<CheckCircleOutlined />
<CheckSquareOutlined />
<ClockCircleOutlined />
<FormOutlined />
<DashOutlined />
<SmallDashOutlined />
<YoutubeOutlined />
<CodepenCircleOutlined />
<AliyunOutlined />
<PlusOutlined />
<LinkedinOutlined />
<AimOutlined />
<BugOutlined />
<CloudDownloadOutlined />
<CloudServerOutlined />
<CloudSyncOutlined />
<CloudUploadOutlined />
<CommentOutlined />
<ConsoleSqlOutlined />
<EyeInvisibleOutlined />
<FileGifOutlined />
<DeliveredProcedureOutlined />
<FieldTimeOutlined />
<FileZipOutlined />
<FolderViewOutlined />
<FundProjectionScreenOutlined />
<FundViewOutlined />
<MacCommandOutlined />
<PlaySquareOutlined />
<OneToOneOutlined />
<RotateLeftOutlined />
<RotateRightOutlined />
<SaveOutlined />
<SwitcherOutlined />
<TranslationOutlined />
<VerifiedOutlined />
<VideoCameraAddOutlined />
<WhatsAppOutlined />
{/*</Col>*/}
</Row>
);
}
Example #17
Source File: FilterTag.tsx From ant-extensions with MIT License | 4 votes |
FilterTag: React.FC<{
filter: IFilterObject;
index: number;
disabled: boolean;
}> = React.memo(({ index, filter, disabled }) => {
const { t } = useTranslation(I18nKey);
const { field, operator, type, value, label, compare, active, negative, required } = filter;
const [open, setOpen] = useState(false);
const [editing, setEditing] = useState(required);
const { fields, updateFilter, removeFilter } = useContext(Context);
const displayValue = useMemo(() => {
const _field = fields.find((f) => f.key === field);
const _compare = fields.find((f) => f.key === compare);
let _value = value ? `"${value.toString()}"` : "";
if (type === "compare") {
_value = _compare ? _compare.name : compare || "";
}
return _value ? (
<span className="ant-ext-sb__filterTag--clip">
{_field && value && _field.type === EnumFieldType.DATE
? DateUtils.label(value.toString())
: _value}
</span>
) : undefined;
}, [fields, value, type, field, compare]);
const displayLabel = useMemo(() => {
const _field = fields.find((f) => f.key === field);
return (
<>
<span className="ant-ext-sb__filterTag--clip">{_field ? _field.name : field}</span>
<b> {t(`operator.${operator}`)} </b>
{displayValue}
</>
);
}, [field, operator, displayValue, fields, t]);
const menuOverlay = useMemo(
() => (
<Menu className="ant-ext-sb__dropdown">
<Menu.Item onClick={() => setEditing(true)}>
<EditOutlined /> {t(`label.edit`)}
</Menu.Item>
<Menu.Item onClick={() => updateFilter(index, { active: !active })}>
{active ? <EyeOutlined /> : <EyeInvisibleOutlined />}{" "}
{t(`label.${active ? "disable" : "enable"}`)}
</Menu.Item>
<Menu.Item onClick={() => updateFilter(index, { negative: !negative })}>
<Icon component={TwoTone} /> {t(`label.${negative ? "include" : "exclude"}`)}
</Menu.Item>
<Menu.Item
className="ant-typography ant-typography-danger"
onClick={() => removeFilter(index)}
>
<DeleteOutlined /> {t("label.remove")}
</Menu.Item>
</Menu>
),
[active, negative, index, removeFilter, t, updateFilter]
);
const formOverlay = useMemo(
() => (
<div className="ant-ext-sb__dropdown" data-for-required={required}>
<FilterForm
filter={filter}
index={index}
onCancel={() => [setOpen(false), setEditing(required)]}
/>
</div>
),
[filter, index, required]
);
const color = useMemo(
() =>
negative ? (type === "filter" ? "red" : "orange") : type === "filter" ? "blue" : "geekblue",
[negative, type]
);
return (
<Tag
className="ant-ext-sb__filterTag"
color={color}
data-active={active && !disabled}
data-negative={negative}
closable={!required}
onClose={() => removeFilter(index)}
>
{!required && (
<Checkbox
style={{ gridArea: "check" }}
checked={active}
disabled={disabled}
onChange={(e) => updateFilter(index, { active: e.target.checked })}
/>
)}
<Tooltip overlay={displayLabel} trigger="hover">
<Dropdown
overlay={editing ? formOverlay : menuOverlay}
trigger={["click"]}
visible={open}
disabled={disabled}
overlayStyle={{ zIndex: 1010 }}
onVisibleChange={(visible) => [setOpen(visible), !visible && setEditing(required)]}
>
<span className="ant-ext-sb__filterTag--label" onClick={() => setOpen(true)}>
{label ? label : displayLabel}
</span>
</Dropdown>
</Tooltip>
</Tag>
);
})