@echothink-ui/annotation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/dist/components/AnnotationDiffViewer.d.ts +12 -0
- package/dist/components/AnnotationEditor.d.ts +26 -0
- package/dist/components/AnnotationRevisionList.d.ts +15 -0
- package/dist/components/PhoneticAnnotationField.d.ts +10 -0
- package/dist/index.cjs +233 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
- package/src/components/AnnotationDiffViewer.tsx +65 -0
- package/src/components/AnnotationEditor.tsx +123 -0
- package/src/components/AnnotationRevisionList.tsx +58 -0
- package/src/components/PhoneticAnnotationField.tsx +37 -0
- package/src/index.tsx +12 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type AnnotationDiffValues = Record<string, string | string[]>;
|
|
2
|
+
export interface AnnotationDiffField {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AnnotationDiffViewerProps {
|
|
7
|
+
before: AnnotationDiffValues;
|
|
8
|
+
after: AnnotationDiffValues;
|
|
9
|
+
fields: AnnotationDiffField[];
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function AnnotationDiffViewer({ before, after, fields, className }: AnnotationDiffViewerProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EthAction } from "@echothink-ui/core";
|
|
2
|
+
export interface AnnotationSchemaField {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
kind: "text" | "choice" | "multi";
|
|
6
|
+
options?: string[];
|
|
7
|
+
helperText?: string;
|
|
8
|
+
}
|
|
9
|
+
export type AnnotationValues = Record<string, string | string[]>;
|
|
10
|
+
export interface AnnotationSuggestion {
|
|
11
|
+
source: string;
|
|
12
|
+
values: AnnotationValues;
|
|
13
|
+
confidence?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface AnnotationEditorProps {
|
|
16
|
+
sentence: string;
|
|
17
|
+
language: string;
|
|
18
|
+
annotationSchemaFields: AnnotationSchemaField[];
|
|
19
|
+
values: AnnotationValues;
|
|
20
|
+
onChange?: (id: string, value: string | string[]) => void;
|
|
21
|
+
suggestion?: AnnotationSuggestion;
|
|
22
|
+
onAcceptSuggestion?: () => void;
|
|
23
|
+
actions?: EthAction[];
|
|
24
|
+
className?: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function AnnotationEditor({ sentence, language, annotationSchemaFields, values, onChange, suggestion, onAcceptSuggestion, actions, className }: AnnotationEditorProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EthOperationalStatus } from "@echothink-ui/core";
|
|
2
|
+
export interface AnnotationRevision {
|
|
3
|
+
id: string;
|
|
4
|
+
submittedBy: string;
|
|
5
|
+
submittedAt: string;
|
|
6
|
+
status: EthOperationalStatus;
|
|
7
|
+
comment?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AnnotationRevisionListProps {
|
|
10
|
+
revisions: AnnotationRevision[];
|
|
11
|
+
selectedId?: string;
|
|
12
|
+
onSelect?: (id: string) => void;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function AnnotationRevisionList({ revisions, selectedId, onSelect, className }: AnnotationRevisionListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface PhoneticAnnotationFieldProps {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
value: string;
|
|
5
|
+
onChange?: (value: string) => void;
|
|
6
|
+
helperText?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function PhoneticAnnotationField({ id, label, value, onChange, helperText, error, className }: PhoneticAnnotationFieldProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.tsx
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
AnnotationComponentNames: () => AnnotationComponentNames,
|
|
34
|
+
AnnotationDiffViewer: () => AnnotationDiffViewer,
|
|
35
|
+
AnnotationEditor: () => AnnotationEditor,
|
|
36
|
+
AnnotationRevisionList: () => AnnotationRevisionList,
|
|
37
|
+
PhoneticAnnotationField: () => PhoneticAnnotationField
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/components/AnnotationEditor.tsx
|
|
42
|
+
var import_clsx = __toESM(require("clsx"), 1);
|
|
43
|
+
var import_core = require("@echothink-ui/core");
|
|
44
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
45
|
+
function AnnotationEditor({
|
|
46
|
+
sentence,
|
|
47
|
+
language,
|
|
48
|
+
annotationSchemaFields,
|
|
49
|
+
values,
|
|
50
|
+
onChange,
|
|
51
|
+
suggestion,
|
|
52
|
+
onAcceptSuggestion,
|
|
53
|
+
actions,
|
|
54
|
+
className
|
|
55
|
+
}) {
|
|
56
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
57
|
+
import_core.Surface,
|
|
58
|
+
{
|
|
59
|
+
className: (0, import_clsx.default)("eth-annotation-editor", className),
|
|
60
|
+
title: "Annotation editor",
|
|
61
|
+
subtitle: language,
|
|
62
|
+
actions,
|
|
63
|
+
"data-eth-component": "AnnotationEditor",
|
|
64
|
+
children: [
|
|
65
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "eth-annotation-editor__sentence", children: sentence }),
|
|
66
|
+
suggestion ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_core.InlineNotification, { severity: "info", title: `Suggestion from ${suggestion.source}`, children: [
|
|
67
|
+
suggestion.confidence !== void 0 ? `${Math.round(suggestion.confidence * 100)}% confidence` : null,
|
|
68
|
+
onAcceptSuggestion ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.Button, { intent: "secondary", density: "compact", onClick: onAcceptSuggestion, children: "Accept suggestion" }) : null
|
|
69
|
+
] }) : null,
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "eth-annotation-editor__fields", children: annotationSchemaFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FieldControl, { field, value: values[field.id], onChange }, field.id)) })
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function FieldControl({
|
|
76
|
+
field,
|
|
77
|
+
value,
|
|
78
|
+
onChange
|
|
79
|
+
}) {
|
|
80
|
+
if (field.kind === "multi") {
|
|
81
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "eth-annotation-editor__field", children: [
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
83
|
+
import_core.MultiSelect,
|
|
84
|
+
{
|
|
85
|
+
label: field.label,
|
|
86
|
+
options: (field.options ?? []).map((option) => ({ value: option, label: option })),
|
|
87
|
+
value: Array.isArray(value) ? value : [],
|
|
88
|
+
onChange: (nextValue) => onChange?.(field.id, nextValue)
|
|
89
|
+
}
|
|
90
|
+
),
|
|
91
|
+
field.helperText ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "eth-annotation-editor__helper", children: field.helperText }) : null
|
|
92
|
+
] });
|
|
93
|
+
}
|
|
94
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_core.FormField, { id: `annotation-${field.id}`, label: field.label, helperText: field.helperText, children: field.kind === "choice" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
95
|
+
import_core.Select,
|
|
96
|
+
{
|
|
97
|
+
value: typeof value === "string" ? value : "",
|
|
98
|
+
options: [
|
|
99
|
+
{ value: "", label: "Select..." },
|
|
100
|
+
...(field.options ?? []).map((option) => ({ value: option, label: option }))
|
|
101
|
+
],
|
|
102
|
+
onChange: (event) => onChange?.(field.id, event.currentTarget.value)
|
|
103
|
+
}
|
|
104
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
105
|
+
import_core.TextInput,
|
|
106
|
+
{
|
|
107
|
+
value: typeof value === "string" ? value : "",
|
|
108
|
+
onChange: (event) => onChange?.(field.id, event.currentTarget.value)
|
|
109
|
+
}
|
|
110
|
+
) });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/components/AnnotationDiffViewer.tsx
|
|
114
|
+
var import_clsx2 = __toESM(require("clsx"), 1);
|
|
115
|
+
var import_core2 = require("@echothink-ui/core");
|
|
116
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
117
|
+
function AnnotationDiffViewer({
|
|
118
|
+
before,
|
|
119
|
+
after,
|
|
120
|
+
fields,
|
|
121
|
+
className
|
|
122
|
+
}) {
|
|
123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
124
|
+
import_core2.Surface,
|
|
125
|
+
{
|
|
126
|
+
className: (0, import_clsx2.default)("eth-annotation-diff-viewer", className),
|
|
127
|
+
title: "Annotation diff",
|
|
128
|
+
"data-eth-component": "AnnotationDiffViewer",
|
|
129
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "eth-annotation-diff-viewer__table", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("table", { children: [
|
|
130
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { children: [
|
|
131
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "col", children: "Field" }),
|
|
132
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "col", children: "Before" }),
|
|
133
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "col", children: "After" }),
|
|
134
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "col", children: "Change" })
|
|
135
|
+
] }) }),
|
|
136
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tbody", { children: fields.map((field) => {
|
|
137
|
+
const changed = formatValue(before[field.id]) !== formatValue(after[field.id]);
|
|
138
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("tr", { className: changed ? "eth-annotation-diff-viewer__row--changed" : void 0, children: [
|
|
139
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("th", { scope: "row", children: field.label }),
|
|
140
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: formatValue(before[field.id]) }),
|
|
141
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: formatValue(after[field.id]) }),
|
|
142
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_core2.Badge, { severity: changed ? "warning" : "success", children: changed ? "Changed" : "Same" }) })
|
|
143
|
+
] }, field.id);
|
|
144
|
+
}) })
|
|
145
|
+
] }) })
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
function formatValue(value) {
|
|
150
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
151
|
+
return value ?? "-";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/components/PhoneticAnnotationField.tsx
|
|
155
|
+
var import_clsx3 = __toESM(require("clsx"), 1);
|
|
156
|
+
var import_core3 = require("@echothink-ui/core");
|
|
157
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
158
|
+
function PhoneticAnnotationField({
|
|
159
|
+
id,
|
|
160
|
+
label,
|
|
161
|
+
value,
|
|
162
|
+
onChange,
|
|
163
|
+
helperText,
|
|
164
|
+
error,
|
|
165
|
+
className
|
|
166
|
+
}) {
|
|
167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: (0, import_clsx3.default)("eth-annotation-phonetic-field", className), "data-eth-component": "PhoneticAnnotationField", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_core3.FormField, { id, label, helperText: helperText ?? "IPA notation supported", error, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
168
|
+
import_core3.TextInput,
|
|
169
|
+
{
|
|
170
|
+
value,
|
|
171
|
+
inputMode: "text",
|
|
172
|
+
autoCapitalize: "off",
|
|
173
|
+
spellCheck: false,
|
|
174
|
+
onChange: (event) => onChange?.(event.currentTarget.value)
|
|
175
|
+
}
|
|
176
|
+
) }) });
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/components/AnnotationRevisionList.tsx
|
|
180
|
+
var import_clsx4 = __toESM(require("clsx"), 1);
|
|
181
|
+
var import_core4 = require("@echothink-ui/core");
|
|
182
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
183
|
+
function AnnotationRevisionList({
|
|
184
|
+
revisions,
|
|
185
|
+
selectedId,
|
|
186
|
+
onSelect,
|
|
187
|
+
className
|
|
188
|
+
}) {
|
|
189
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
190
|
+
import_core4.Surface,
|
|
191
|
+
{
|
|
192
|
+
className: (0, import_clsx4.default)("eth-annotation-revision-list", className),
|
|
193
|
+
title: "Revision history",
|
|
194
|
+
"data-eth-component": "AnnotationRevisionList",
|
|
195
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "eth-annotation-revision-list__items", children: revisions.map((revision) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
196
|
+
"article",
|
|
197
|
+
{
|
|
198
|
+
className: (0, import_clsx4.default)(
|
|
199
|
+
"eth-annotation-revision-list__item",
|
|
200
|
+
revision.id === selectedId && "eth-annotation-revision-list__item--selected"
|
|
201
|
+
),
|
|
202
|
+
children: [
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
|
|
204
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: revision.submittedBy }),
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: revision.submittedAt }),
|
|
206
|
+
revision.comment ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: revision.comment }) : null
|
|
207
|
+
] }),
|
|
208
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core4.StatusDot, { status: revision.status, label: revision.status }),
|
|
209
|
+
onSelect ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_core4.Button, { intent: "secondary", density: "compact", onClick: () => onSelect(revision.id), children: "Select" }) : null
|
|
210
|
+
]
|
|
211
|
+
},
|
|
212
|
+
revision.id
|
|
213
|
+
)) })
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/index.tsx
|
|
219
|
+
var AnnotationComponentNames = [
|
|
220
|
+
"AnnotationEditor",
|
|
221
|
+
"AnnotationDiffViewer",
|
|
222
|
+
"PhoneticAnnotationField",
|
|
223
|
+
"AnnotationRevisionList"
|
|
224
|
+
];
|
|
225
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
226
|
+
0 && (module.exports = {
|
|
227
|
+
AnnotationComponentNames,
|
|
228
|
+
AnnotationDiffViewer,
|
|
229
|
+
AnnotationEditor,
|
|
230
|
+
AnnotationRevisionList,
|
|
231
|
+
PhoneticAnnotationField
|
|
232
|
+
});
|
|
233
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.tsx","../src/components/AnnotationEditor.tsx","../src/components/AnnotationDiffViewer.tsx","../src/components/PhoneticAnnotationField.tsx","../src/components/AnnotationRevisionList.tsx"],"sourcesContent":["export * from \"./components/AnnotationEditor\";\nexport * from \"./components/AnnotationDiffViewer\";\nexport * from \"./components/PhoneticAnnotationField\";\nexport * from \"./components/AnnotationRevisionList\";\n\nexport const AnnotationComponentNames = [\n \"AnnotationEditor\",\n \"AnnotationDiffViewer\",\n \"PhoneticAnnotationField\",\n \"AnnotationRevisionList\"\n] as const;\nexport type AnnotationComponentName = (typeof AnnotationComponentNames)[number];\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport {\n Button,\n FormField,\n InlineNotification,\n MultiSelect,\n Select,\n Surface,\n TextInput\n} from \"@echothink-ui/core\";\nimport type { EthAction } from \"@echothink-ui/core\";\n\nexport interface AnnotationSchemaField {\n id: string;\n label: string;\n kind: \"text\" | \"choice\" | \"multi\";\n options?: string[];\n helperText?: string;\n}\n\nexport type AnnotationValues = Record<string, string | string[]>;\n\nexport interface AnnotationSuggestion {\n source: string;\n values: AnnotationValues;\n confidence?: number;\n}\n\nexport interface AnnotationEditorProps {\n sentence: string;\n language: string;\n annotationSchemaFields: AnnotationSchemaField[];\n values: AnnotationValues;\n onChange?: (id: string, value: string | string[]) => void;\n suggestion?: AnnotationSuggestion;\n onAcceptSuggestion?: () => void;\n actions?: EthAction[];\n className?: string;\n}\n\nexport function AnnotationEditor({\n sentence,\n language,\n annotationSchemaFields,\n values,\n onChange,\n suggestion,\n onAcceptSuggestion,\n actions,\n className\n}: AnnotationEditorProps) {\n return (\n <Surface\n className={clsx(\"eth-annotation-editor\", className)}\n title=\"Annotation editor\"\n subtitle={language}\n actions={actions}\n data-eth-component=\"AnnotationEditor\"\n >\n <p className=\"eth-annotation-editor__sentence\">{sentence}</p>\n {suggestion ? (\n <InlineNotification severity=\"info\" title={`Suggestion from ${suggestion.source}`}>\n {suggestion.confidence !== undefined ? `${Math.round(suggestion.confidence * 100)}% confidence` : null}\n {onAcceptSuggestion ? (\n <Button intent=\"secondary\" density=\"compact\" onClick={onAcceptSuggestion}>\n Accept suggestion\n </Button>\n ) : null}\n </InlineNotification>\n ) : null}\n <div className=\"eth-annotation-editor__fields\">\n {annotationSchemaFields.map((field) => (\n <FieldControl key={field.id} field={field} value={values[field.id]} onChange={onChange} />\n ))}\n </div>\n </Surface>\n );\n}\n\nfunction FieldControl({\n field,\n value,\n onChange\n}: {\n field: AnnotationSchemaField;\n value?: string | string[];\n onChange?: (id: string, value: string | string[]) => void;\n}) {\n if (field.kind === \"multi\") {\n return (\n <div className=\"eth-annotation-editor__field\">\n <MultiSelect\n label={field.label}\n options={(field.options ?? []).map((option) => ({ value: option, label: option }))}\n value={Array.isArray(value) ? value : []}\n onChange={(nextValue) => onChange?.(field.id, nextValue)}\n />\n {field.helperText ? <p className=\"eth-annotation-editor__helper\">{field.helperText}</p> : null}\n </div>\n );\n }\n\n return (\n <FormField id={`annotation-${field.id}`} label={field.label} helperText={field.helperText}>\n {field.kind === \"choice\" ? (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n options={[\n { value: \"\", label: \"Select...\" },\n ...(field.options ?? []).map((option) => ({ value: option, label: option }))\n ]}\n onChange={(event) => onChange?.(field.id, event.currentTarget.value)}\n />\n ) : (\n <TextInput\n value={typeof value === \"string\" ? value : \"\"}\n onChange={(event) => onChange?.(field.id, event.currentTarget.value)}\n />\n )}\n </FormField>\n );\n}\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport { Badge, Surface } from \"@echothink-ui/core\";\n\nexport type AnnotationDiffValues = Record<string, string | string[]>;\n\nexport interface AnnotationDiffField {\n id: string;\n label: string;\n}\n\nexport interface AnnotationDiffViewerProps {\n before: AnnotationDiffValues;\n after: AnnotationDiffValues;\n fields: AnnotationDiffField[];\n className?: string;\n}\n\nexport function AnnotationDiffViewer({\n before,\n after,\n fields,\n className\n}: AnnotationDiffViewerProps) {\n return (\n <Surface\n className={clsx(\"eth-annotation-diff-viewer\", className)}\n title=\"Annotation diff\"\n data-eth-component=\"AnnotationDiffViewer\"\n >\n <div className=\"eth-annotation-diff-viewer__table\">\n <table>\n <thead>\n <tr>\n <th scope=\"col\">Field</th>\n <th scope=\"col\">Before</th>\n <th scope=\"col\">After</th>\n <th scope=\"col\">Change</th>\n </tr>\n </thead>\n <tbody>\n {fields.map((field) => {\n const changed = formatValue(before[field.id]) !== formatValue(after[field.id]);\n return (\n <tr key={field.id} className={changed ? \"eth-annotation-diff-viewer__row--changed\" : undefined}>\n <th scope=\"row\">{field.label}</th>\n <td>{formatValue(before[field.id])}</td>\n <td>{formatValue(after[field.id])}</td>\n <td>\n <Badge severity={changed ? \"warning\" : \"success\"}>{changed ? \"Changed\" : \"Same\"}</Badge>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </Surface>\n );\n}\n\nfunction formatValue(value?: string | string[]) {\n if (Array.isArray(value)) return value.join(\", \");\n return value ?? \"-\";\n}\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport { FormField, TextInput } from \"@echothink-ui/core\";\n\nexport interface PhoneticAnnotationFieldProps {\n id: string;\n label: string;\n value: string;\n onChange?: (value: string) => void;\n helperText?: string;\n error?: string;\n className?: string;\n}\n\nexport function PhoneticAnnotationField({\n id,\n label,\n value,\n onChange,\n helperText,\n error,\n className\n}: PhoneticAnnotationFieldProps) {\n return (\n <div className={clsx(\"eth-annotation-phonetic-field\", className)} data-eth-component=\"PhoneticAnnotationField\">\n <FormField id={id} label={label} helperText={helperText ?? \"IPA notation supported\"} error={error}>\n <TextInput\n value={value}\n inputMode=\"text\"\n autoCapitalize=\"off\"\n spellCheck={false}\n onChange={(event) => onChange?.(event.currentTarget.value)}\n />\n </FormField>\n </div>\n );\n}\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport { Button, StatusDot, Surface } from \"@echothink-ui/core\";\nimport type { EthOperationalStatus } from \"@echothink-ui/core\";\n\nexport interface AnnotationRevision {\n id: string;\n submittedBy: string;\n submittedAt: string;\n status: EthOperationalStatus;\n comment?: string;\n}\n\nexport interface AnnotationRevisionListProps {\n revisions: AnnotationRevision[];\n selectedId?: string;\n onSelect?: (id: string) => void;\n className?: string;\n}\n\nexport function AnnotationRevisionList({\n revisions,\n selectedId,\n onSelect,\n className\n}: AnnotationRevisionListProps) {\n return (\n <Surface\n className={clsx(\"eth-annotation-revision-list\", className)}\n title=\"Revision history\"\n data-eth-component=\"AnnotationRevisionList\"\n >\n <div className=\"eth-annotation-revision-list__items\">\n {revisions.map((revision) => (\n <article\n key={revision.id}\n className={clsx(\n \"eth-annotation-revision-list__item\",\n revision.id === selectedId && \"eth-annotation-revision-list__item--selected\"\n )}\n >\n <div>\n <strong>{revision.submittedBy}</strong>\n <p>{revision.submittedAt}</p>\n {revision.comment ? <p>{revision.comment}</p> : null}\n </div>\n <StatusDot status={revision.status} label={revision.status} />\n {onSelect ? (\n <Button intent=\"secondary\" density=\"compact\" onClick={() => onSelect(revision.id)}>\n Select\n </Button>\n ) : null}\n </article>\n ))}\n </div>\n </Surface>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,kBAAiB;AACjB,kBAQO;AAkDD;AAnBC,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW,YAAAA,SAAK,yBAAyB,SAAS;AAAA,MAClD,OAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA,sBAAmB;AAAA,MAEnB;AAAA,oDAAC,OAAE,WAAU,mCAAmC,oBAAS;AAAA,QACxD,aACC,6CAAC,kCAAmB,UAAS,QAAO,OAAO,mBAAmB,WAAW,MAAM,IAC5E;AAAA,qBAAW,eAAe,SAAY,GAAG,KAAK,MAAM,WAAW,aAAa,GAAG,CAAC,iBAAiB;AAAA,UACjG,qBACC,4CAAC,sBAAO,QAAO,aAAY,SAAQ,WAAU,SAAS,oBAAoB,+BAE1E,IACE;AAAA,WACN,IACE;AAAA,QACJ,4CAAC,SAAI,WAAU,iCACZ,iCAAuB,IAAI,CAAC,UAC3B,4CAAC,gBAA4B,OAAc,OAAO,OAAO,MAAM,EAAE,GAAG,YAAjD,MAAM,EAA+D,CACzF,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,MAAM,SAAS,SAAS;AAC1B,WACE,6CAAC,SAAI,WAAU,gCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM;AAAA,UACb,UAAU,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,QAAQ,OAAO,OAAO,EAAE;AAAA,UACjF,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AAAA,UACvC,UAAU,CAAC,cAAc,WAAW,MAAM,IAAI,SAAS;AAAA;AAAA,MACzD;AAAA,MACC,MAAM,aAAa,4CAAC,OAAE,WAAU,iCAAiC,gBAAM,YAAW,IAAO;AAAA,OAC5F;AAAA,EAEJ;AAEA,SACE,4CAAC,yBAAU,IAAI,cAAc,MAAM,EAAE,IAAI,OAAO,MAAM,OAAO,YAAY,MAAM,YAC5E,gBAAM,SAAS,WACd;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,MAC3C,SAAS;AAAA,QACP,EAAE,OAAO,IAAI,OAAO,YAAY;AAAA,QAChC,IAAI,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,QAAQ,OAAO,OAAO,EAAE;AAAA,MAC7E;AAAA,MACA,UAAU,CAAC,UAAU,WAAW,MAAM,IAAI,MAAM,cAAc,KAAK;AAAA;AAAA,EACrE,IAEA;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,MAC3C,UAAU,CAAC,UAAU,WAAW,MAAM,IAAI,MAAM,cAAc,KAAK;AAAA;AAAA,EACrE,GAEJ;AAEJ;;;ACzHA,IAAAC,eAAiB;AACjB,IAAAC,eAA+B;AA+BnB,IAAAC,sBAAA;AAfL,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW,aAAAC,SAAK,8BAA8B,SAAS;AAAA,MACvD,OAAM;AAAA,MACN,sBAAmB;AAAA,MAEnB,uDAAC,SAAI,WAAU,qCACb,wDAAC,WACC;AAAA,qDAAC,WACC,wDAAC,QACC;AAAA,uDAAC,QAAG,OAAM,OAAM,mBAAK;AAAA,UACrB,6CAAC,QAAG,OAAM,OAAM,oBAAM;AAAA,UACtB,6CAAC,QAAG,OAAM,OAAM,mBAAK;AAAA,UACrB,6CAAC,QAAG,OAAM,OAAM,oBAAM;AAAA,WACxB,GACF;AAAA,QACA,6CAAC,WACE,iBAAO,IAAI,CAAC,UAAU;AACrB,gBAAM,UAAU,YAAY,OAAO,MAAM,EAAE,CAAC,MAAM,YAAY,MAAM,MAAM,EAAE,CAAC;AAC7E,iBACE,8CAAC,QAAkB,WAAW,UAAU,6CAA6C,QACnF;AAAA,yDAAC,QAAG,OAAM,OAAO,gBAAM,OAAM;AAAA,YAC7B,6CAAC,QAAI,sBAAY,OAAO,MAAM,EAAE,CAAC,GAAE;AAAA,YACnC,6CAAC,QAAI,sBAAY,MAAM,MAAM,EAAE,CAAC,GAAE;AAAA,YAClC,6CAAC,QACC,uDAAC,sBAAM,UAAU,UAAU,YAAY,WAAY,oBAAU,YAAY,QAAO,GAClF;AAAA,eANO,MAAM,EAOf;AAAA,QAEJ,CAAC,GACH;AAAA,SACF,GACF;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,YAAY,OAA2B;AAC9C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,IAAI;AAChD,SAAO,SAAS;AAClB;;;AC/DA,IAAAC,eAAiB;AACjB,IAAAC,eAAqC;AAwB7B,IAAAC,sBAAA;AAZD,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,6CAAC,SAAI,eAAW,aAAAC,SAAK,iCAAiC,SAAS,GAAG,sBAAmB,2BACnF,uDAAC,0BAAU,IAAQ,OAAc,YAAY,cAAc,0BAA0B,OACnF;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV,gBAAe;AAAA,MACf,YAAY;AAAA,MACZ,UAAU,CAAC,UAAU,WAAW,MAAM,cAAc,KAAK;AAAA;AAAA,EAC3D,GACF,GACF;AAEJ;;;ACnCA,IAAAC,eAAiB;AACjB,IAAAC,eAA2C;AAuC/B,IAAAC,sBAAA;AArBL,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAC9B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW,aAAAC,SAAK,gCAAgC,SAAS;AAAA,MACzD,OAAM;AAAA,MACN,sBAAmB;AAAA,MAEnB,uDAAC,SAAI,WAAU,uCACZ,oBAAU,IAAI,CAAC,aACd;AAAA,QAAC;AAAA;AAAA,UAEC,eAAW,aAAAA;AAAA,YACT;AAAA,YACA,SAAS,OAAO,cAAc;AAAA,UAChC;AAAA,UAEA;AAAA,0DAAC,SACC;AAAA,2DAAC,YAAQ,mBAAS,aAAY;AAAA,cAC9B,6CAAC,OAAG,mBAAS,aAAY;AAAA,cACxB,SAAS,UAAU,6CAAC,OAAG,mBAAS,SAAQ,IAAO;AAAA,eAClD;AAAA,YACA,6CAAC,0BAAU,QAAQ,SAAS,QAAQ,OAAO,SAAS,QAAQ;AAAA,YAC3D,WACC,6CAAC,uBAAO,QAAO,aAAY,SAAQ,WAAU,SAAS,MAAM,SAAS,SAAS,EAAE,GAAG,oBAEnF,IACE;AAAA;AAAA;AAAA,QAhBC,SAAS;AAAA,MAiBhB,CACD,GACH;AAAA;AAAA,EACF;AAEJ;;;AJpDO,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":["clsx","import_clsx","import_core","import_jsx_runtime","clsx","import_clsx","import_core","import_jsx_runtime","clsx","import_clsx","import_core","import_jsx_runtime","clsx"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./components/AnnotationEditor";
|
|
2
|
+
export * from "./components/AnnotationDiffViewer";
|
|
3
|
+
export * from "./components/PhoneticAnnotationField";
|
|
4
|
+
export * from "./components/AnnotationRevisionList";
|
|
5
|
+
export declare const AnnotationComponentNames: readonly ["AnnotationEditor", "AnnotationDiffViewer", "PhoneticAnnotationField", "AnnotationRevisionList"];
|
|
6
|
+
export type AnnotationComponentName = (typeof AnnotationComponentNames)[number];
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// src/components/AnnotationEditor.tsx
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
FormField,
|
|
6
|
+
InlineNotification,
|
|
7
|
+
MultiSelect,
|
|
8
|
+
Select,
|
|
9
|
+
Surface,
|
|
10
|
+
TextInput
|
|
11
|
+
} from "@echothink-ui/core";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
function AnnotationEditor({
|
|
14
|
+
sentence,
|
|
15
|
+
language,
|
|
16
|
+
annotationSchemaFields,
|
|
17
|
+
values,
|
|
18
|
+
onChange,
|
|
19
|
+
suggestion,
|
|
20
|
+
onAcceptSuggestion,
|
|
21
|
+
actions,
|
|
22
|
+
className
|
|
23
|
+
}) {
|
|
24
|
+
return /* @__PURE__ */ jsxs(
|
|
25
|
+
Surface,
|
|
26
|
+
{
|
|
27
|
+
className: clsx("eth-annotation-editor", className),
|
|
28
|
+
title: "Annotation editor",
|
|
29
|
+
subtitle: language,
|
|
30
|
+
actions,
|
|
31
|
+
"data-eth-component": "AnnotationEditor",
|
|
32
|
+
children: [
|
|
33
|
+
/* @__PURE__ */ jsx("p", { className: "eth-annotation-editor__sentence", children: sentence }),
|
|
34
|
+
suggestion ? /* @__PURE__ */ jsxs(InlineNotification, { severity: "info", title: `Suggestion from ${suggestion.source}`, children: [
|
|
35
|
+
suggestion.confidence !== void 0 ? `${Math.round(suggestion.confidence * 100)}% confidence` : null,
|
|
36
|
+
onAcceptSuggestion ? /* @__PURE__ */ jsx(Button, { intent: "secondary", density: "compact", onClick: onAcceptSuggestion, children: "Accept suggestion" }) : null
|
|
37
|
+
] }) : null,
|
|
38
|
+
/* @__PURE__ */ jsx("div", { className: "eth-annotation-editor__fields", children: annotationSchemaFields.map((field) => /* @__PURE__ */ jsx(FieldControl, { field, value: values[field.id], onChange }, field.id)) })
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function FieldControl({
|
|
44
|
+
field,
|
|
45
|
+
value,
|
|
46
|
+
onChange
|
|
47
|
+
}) {
|
|
48
|
+
if (field.kind === "multi") {
|
|
49
|
+
return /* @__PURE__ */ jsxs("div", { className: "eth-annotation-editor__field", children: [
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
MultiSelect,
|
|
52
|
+
{
|
|
53
|
+
label: field.label,
|
|
54
|
+
options: (field.options ?? []).map((option) => ({ value: option, label: option })),
|
|
55
|
+
value: Array.isArray(value) ? value : [],
|
|
56
|
+
onChange: (nextValue) => onChange?.(field.id, nextValue)
|
|
57
|
+
}
|
|
58
|
+
),
|
|
59
|
+
field.helperText ? /* @__PURE__ */ jsx("p", { className: "eth-annotation-editor__helper", children: field.helperText }) : null
|
|
60
|
+
] });
|
|
61
|
+
}
|
|
62
|
+
return /* @__PURE__ */ jsx(FormField, { id: `annotation-${field.id}`, label: field.label, helperText: field.helperText, children: field.kind === "choice" ? /* @__PURE__ */ jsx(
|
|
63
|
+
Select,
|
|
64
|
+
{
|
|
65
|
+
value: typeof value === "string" ? value : "",
|
|
66
|
+
options: [
|
|
67
|
+
{ value: "", label: "Select..." },
|
|
68
|
+
...(field.options ?? []).map((option) => ({ value: option, label: option }))
|
|
69
|
+
],
|
|
70
|
+
onChange: (event) => onChange?.(field.id, event.currentTarget.value)
|
|
71
|
+
}
|
|
72
|
+
) : /* @__PURE__ */ jsx(
|
|
73
|
+
TextInput,
|
|
74
|
+
{
|
|
75
|
+
value: typeof value === "string" ? value : "",
|
|
76
|
+
onChange: (event) => onChange?.(field.id, event.currentTarget.value)
|
|
77
|
+
}
|
|
78
|
+
) });
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// src/components/AnnotationDiffViewer.tsx
|
|
82
|
+
import clsx2 from "clsx";
|
|
83
|
+
import { Badge, Surface as Surface2 } from "@echothink-ui/core";
|
|
84
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
85
|
+
function AnnotationDiffViewer({
|
|
86
|
+
before,
|
|
87
|
+
after,
|
|
88
|
+
fields,
|
|
89
|
+
className
|
|
90
|
+
}) {
|
|
91
|
+
return /* @__PURE__ */ jsx2(
|
|
92
|
+
Surface2,
|
|
93
|
+
{
|
|
94
|
+
className: clsx2("eth-annotation-diff-viewer", className),
|
|
95
|
+
title: "Annotation diff",
|
|
96
|
+
"data-eth-component": "AnnotationDiffViewer",
|
|
97
|
+
children: /* @__PURE__ */ jsx2("div", { className: "eth-annotation-diff-viewer__table", children: /* @__PURE__ */ jsxs2("table", { children: [
|
|
98
|
+
/* @__PURE__ */ jsx2("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
|
|
99
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", children: "Field" }),
|
|
100
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", children: "Before" }),
|
|
101
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", children: "After" }),
|
|
102
|
+
/* @__PURE__ */ jsx2("th", { scope: "col", children: "Change" })
|
|
103
|
+
] }) }),
|
|
104
|
+
/* @__PURE__ */ jsx2("tbody", { children: fields.map((field) => {
|
|
105
|
+
const changed = formatValue(before[field.id]) !== formatValue(after[field.id]);
|
|
106
|
+
return /* @__PURE__ */ jsxs2("tr", { className: changed ? "eth-annotation-diff-viewer__row--changed" : void 0, children: [
|
|
107
|
+
/* @__PURE__ */ jsx2("th", { scope: "row", children: field.label }),
|
|
108
|
+
/* @__PURE__ */ jsx2("td", { children: formatValue(before[field.id]) }),
|
|
109
|
+
/* @__PURE__ */ jsx2("td", { children: formatValue(after[field.id]) }),
|
|
110
|
+
/* @__PURE__ */ jsx2("td", { children: /* @__PURE__ */ jsx2(Badge, { severity: changed ? "warning" : "success", children: changed ? "Changed" : "Same" }) })
|
|
111
|
+
] }, field.id);
|
|
112
|
+
}) })
|
|
113
|
+
] }) })
|
|
114
|
+
}
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
function formatValue(value) {
|
|
118
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
119
|
+
return value ?? "-";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/components/PhoneticAnnotationField.tsx
|
|
123
|
+
import clsx3 from "clsx";
|
|
124
|
+
import { FormField as FormField2, TextInput as TextInput2 } from "@echothink-ui/core";
|
|
125
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
126
|
+
function PhoneticAnnotationField({
|
|
127
|
+
id,
|
|
128
|
+
label,
|
|
129
|
+
value,
|
|
130
|
+
onChange,
|
|
131
|
+
helperText,
|
|
132
|
+
error,
|
|
133
|
+
className
|
|
134
|
+
}) {
|
|
135
|
+
return /* @__PURE__ */ jsx3("div", { className: clsx3("eth-annotation-phonetic-field", className), "data-eth-component": "PhoneticAnnotationField", children: /* @__PURE__ */ jsx3(FormField2, { id, label, helperText: helperText ?? "IPA notation supported", error, children: /* @__PURE__ */ jsx3(
|
|
136
|
+
TextInput2,
|
|
137
|
+
{
|
|
138
|
+
value,
|
|
139
|
+
inputMode: "text",
|
|
140
|
+
autoCapitalize: "off",
|
|
141
|
+
spellCheck: false,
|
|
142
|
+
onChange: (event) => onChange?.(event.currentTarget.value)
|
|
143
|
+
}
|
|
144
|
+
) }) });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/components/AnnotationRevisionList.tsx
|
|
148
|
+
import clsx4 from "clsx";
|
|
149
|
+
import { Button as Button2, StatusDot, Surface as Surface3 } from "@echothink-ui/core";
|
|
150
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
151
|
+
function AnnotationRevisionList({
|
|
152
|
+
revisions,
|
|
153
|
+
selectedId,
|
|
154
|
+
onSelect,
|
|
155
|
+
className
|
|
156
|
+
}) {
|
|
157
|
+
return /* @__PURE__ */ jsx4(
|
|
158
|
+
Surface3,
|
|
159
|
+
{
|
|
160
|
+
className: clsx4("eth-annotation-revision-list", className),
|
|
161
|
+
title: "Revision history",
|
|
162
|
+
"data-eth-component": "AnnotationRevisionList",
|
|
163
|
+
children: /* @__PURE__ */ jsx4("div", { className: "eth-annotation-revision-list__items", children: revisions.map((revision) => /* @__PURE__ */ jsxs3(
|
|
164
|
+
"article",
|
|
165
|
+
{
|
|
166
|
+
className: clsx4(
|
|
167
|
+
"eth-annotation-revision-list__item",
|
|
168
|
+
revision.id === selectedId && "eth-annotation-revision-list__item--selected"
|
|
169
|
+
),
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ jsxs3("div", { children: [
|
|
172
|
+
/* @__PURE__ */ jsx4("strong", { children: revision.submittedBy }),
|
|
173
|
+
/* @__PURE__ */ jsx4("p", { children: revision.submittedAt }),
|
|
174
|
+
revision.comment ? /* @__PURE__ */ jsx4("p", { children: revision.comment }) : null
|
|
175
|
+
] }),
|
|
176
|
+
/* @__PURE__ */ jsx4(StatusDot, { status: revision.status, label: revision.status }),
|
|
177
|
+
onSelect ? /* @__PURE__ */ jsx4(Button2, { intent: "secondary", density: "compact", onClick: () => onSelect(revision.id), children: "Select" }) : null
|
|
178
|
+
]
|
|
179
|
+
},
|
|
180
|
+
revision.id
|
|
181
|
+
)) })
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/index.tsx
|
|
187
|
+
var AnnotationComponentNames = [
|
|
188
|
+
"AnnotationEditor",
|
|
189
|
+
"AnnotationDiffViewer",
|
|
190
|
+
"PhoneticAnnotationField",
|
|
191
|
+
"AnnotationRevisionList"
|
|
192
|
+
];
|
|
193
|
+
export {
|
|
194
|
+
AnnotationComponentNames,
|
|
195
|
+
AnnotationDiffViewer,
|
|
196
|
+
AnnotationEditor,
|
|
197
|
+
AnnotationRevisionList,
|
|
198
|
+
PhoneticAnnotationField
|
|
199
|
+
};
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/AnnotationEditor.tsx","../src/components/AnnotationDiffViewer.tsx","../src/components/PhoneticAnnotationField.tsx","../src/components/AnnotationRevisionList.tsx","../src/index.tsx"],"sourcesContent":["import * as React from \"react\";\nimport clsx from \"clsx\";\nimport {\n Button,\n FormField,\n InlineNotification,\n MultiSelect,\n Select,\n Surface,\n TextInput\n} from \"@echothink-ui/core\";\nimport type { EthAction } from \"@echothink-ui/core\";\n\nexport interface AnnotationSchemaField {\n id: string;\n label: string;\n kind: \"text\" | \"choice\" | \"multi\";\n options?: string[];\n helperText?: string;\n}\n\nexport type AnnotationValues = Record<string, string | string[]>;\n\nexport interface AnnotationSuggestion {\n source: string;\n values: AnnotationValues;\n confidence?: number;\n}\n\nexport interface AnnotationEditorProps {\n sentence: string;\n language: string;\n annotationSchemaFields: AnnotationSchemaField[];\n values: AnnotationValues;\n onChange?: (id: string, value: string | string[]) => void;\n suggestion?: AnnotationSuggestion;\n onAcceptSuggestion?: () => void;\n actions?: EthAction[];\n className?: string;\n}\n\nexport function AnnotationEditor({\n sentence,\n language,\n annotationSchemaFields,\n values,\n onChange,\n suggestion,\n onAcceptSuggestion,\n actions,\n className\n}: AnnotationEditorProps) {\n return (\n <Surface\n className={clsx(\"eth-annotation-editor\", className)}\n title=\"Annotation editor\"\n subtitle={language}\n actions={actions}\n data-eth-component=\"AnnotationEditor\"\n >\n <p className=\"eth-annotation-editor__sentence\">{sentence}</p>\n {suggestion ? (\n <InlineNotification severity=\"info\" title={`Suggestion from ${suggestion.source}`}>\n {suggestion.confidence !== undefined ? `${Math.round(suggestion.confidence * 100)}% confidence` : null}\n {onAcceptSuggestion ? (\n <Button intent=\"secondary\" density=\"compact\" onClick={onAcceptSuggestion}>\n Accept suggestion\n </Button>\n ) : null}\n </InlineNotification>\n ) : null}\n <div className=\"eth-annotation-editor__fields\">\n {annotationSchemaFields.map((field) => (\n <FieldControl key={field.id} field={field} value={values[field.id]} onChange={onChange} />\n ))}\n </div>\n </Surface>\n );\n}\n\nfunction FieldControl({\n field,\n value,\n onChange\n}: {\n field: AnnotationSchemaField;\n value?: string | string[];\n onChange?: (id: string, value: string | string[]) => void;\n}) {\n if (field.kind === \"multi\") {\n return (\n <div className=\"eth-annotation-editor__field\">\n <MultiSelect\n label={field.label}\n options={(field.options ?? []).map((option) => ({ value: option, label: option }))}\n value={Array.isArray(value) ? value : []}\n onChange={(nextValue) => onChange?.(field.id, nextValue)}\n />\n {field.helperText ? <p className=\"eth-annotation-editor__helper\">{field.helperText}</p> : null}\n </div>\n );\n }\n\n return (\n <FormField id={`annotation-${field.id}`} label={field.label} helperText={field.helperText}>\n {field.kind === \"choice\" ? (\n <Select\n value={typeof value === \"string\" ? value : \"\"}\n options={[\n { value: \"\", label: \"Select...\" },\n ...(field.options ?? []).map((option) => ({ value: option, label: option }))\n ]}\n onChange={(event) => onChange?.(field.id, event.currentTarget.value)}\n />\n ) : (\n <TextInput\n value={typeof value === \"string\" ? value : \"\"}\n onChange={(event) => onChange?.(field.id, event.currentTarget.value)}\n />\n )}\n </FormField>\n );\n}\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport { Badge, Surface } from \"@echothink-ui/core\";\n\nexport type AnnotationDiffValues = Record<string, string | string[]>;\n\nexport interface AnnotationDiffField {\n id: string;\n label: string;\n}\n\nexport interface AnnotationDiffViewerProps {\n before: AnnotationDiffValues;\n after: AnnotationDiffValues;\n fields: AnnotationDiffField[];\n className?: string;\n}\n\nexport function AnnotationDiffViewer({\n before,\n after,\n fields,\n className\n}: AnnotationDiffViewerProps) {\n return (\n <Surface\n className={clsx(\"eth-annotation-diff-viewer\", className)}\n title=\"Annotation diff\"\n data-eth-component=\"AnnotationDiffViewer\"\n >\n <div className=\"eth-annotation-diff-viewer__table\">\n <table>\n <thead>\n <tr>\n <th scope=\"col\">Field</th>\n <th scope=\"col\">Before</th>\n <th scope=\"col\">After</th>\n <th scope=\"col\">Change</th>\n </tr>\n </thead>\n <tbody>\n {fields.map((field) => {\n const changed = formatValue(before[field.id]) !== formatValue(after[field.id]);\n return (\n <tr key={field.id} className={changed ? \"eth-annotation-diff-viewer__row--changed\" : undefined}>\n <th scope=\"row\">{field.label}</th>\n <td>{formatValue(before[field.id])}</td>\n <td>{formatValue(after[field.id])}</td>\n <td>\n <Badge severity={changed ? \"warning\" : \"success\"}>{changed ? \"Changed\" : \"Same\"}</Badge>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </Surface>\n );\n}\n\nfunction formatValue(value?: string | string[]) {\n if (Array.isArray(value)) return value.join(\", \");\n return value ?? \"-\";\n}\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport { FormField, TextInput } from \"@echothink-ui/core\";\n\nexport interface PhoneticAnnotationFieldProps {\n id: string;\n label: string;\n value: string;\n onChange?: (value: string) => void;\n helperText?: string;\n error?: string;\n className?: string;\n}\n\nexport function PhoneticAnnotationField({\n id,\n label,\n value,\n onChange,\n helperText,\n error,\n className\n}: PhoneticAnnotationFieldProps) {\n return (\n <div className={clsx(\"eth-annotation-phonetic-field\", className)} data-eth-component=\"PhoneticAnnotationField\">\n <FormField id={id} label={label} helperText={helperText ?? \"IPA notation supported\"} error={error}>\n <TextInput\n value={value}\n inputMode=\"text\"\n autoCapitalize=\"off\"\n spellCheck={false}\n onChange={(event) => onChange?.(event.currentTarget.value)}\n />\n </FormField>\n </div>\n );\n}\n","import * as React from \"react\";\nimport clsx from \"clsx\";\nimport { Button, StatusDot, Surface } from \"@echothink-ui/core\";\nimport type { EthOperationalStatus } from \"@echothink-ui/core\";\n\nexport interface AnnotationRevision {\n id: string;\n submittedBy: string;\n submittedAt: string;\n status: EthOperationalStatus;\n comment?: string;\n}\n\nexport interface AnnotationRevisionListProps {\n revisions: AnnotationRevision[];\n selectedId?: string;\n onSelect?: (id: string) => void;\n className?: string;\n}\n\nexport function AnnotationRevisionList({\n revisions,\n selectedId,\n onSelect,\n className\n}: AnnotationRevisionListProps) {\n return (\n <Surface\n className={clsx(\"eth-annotation-revision-list\", className)}\n title=\"Revision history\"\n data-eth-component=\"AnnotationRevisionList\"\n >\n <div className=\"eth-annotation-revision-list__items\">\n {revisions.map((revision) => (\n <article\n key={revision.id}\n className={clsx(\n \"eth-annotation-revision-list__item\",\n revision.id === selectedId && \"eth-annotation-revision-list__item--selected\"\n )}\n >\n <div>\n <strong>{revision.submittedBy}</strong>\n <p>{revision.submittedAt}</p>\n {revision.comment ? <p>{revision.comment}</p> : null}\n </div>\n <StatusDot status={revision.status} label={revision.status} />\n {onSelect ? (\n <Button intent=\"secondary\" density=\"compact\" onClick={() => onSelect(revision.id)}>\n Select\n </Button>\n ) : null}\n </article>\n ))}\n </div>\n </Surface>\n );\n}\n","export * from \"./components/AnnotationEditor\";\nexport * from \"./components/AnnotationDiffViewer\";\nexport * from \"./components/PhoneticAnnotationField\";\nexport * from \"./components/AnnotationRevisionList\";\n\nexport const AnnotationComponentNames = [\n \"AnnotationEditor\",\n \"AnnotationDiffViewer\",\n \"PhoneticAnnotationField\",\n \"AnnotationRevisionList\"\n] as const;\nexport type AnnotationComponentName = (typeof AnnotationComponentNames)[number];\n"],"mappings":";AACA,OAAO,UAAU;AACjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAkDD,cAEE,YAFF;AAnBC,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW,KAAK,yBAAyB,SAAS;AAAA,MAClD,OAAM;AAAA,MACN,UAAU;AAAA,MACV;AAAA,MACA,sBAAmB;AAAA,MAEnB;AAAA,4BAAC,OAAE,WAAU,mCAAmC,oBAAS;AAAA,QACxD,aACC,qBAAC,sBAAmB,UAAS,QAAO,OAAO,mBAAmB,WAAW,MAAM,IAC5E;AAAA,qBAAW,eAAe,SAAY,GAAG,KAAK,MAAM,WAAW,aAAa,GAAG,CAAC,iBAAiB;AAAA,UACjG,qBACC,oBAAC,UAAO,QAAO,aAAY,SAAQ,WAAU,SAAS,oBAAoB,+BAE1E,IACE;AAAA,WACN,IACE;AAAA,QACJ,oBAAC,SAAI,WAAU,iCACZ,iCAAuB,IAAI,CAAC,UAC3B,oBAAC,gBAA4B,OAAc,OAAO,OAAO,MAAM,EAAE,GAAG,YAAjD,MAAM,EAA+D,CACzF,GACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,MAAM,SAAS,SAAS;AAC1B,WACE,qBAAC,SAAI,WAAU,gCACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM;AAAA,UACb,UAAU,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,QAAQ,OAAO,OAAO,EAAE;AAAA,UACjF,OAAO,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;AAAA,UACvC,UAAU,CAAC,cAAc,WAAW,MAAM,IAAI,SAAS;AAAA;AAAA,MACzD;AAAA,MACC,MAAM,aAAa,oBAAC,OAAE,WAAU,iCAAiC,gBAAM,YAAW,IAAO;AAAA,OAC5F;AAAA,EAEJ;AAEA,SACE,oBAAC,aAAU,IAAI,cAAc,MAAM,EAAE,IAAI,OAAO,MAAM,OAAO,YAAY,MAAM,YAC5E,gBAAM,SAAS,WACd;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,MAC3C,SAAS;AAAA,QACP,EAAE,OAAO,IAAI,OAAO,YAAY;AAAA,QAChC,IAAI,MAAM,WAAW,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,OAAO,QAAQ,OAAO,OAAO,EAAE;AAAA,MAC7E;AAAA,MACA,UAAU,CAAC,UAAU,WAAW,MAAM,IAAI,MAAM,cAAc,KAAK;AAAA;AAAA,EACrE,IAEA;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,OAAO,UAAU,WAAW,QAAQ;AAAA,MAC3C,UAAU,CAAC,UAAU,WAAW,MAAM,IAAI,MAAM,cAAc,KAAK;AAAA;AAAA,EACrE,GAEJ;AAEJ;;;ACzHA,OAAOA,WAAU;AACjB,SAAS,OAAO,WAAAC,gBAAe;AA+BnB,SACE,OAAAC,MADF,QAAAC,aAAA;AAfL,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,SACE,gBAAAD;AAAA,IAACD;AAAA,IAAA;AAAA,MACC,WAAWD,MAAK,8BAA8B,SAAS;AAAA,MACvD,OAAM;AAAA,MACN,sBAAmB;AAAA,MAEnB,0BAAAE,KAAC,SAAI,WAAU,qCACb,0BAAAC,MAAC,WACC;AAAA,wBAAAD,KAAC,WACC,0BAAAC,MAAC,QACC;AAAA,0BAAAD,KAAC,QAAG,OAAM,OAAM,mBAAK;AAAA,UACrB,gBAAAA,KAAC,QAAG,OAAM,OAAM,oBAAM;AAAA,UACtB,gBAAAA,KAAC,QAAG,OAAM,OAAM,mBAAK;AAAA,UACrB,gBAAAA,KAAC,QAAG,OAAM,OAAM,oBAAM;AAAA,WACxB,GACF;AAAA,QACA,gBAAAA,KAAC,WACE,iBAAO,IAAI,CAAC,UAAU;AACrB,gBAAM,UAAU,YAAY,OAAO,MAAM,EAAE,CAAC,MAAM,YAAY,MAAM,MAAM,EAAE,CAAC;AAC7E,iBACE,gBAAAC,MAAC,QAAkB,WAAW,UAAU,6CAA6C,QACnF;AAAA,4BAAAD,KAAC,QAAG,OAAM,OAAO,gBAAM,OAAM;AAAA,YAC7B,gBAAAA,KAAC,QAAI,sBAAY,OAAO,MAAM,EAAE,CAAC,GAAE;AAAA,YACnC,gBAAAA,KAAC,QAAI,sBAAY,MAAM,MAAM,EAAE,CAAC,GAAE;AAAA,YAClC,gBAAAA,KAAC,QACC,0BAAAA,KAAC,SAAM,UAAU,UAAU,YAAY,WAAY,oBAAU,YAAY,QAAO,GAClF;AAAA,eANO,MAAM,EAOf;AAAA,QAEJ,CAAC,GACH;AAAA,SACF,GACF;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,YAAY,OAA2B;AAC9C,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,KAAK,IAAI;AAChD,SAAO,SAAS;AAClB;;;AC/DA,OAAOE,WAAU;AACjB,SAAS,aAAAC,YAAW,aAAAC,kBAAiB;AAwB7B,gBAAAC,YAAA;AAZD,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,gBAAAA,KAAC,SAAI,WAAWH,MAAK,iCAAiC,SAAS,GAAG,sBAAmB,2BACnF,0BAAAG,KAACF,YAAA,EAAU,IAAQ,OAAc,YAAY,cAAc,0BAA0B,OACnF,0BAAAE;AAAA,IAACD;AAAA,IAAA;AAAA,MACC;AAAA,MACA,WAAU;AAAA,MACV,gBAAe;AAAA,MACf,YAAY;AAAA,MACZ,UAAU,CAAC,UAAU,WAAW,MAAM,cAAc,KAAK;AAAA;AAAA,EAC3D,GACF,GACF;AAEJ;;;ACnCA,OAAOE,WAAU;AACjB,SAAS,UAAAC,SAAQ,WAAW,WAAAC,gBAAe;AAuC/B,SACE,OAAAC,MADF,QAAAC,aAAA;AArBL,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgC;AAC9B,SACE,gBAAAD;AAAA,IAACD;AAAA,IAAA;AAAA,MACC,WAAWF,MAAK,gCAAgC,SAAS;AAAA,MACzD,OAAM;AAAA,MACN,sBAAmB;AAAA,MAEnB,0BAAAG,KAAC,SAAI,WAAU,uCACZ,oBAAU,IAAI,CAAC,aACd,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,WAAWJ;AAAA,YACT;AAAA,YACA,SAAS,OAAO,cAAc;AAAA,UAChC;AAAA,UAEA;AAAA,4BAAAI,MAAC,SACC;AAAA,8BAAAD,KAAC,YAAQ,mBAAS,aAAY;AAAA,cAC9B,gBAAAA,KAAC,OAAG,mBAAS,aAAY;AAAA,cACxB,SAAS,UAAU,gBAAAA,KAAC,OAAG,mBAAS,SAAQ,IAAO;AAAA,eAClD;AAAA,YACA,gBAAAA,KAAC,aAAU,QAAQ,SAAS,QAAQ,OAAO,SAAS,QAAQ;AAAA,YAC3D,WACC,gBAAAA,KAACF,SAAA,EAAO,QAAO,aAAY,SAAQ,WAAU,SAAS,MAAM,SAAS,SAAS,EAAE,GAAG,oBAEnF,IACE;AAAA;AAAA;AAAA,QAhBC,SAAS;AAAA,MAiBhB,CACD,GACH;AAAA;AAAA,EACF;AAEJ;;;ACpDO,IAAM,2BAA2B;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":["clsx","Surface","jsx","jsxs","clsx","FormField","TextInput","jsx","clsx","Button","Surface","jsx","jsxs"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@echothink-ui/annotation",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.cjs",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"src",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": ">=18.3.0",
|
|
24
|
+
"react-dom": ">=18.3.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"clsx": "^2.1.1",
|
|
28
|
+
"@echothink-ui/core": "0.2.0"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsup src/index.tsx --format esm,cjs --sourcemap --clean --external react --external react-dom && tsc -p tsconfig.json --declaration --emitDeclarationOnly --noEmit false --outDir dist",
|
|
35
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
36
|
+
"test": "vitest run --config ../../vitest.config.ts --passWithNoTests",
|
|
37
|
+
"lint": "eslint src"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { Badge, Surface } from "@echothink-ui/core";
|
|
4
|
+
|
|
5
|
+
export type AnnotationDiffValues = Record<string, string | string[]>;
|
|
6
|
+
|
|
7
|
+
export interface AnnotationDiffField {
|
|
8
|
+
id: string;
|
|
9
|
+
label: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AnnotationDiffViewerProps {
|
|
13
|
+
before: AnnotationDiffValues;
|
|
14
|
+
after: AnnotationDiffValues;
|
|
15
|
+
fields: AnnotationDiffField[];
|
|
16
|
+
className?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function AnnotationDiffViewer({
|
|
20
|
+
before,
|
|
21
|
+
after,
|
|
22
|
+
fields,
|
|
23
|
+
className
|
|
24
|
+
}: AnnotationDiffViewerProps) {
|
|
25
|
+
return (
|
|
26
|
+
<Surface
|
|
27
|
+
className={clsx("eth-annotation-diff-viewer", className)}
|
|
28
|
+
title="Annotation diff"
|
|
29
|
+
data-eth-component="AnnotationDiffViewer"
|
|
30
|
+
>
|
|
31
|
+
<div className="eth-annotation-diff-viewer__table">
|
|
32
|
+
<table>
|
|
33
|
+
<thead>
|
|
34
|
+
<tr>
|
|
35
|
+
<th scope="col">Field</th>
|
|
36
|
+
<th scope="col">Before</th>
|
|
37
|
+
<th scope="col">After</th>
|
|
38
|
+
<th scope="col">Change</th>
|
|
39
|
+
</tr>
|
|
40
|
+
</thead>
|
|
41
|
+
<tbody>
|
|
42
|
+
{fields.map((field) => {
|
|
43
|
+
const changed = formatValue(before[field.id]) !== formatValue(after[field.id]);
|
|
44
|
+
return (
|
|
45
|
+
<tr key={field.id} className={changed ? "eth-annotation-diff-viewer__row--changed" : undefined}>
|
|
46
|
+
<th scope="row">{field.label}</th>
|
|
47
|
+
<td>{formatValue(before[field.id])}</td>
|
|
48
|
+
<td>{formatValue(after[field.id])}</td>
|
|
49
|
+
<td>
|
|
50
|
+
<Badge severity={changed ? "warning" : "success"}>{changed ? "Changed" : "Same"}</Badge>
|
|
51
|
+
</td>
|
|
52
|
+
</tr>
|
|
53
|
+
);
|
|
54
|
+
})}
|
|
55
|
+
</tbody>
|
|
56
|
+
</table>
|
|
57
|
+
</div>
|
|
58
|
+
</Surface>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function formatValue(value?: string | string[]) {
|
|
63
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
64
|
+
return value ?? "-";
|
|
65
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
FormField,
|
|
6
|
+
InlineNotification,
|
|
7
|
+
MultiSelect,
|
|
8
|
+
Select,
|
|
9
|
+
Surface,
|
|
10
|
+
TextInput
|
|
11
|
+
} from "@echothink-ui/core";
|
|
12
|
+
import type { EthAction } from "@echothink-ui/core";
|
|
13
|
+
|
|
14
|
+
export interface AnnotationSchemaField {
|
|
15
|
+
id: string;
|
|
16
|
+
label: string;
|
|
17
|
+
kind: "text" | "choice" | "multi";
|
|
18
|
+
options?: string[];
|
|
19
|
+
helperText?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type AnnotationValues = Record<string, string | string[]>;
|
|
23
|
+
|
|
24
|
+
export interface AnnotationSuggestion {
|
|
25
|
+
source: string;
|
|
26
|
+
values: AnnotationValues;
|
|
27
|
+
confidence?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface AnnotationEditorProps {
|
|
31
|
+
sentence: string;
|
|
32
|
+
language: string;
|
|
33
|
+
annotationSchemaFields: AnnotationSchemaField[];
|
|
34
|
+
values: AnnotationValues;
|
|
35
|
+
onChange?: (id: string, value: string | string[]) => void;
|
|
36
|
+
suggestion?: AnnotationSuggestion;
|
|
37
|
+
onAcceptSuggestion?: () => void;
|
|
38
|
+
actions?: EthAction[];
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function AnnotationEditor({
|
|
43
|
+
sentence,
|
|
44
|
+
language,
|
|
45
|
+
annotationSchemaFields,
|
|
46
|
+
values,
|
|
47
|
+
onChange,
|
|
48
|
+
suggestion,
|
|
49
|
+
onAcceptSuggestion,
|
|
50
|
+
actions,
|
|
51
|
+
className
|
|
52
|
+
}: AnnotationEditorProps) {
|
|
53
|
+
return (
|
|
54
|
+
<Surface
|
|
55
|
+
className={clsx("eth-annotation-editor", className)}
|
|
56
|
+
title="Annotation editor"
|
|
57
|
+
subtitle={language}
|
|
58
|
+
actions={actions}
|
|
59
|
+
data-eth-component="AnnotationEditor"
|
|
60
|
+
>
|
|
61
|
+
<p className="eth-annotation-editor__sentence">{sentence}</p>
|
|
62
|
+
{suggestion ? (
|
|
63
|
+
<InlineNotification severity="info" title={`Suggestion from ${suggestion.source}`}>
|
|
64
|
+
{suggestion.confidence !== undefined ? `${Math.round(suggestion.confidence * 100)}% confidence` : null}
|
|
65
|
+
{onAcceptSuggestion ? (
|
|
66
|
+
<Button intent="secondary" density="compact" onClick={onAcceptSuggestion}>
|
|
67
|
+
Accept suggestion
|
|
68
|
+
</Button>
|
|
69
|
+
) : null}
|
|
70
|
+
</InlineNotification>
|
|
71
|
+
) : null}
|
|
72
|
+
<div className="eth-annotation-editor__fields">
|
|
73
|
+
{annotationSchemaFields.map((field) => (
|
|
74
|
+
<FieldControl key={field.id} field={field} value={values[field.id]} onChange={onChange} />
|
|
75
|
+
))}
|
|
76
|
+
</div>
|
|
77
|
+
</Surface>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function FieldControl({
|
|
82
|
+
field,
|
|
83
|
+
value,
|
|
84
|
+
onChange
|
|
85
|
+
}: {
|
|
86
|
+
field: AnnotationSchemaField;
|
|
87
|
+
value?: string | string[];
|
|
88
|
+
onChange?: (id: string, value: string | string[]) => void;
|
|
89
|
+
}) {
|
|
90
|
+
if (field.kind === "multi") {
|
|
91
|
+
return (
|
|
92
|
+
<div className="eth-annotation-editor__field">
|
|
93
|
+
<MultiSelect
|
|
94
|
+
label={field.label}
|
|
95
|
+
options={(field.options ?? []).map((option) => ({ value: option, label: option }))}
|
|
96
|
+
value={Array.isArray(value) ? value : []}
|
|
97
|
+
onChange={(nextValue) => onChange?.(field.id, nextValue)}
|
|
98
|
+
/>
|
|
99
|
+
{field.helperText ? <p className="eth-annotation-editor__helper">{field.helperText}</p> : null}
|
|
100
|
+
</div>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<FormField id={`annotation-${field.id}`} label={field.label} helperText={field.helperText}>
|
|
106
|
+
{field.kind === "choice" ? (
|
|
107
|
+
<Select
|
|
108
|
+
value={typeof value === "string" ? value : ""}
|
|
109
|
+
options={[
|
|
110
|
+
{ value: "", label: "Select..." },
|
|
111
|
+
...(field.options ?? []).map((option) => ({ value: option, label: option }))
|
|
112
|
+
]}
|
|
113
|
+
onChange={(event) => onChange?.(field.id, event.currentTarget.value)}
|
|
114
|
+
/>
|
|
115
|
+
) : (
|
|
116
|
+
<TextInput
|
|
117
|
+
value={typeof value === "string" ? value : ""}
|
|
118
|
+
onChange={(event) => onChange?.(field.id, event.currentTarget.value)}
|
|
119
|
+
/>
|
|
120
|
+
)}
|
|
121
|
+
</FormField>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { Button, StatusDot, Surface } from "@echothink-ui/core";
|
|
4
|
+
import type { EthOperationalStatus } from "@echothink-ui/core";
|
|
5
|
+
|
|
6
|
+
export interface AnnotationRevision {
|
|
7
|
+
id: string;
|
|
8
|
+
submittedBy: string;
|
|
9
|
+
submittedAt: string;
|
|
10
|
+
status: EthOperationalStatus;
|
|
11
|
+
comment?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface AnnotationRevisionListProps {
|
|
15
|
+
revisions: AnnotationRevision[];
|
|
16
|
+
selectedId?: string;
|
|
17
|
+
onSelect?: (id: string) => void;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function AnnotationRevisionList({
|
|
22
|
+
revisions,
|
|
23
|
+
selectedId,
|
|
24
|
+
onSelect,
|
|
25
|
+
className
|
|
26
|
+
}: AnnotationRevisionListProps) {
|
|
27
|
+
return (
|
|
28
|
+
<Surface
|
|
29
|
+
className={clsx("eth-annotation-revision-list", className)}
|
|
30
|
+
title="Revision history"
|
|
31
|
+
data-eth-component="AnnotationRevisionList"
|
|
32
|
+
>
|
|
33
|
+
<div className="eth-annotation-revision-list__items">
|
|
34
|
+
{revisions.map((revision) => (
|
|
35
|
+
<article
|
|
36
|
+
key={revision.id}
|
|
37
|
+
className={clsx(
|
|
38
|
+
"eth-annotation-revision-list__item",
|
|
39
|
+
revision.id === selectedId && "eth-annotation-revision-list__item--selected"
|
|
40
|
+
)}
|
|
41
|
+
>
|
|
42
|
+
<div>
|
|
43
|
+
<strong>{revision.submittedBy}</strong>
|
|
44
|
+
<p>{revision.submittedAt}</p>
|
|
45
|
+
{revision.comment ? <p>{revision.comment}</p> : null}
|
|
46
|
+
</div>
|
|
47
|
+
<StatusDot status={revision.status} label={revision.status} />
|
|
48
|
+
{onSelect ? (
|
|
49
|
+
<Button intent="secondary" density="compact" onClick={() => onSelect(revision.id)}>
|
|
50
|
+
Select
|
|
51
|
+
</Button>
|
|
52
|
+
) : null}
|
|
53
|
+
</article>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
</Surface>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { FormField, TextInput } from "@echothink-ui/core";
|
|
4
|
+
|
|
5
|
+
export interface PhoneticAnnotationFieldProps {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
value: string;
|
|
9
|
+
onChange?: (value: string) => void;
|
|
10
|
+
helperText?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function PhoneticAnnotationField({
|
|
16
|
+
id,
|
|
17
|
+
label,
|
|
18
|
+
value,
|
|
19
|
+
onChange,
|
|
20
|
+
helperText,
|
|
21
|
+
error,
|
|
22
|
+
className
|
|
23
|
+
}: PhoneticAnnotationFieldProps) {
|
|
24
|
+
return (
|
|
25
|
+
<div className={clsx("eth-annotation-phonetic-field", className)} data-eth-component="PhoneticAnnotationField">
|
|
26
|
+
<FormField id={id} label={label} helperText={helperText ?? "IPA notation supported"} error={error}>
|
|
27
|
+
<TextInput
|
|
28
|
+
value={value}
|
|
29
|
+
inputMode="text"
|
|
30
|
+
autoCapitalize="off"
|
|
31
|
+
spellCheck={false}
|
|
32
|
+
onChange={(event) => onChange?.(event.currentTarget.value)}
|
|
33
|
+
/>
|
|
34
|
+
</FormField>
|
|
35
|
+
</div>
|
|
36
|
+
);
|
|
37
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./components/AnnotationEditor";
|
|
2
|
+
export * from "./components/AnnotationDiffViewer";
|
|
3
|
+
export * from "./components/PhoneticAnnotationField";
|
|
4
|
+
export * from "./components/AnnotationRevisionList";
|
|
5
|
+
|
|
6
|
+
export const AnnotationComponentNames = [
|
|
7
|
+
"AnnotationEditor",
|
|
8
|
+
"AnnotationDiffViewer",
|
|
9
|
+
"PhoneticAnnotationField",
|
|
10
|
+
"AnnotationRevisionList"
|
|
11
|
+
] as const;
|
|
12
|
+
export type AnnotationComponentName = (typeof AnnotationComponentNames)[number];
|