UNPKG

@kgdata/annotation

Version:
381 lines (322 loc) 10.9 kB
--- mobile: false nav: title: annotation path: /annotation --- ## kgAnnotation 普通实践: ```tsx import React, { useState, useEffect, useRef } from 'react'; import Annotation from '@kgdata/annotation'; import 'antd/dist/antd.css'; import { Modal, Radio, Form } from 'antd'; import mockData from './data.json'; const cyData = Object.assign({}, mockData); const App = () => { const [form] = Form.useForm(); const [isModalVisible, setIsModalVisible] = useState(false); const [annotationData, setAnnotationData] = useState(cyData); const [action, setAction] = useState(null); const [textSelectedIndex, setTextSelectedIndex] = useState([]); const [textSelected, seTextSelected] = useState(''); const [actionType, setActionType] = useState< | 'LABELCREATE' | 'LABELUPDATE' | 'LABELDELETE' | 'CONNECTIONCREATE' | 'CONNECTIONUPDATE' | 'CONNECTIONDELETE' >('LABELCREATE'); const selectLabelRef = useRef(); const annotatorStoreRef = useRef(); const connectionRef = useRef(); const selectConnectionRef = useRef(); useEffect(() => { if (textSelected) { setIsModalVisible(true); } }, [textSelected]); const onTextSelected = (startIndex, endIndex) => { const { content } = annotationData; setTextSelectedIndex([startIndex, endIndex]); seTextSelected(content.slice(startIndex, endIndex)); setActionType('LABELCREATE'); }; const onLabelDoubleClicked = (id, e) => { const { labels, content } = annotatorStoreRef.current; const { startIndex, endIndex, categoryId } = labels.filter( (v) => v.id === id, )[0]; seTextSelected(content.slice(startIndex, endIndex)); setIsModalVisible(true); form.setFieldsValue({ type: categoryId }); setActionType('LABELUPDATE'); selectLabelRef.current = id; }; const onLabelRightClicked = (id, e) => { setActionType('LABELDELETE'); // selectLabelRef.current = id; setAction({ type: 'LABELDELETE', data: { labelId: id } }); }; const onTwoLabelsClicked = (first, second) => { setActionType('CONNECTIONCREATE'); setIsModalVisible(true); connectionRef.current = [first, second]; }; const onConnectionDoubleClicked = (id, e) => { const { connections } = annotatorStoreRef.current; const { categoryId } = connections.filter((v) => v.id === id)[0]; setIsModalVisible(true); form.setFieldsValue({ type: categoryId }); setActionType('CONNECTIONUPDATE'); selectConnectionRef.current = id; }; const onConnectionRightClicked = (id, e) => { setActionType('CONNECTIONDELETE'); setAction({ type: 'CONNECTIONDELETE', data: { connectionId: id } }); }; const handleOk = () => { const { content, labelCategories, labels, connectionCategories, connections, } = annotationData; form.validateFields().then((res) => { const { type } = res; switch (actionType) { case 'LABELCREATE': setAction({ type: 'LABELCREATE', data: { categoryId: type, startIndex: textSelectedIndex[0], endIndex: textSelectedIndex[1], }, }); break; case 'LABELUPDATE': setAction({ type: 'LABELUPDATE', data: { labelId: selectLabelRef.current, categoryId: type }, }); break; case 'CONNECTIONCREATE': setAction({ type: 'CONNECTIONCREATE', data: { categoryId: type, fromId: connectionRef.current[0], toId: connectionRef.current[1], }, }); break; case 'CONNECTIONUPDATE': setAction({ type: 'CONNECTIONUPDATE', data: { connectionId: selectConnectionRef.current, categoryId: type, }, }); break; default: break; } setIsModalVisible(false); }); }; const onAnnotatorUpdate = (store) => { console.log('store..........', store); annotatorStoreRef.current = store; }; return ( <div> <Annotation annotationData={annotationData} action={action} onTextSelected={onTextSelected} onLabelDoubleClicked={onLabelDoubleClicked} onLabelRightClicked={onLabelRightClicked} onTwoLabelsClicked={onTwoLabelsClicked} onConnectionDoubleClicked={onConnectionDoubleClicked} onConnectionRightClicked={onConnectionRightClicked} onAnnotatorUpdate={onAnnotatorUpdate} /> <Modal title="新建标注" visible={isModalVisible} onOk={handleOk} onCancel={() => setIsModalVisible(false)} > {actionType.indexOf('LABEL') !== -1 ? ( <Form form={form}> <p>划词内容:{textSelected}</p> <p>语义类型:</p> <Form.Item name="type" initialValue={annotationData.labelCategories[0].id} > <Radio.Group> {annotationData.labelCategories.map((item) => { return ( <Radio key={item.id} value={item.id}> {item.text} </Radio> ); })} </Radio.Group> </Form.Item> </Form> ) : ( <Form form={form}> <Form.Item name="type" initialValue={annotationData.connectionCategories[0].id} > <Radio.Group> {annotationData.connectionCategories.map((item) => { return ( <Radio key={item.id} value={item.id}> {item.text} </Radio> ); })} </Radio.Group> </Form.Item> </Form> )} </Modal> </div> ); }; export default App; ``` # Reference API ## CSS 目前支持设置: - 整个元素的宽度(这是指定你要渲染出的`svg`元素的宽度的唯一方法),默认为容器宽度 ```css #annotation-container > svg { width: 500px; } ``` ## annotationData 在 annotationData 为 JSON 时,格式如下: ```json { "content": "文本内容", "labelCategories": [ { "id": Label类型Id, "text": Label文字, "color": Label颜色, "borderColor": Label边框颜色 }, { "id": Label类型Id, "text": Label文字, "color": Label颜色, "borderColor": Label边框颜色 }, ... ], "labels": [ { "id": LabelId, "categoryId": Label类型, "startIndex": Label开始位置(包含), "endIndex": Label结束位置(不包含) }, { "id": LabelId, "categoryId": Label类型, "startIndex": Label开始位置(包含), "endIndex": Label结束位置(不包含) }, ... ], "connectionCategories": [ { "id": Connection类型Id, "text": Connection文字 }, ... ], "connections": [ { "id": ConnectionId, "categoryId": Connection类型, "fromId": Connection开始的Label的id, "toId": Connection结束的Label的id } ] } ``` ## action 用户触发事件后,通过 action 更新视图 | 参数 | 意义 | | ---------- | ------------------ | | type | action type | | data | 当前 action 所需参数,通过 events 获取 | | Action Type | 说明 | 参数 | | ------------------ | --------------------------- | ---------------------------------- | | `LABELCREATE` | 创建 Label | (categoryId, startIndex, endIndex) | | `LABELUPDATE` | 修改 Label 的 category | (labelId, categoryId) | | `LABELDELETE` | 删除 Label | (labelId) | | `CONNECTIONCREATE` | 创建 Connection | (categoryId, fromId, toId) | | `CONNECTIONUPDATE` | 修改 Connection 的 category | (connectionId, categoryId) | | `CONNECTIONDELETE` | 删除 Connection | (connectionId) | ## Events #### onTextSelected 在用户在页面上选取了一段文本后,会触发`onTextSelected`事件。 这个 event 会带两个参数,我们将其分别称为`startIndex`和`endIndex`: | 参数 | 意义 | | ---------- | ------------------ | | startIndex | 选取部分的开始坐标 | | endIndex | 选取部分的结束坐标 | 它们代表用户选取了在原文本中的`[startIndex, endIndex)`部分。 #### onLabelRightClicked 在用户右键点击了一个 Label 后会触发这个事件。 这个 event 会带两个参数,为被点击的标注的 ID 和被点击事件本身: | 参数 | 意义 | | ----- | ----------------- | | id | 被点击的标注的 id | | event | 点击事件 | #### onLabelDoubleClicked 在用户双击了一个 Label 后会触发这个事件。 | 参数 | 意义 | | ----- | ----------------- | | id | 被点击的标注的 id | | event | 点击事件 | #### onTwoLabelsClicked 在用户先后左键点击了两个 Label 后会触发这个事件。 这个 event 会带两个参数,我们将其分别称为`first`和`second`: | 参数 | 意义 | | ------ | --------------------- | | first | 第一个点击的标注的 id | | second | 第二个点击的标注的 id | 他们代表了用户先后点击了 id 为`first`和`second`的两个标注。 #### onConnectionRightClicked 在用户右键点击了一个连接的文字部分后会触发这个事件。 这个 event 会带两个参数,为被点击的连接的 ID 和点击事件本身: | 参数 | 意义 | | ----- | ----------------- | | id | 被点击的连接的 id | | event | 点击事件 | #### onConnectionDoubleClicked 在用户双击了一个连接的文字部分后会触发这个事件。 | 参数 | 意义 | | ----- | ----------------- | | id | 被点击的连接的 id | | event | 点击事件 | #### onAnnotatorUpdate 在标注视图更新后,获取当前 annotationData | 参数 | 意义 | | ----- | -------------- | | store | 更新后视图数据 | ## 参考插件 https://github.com/synyi/poplar 其他功能,待确认需求后增加