@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 ADDED
@@ -0,0 +1,3 @@
1
+ # @echothink-ui/annotation
2
+
3
+ Annotation package for EchoThink voice dataset annotation workflows, including reusable editor, diff, phonetic field, and revision list components.
@@ -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":"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","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"]}
@@ -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":"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","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];