Skip to content

Commit

Permalink
#44 Added a simple table editor, with CSV import/export
Browse files Browse the repository at this point in the history
  • Loading branch information
wkwong-ribose committed Oct 19, 2021
1 parent ecf6409 commit 8d366d5
Show file tree
Hide file tree
Showing 13 changed files with 438 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/smart/model/editormodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
MMELProvision,
MMELReference,
MMELRole,
MMELTable,
MMELTerm,
MMELVariable,
MMELView,
Expand Down Expand Up @@ -100,6 +101,7 @@ export interface EditorModel {
pages: Record<string, EditorSubprocess>;
views: Record<string, MMELView>;
terms: Record<string, MMELTerm>;
tables: Record<string, MMELTable>;
root: string;
}

Expand Down
9 changes: 9 additions & 0 deletions src/smart/serialize/MMEL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
parseProvision,
parseReference,
parseRole,
parseTable,
parseTerm,
parseVariable,
parseView,
Expand Down Expand Up @@ -32,6 +33,7 @@ import {
toReferenceModel,
toRoleModel,
toSubprocessModel,
toTableModel,
toTermsModel,
toVariableModel,
toViewProfile as toViewProfileModel,
Expand Down Expand Up @@ -82,6 +84,9 @@ export function MMELToText(model: MMELModel): string {
for (const t in model.terms) {
out += toTermsModel(model.terms[t]) + '\n';
}
for (const t in model.tables) {
out += toTableModel(model.tables[t]) + '\n';
}
return out;
}

Expand All @@ -98,6 +103,7 @@ function parseModel(input: string): MMELModel {
notes: {},
views: {},
terms: {},
tables: {},
root: '',
};

Expand Down Expand Up @@ -163,6 +169,9 @@ function parseModel(input: string): MMELModel {
} else if (command === 'term') {
const e = parseTerm(token[i++], token[i++]);
model.terms[e.id] = e;
} else if (command === 'table') {
const t = parseTable(token[i++], token[i++]);
model.tables[t.id] = t;
} else {
console.error('Unknown command ' + command);
break;
Expand Down
57 changes: 57 additions & 0 deletions src/smart/serialize/handler/supporthandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MMELProvision,
MMELReference,
MMELRole,
MMELTable,
MMELTerm,
MMELVariable,
MMELVarSetting,
Expand Down Expand Up @@ -243,6 +244,62 @@ export function parseView(id: string, data: string): MMELView {
return v;
}

function to2DArray(data: string[], column: number): string[][] {
let count = 0;
let row: string[] = [];
const ret: string[][] = [];
for (const x of data) {
row[count++] = MMELremovePackage(x);
if (count >= column) {
ret.push(row);
row = [];
count = 0;
}
}
return ret;
}

export function parseTable(id: string, data: string): MMELTable {
const table: MMELTable = {
id,
title: '',
columns: 1,
data: [],
datatype: DataType.TABLE,
};

let cells: string[] = [];
if (data !== '') {
const t = MMELtokenizePackage(data);
let i = 0;
while (i < t.length) {
const command: string = t[i++];
if (i < t.length) {
if (command === 'title') {
table.title = MMELremovePackage(t[i++]);
} else if (command === 'columns') {
table.columns = parseInt(MMELremovePackage(t[i++]));
} else if (command === 'data') {
cells = MMELtokenizePackage(t[i++]);
} else {
throw new Error(
`Parsing error: table. ID ${id}: Unknown keyword ${command}`
);
}
} else {
throw new Error(
'Parsing error: variable. ID ' +
id +
': Expecting value for ' +
command
);
}
}
}
table.data = to2DArray(cells, table.columns);
return table;
}

export function parseTerm(id: string, data: string): MMELTerm {
const v: MMELTerm = {
id,
Expand Down
1 change: 1 addition & 0 deletions src/smart/serialize/interface/baseinterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export enum DataType {
VIEW = 'view',
NOTE = 'note',
TERMS = 'terms',
TABLE = 'table',
}

// the base interface for all objects in MMEL
Expand Down
2 changes: 2 additions & 0 deletions src/smart/serialize/interface/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MMELProvision,
MMELReference,
MMELRole,
MMELTable,
MMELTerm,
MMELVariable,
MMELView,
Expand All @@ -24,6 +25,7 @@ export interface MMELModel {
views: Record<string, MMELView>;
notes: Record<string, MMELNote>;
terms: Record<string, MMELTerm>;
tables: Record<string, MMELTable>;

root: string;
}
8 changes: 8 additions & 0 deletions src/smart/serialize/interface/supportinterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,11 @@ export interface MMELTerm extends MMELObject {
notes: string[];
datatype: DataType.TERMS;
}

export interface MMELTable extends MMELObject {
id: string;
title: string;
columns: number;
data: string[][];
datatype: DataType.TABLE;
}
13 changes: 13 additions & 0 deletions src/smart/serialize/util/serailizeformater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
MMELProvision,
MMELReference,
MMELRole,
MMELTable,
MMELTerm,
MMELVariable,
MMELVarSetting,
Expand Down Expand Up @@ -330,6 +331,18 @@ export function toTermsModel(term: MMELTerm): string {
out += '}\n';
return out;
}
export function toTableModel(table: MMELTable): string {
let out: string = 'table ' + table.id + ' {\n';
out += ' title "' + table.title + '"\n';
out += ' columns "' + table.columns + '"\n';
out += ' data {\n';
for (const row of table.data) {
out += ' ' + row.map(x => `"${x}"`).join(' ') + '\n';
}
out += ' }\n';
out += '}\n';
return out;
}

export function toRoleModel(role: MMELRole): string {
let out: string = 'role ' + role.id + ' {\n';
Expand Down
34 changes: 31 additions & 3 deletions src/smart/ui/common/fields.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
/** @jsx jsx */

import { jsx } from '@emotion/react';
import { FormGroup, HTMLSelect, IconName } from '@blueprintjs/core';
import {
FormGroup,
HTMLSelect,
IconName,
NumericInput,
} from '@blueprintjs/core';
import React, { RefObject, useState } from 'react';
import { EditorModel } from '../../model/editormodel';
import MGDButton from '../../MGDComponents/MGDButton';
Expand Down Expand Up @@ -32,6 +37,13 @@ export interface IField {
rows?: number;
}

export interface INumField {
text?: string;
value: number;
onChange?: (x: number) => void;
extend?: JSX.Element;
}

export interface IComboField {
text?: string;
options: readonly string[];
Expand Down Expand Up @@ -113,12 +125,28 @@ export interface IUpdateInterface<T> {
cancelClicked: () => void;
}

export const NormalTextField: React.FC<IField> = (f: IField) => {
export const NumberTextField: React.FC<INumField> = f => {
return (
<FormGroup label={f.text} helperText={f.extend}>
<NumericInput
readOnly={f.onChange === undefined}
onValueChange={x => {
if (f.onChange) {
f.onChange(x);
}
}}
value={f.value}
fill
/>
</FormGroup>
);
};

export const NormalTextField: React.FC<IField> = f => {
return (
<FormGroup label={f.text} helperText={f.extend}>
<MGDTextarea
readOnly={f.onChange === undefined}
id="field#text"
onChange={e => {
if (f.onChange) {
f.onChange(e.target.value);
Expand Down
9 changes: 8 additions & 1 deletion src/smart/ui/control/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import MetaEditPage from '../edit/metaedit';
import ReferenceEditPage from '../edit/refedit';
import RegistryEditPage from '../edit/registryedit';
import RoleEditPage from '../edit/roleedit';
import TableEditPage from '../edit/table/TableEdit';
import TermsEditPage from '../edit/TermEdit';
import ViewProfileEditPage from '../edit/ViewProfileEdit';

Expand All @@ -35,6 +36,7 @@ export enum SETTINGPAGE {
MEASUREMENT = 'measure',
PROFILE = 'profile',
TERMPAGE = 'terms',
TABLEPAGE = 'table',
}

interface TabProps {
Expand Down Expand Up @@ -108,6 +110,12 @@ const tabs: Record<SETTINGPAGE, TabProps> = {
<ViewProfileEditPage model={model} setModel={setModel} />
),
},
[SETTINGPAGE.TABLEPAGE]: {
title: 'Tables',
Panel: ({ model, setModel }) => (
<TableEditPage model={model} setModel={setModel} />
),
},
};

const BasicSettingPane: React.FC<{
Expand All @@ -124,7 +132,6 @@ const BasicSettingPane: React.FC<{
<MGDDisplayPane>
<Tabs
css={mgd_tabs}
id="TabsExample"
onChange={x => setPage(x as SETTINGPAGE)}
selectedTabId={page}
animate={false}
Expand Down
89 changes: 89 additions & 0 deletions src/smart/ui/edit/table/TableEdit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/** @jsx jsx */
/** @jsxFrag React.Fragment */

import { jsx } from '@emotion/react';
import React from 'react';
import { EditorModel } from '../../../model/editormodel';
import { MMELTable } from '../../../serialize/interface/supportinterface';
import { checkId, defaultItemSorter } from '../../../utils/ModelFunctions';
import { createTable } from '../../../utils/EditorFactory';
import { IListItem, IManageHandler } from '../../common/fields';
import ListManagePage from '../../common/listmanagement/listmanagement';
import TableItemEditPage from './TableItemEdit';

const TableEditPage: React.FC<{
model: EditorModel;
setModel: (model: EditorModel) => void;
}> = function ({ model, setModel }) {
function matchFilter(x: MMELTable, filter: string) {
return (
filter === '' ||
x.id.toLowerCase().includes(filter) ||
x.title.toLowerCase().includes(filter)
);
}

function getTableListItems(filter: string): IListItem[] {
return Object.values(model.tables)
.filter(x => matchFilter(x, filter))
.map(x => ({ id: x.id, text: x.title }))
.sort(defaultItemSorter);
}

function removeTableListItem(ids: string[]) {
for (const id of ids) {
delete model.tables[id];
}
setModel(model);
}

function addTable(x: MMELTable): boolean {
if (checkId(x.id, model.tables)) {
model.tables[x.id] = { ...x };
setModel(model);
return true;
}
return false;
}

function updateTable(oldid: string, x: MMELTable): boolean {
if (oldid !== x.id) {
if (checkId(x.id, model.tables)) {
delete model.tables[oldid];
model.tables[x.id] = { ...x };
setModel(model);
return true;
}
return false;
} else {
model.tables[oldid] = { ...x };
setModel(model);
return true;
}
}

function getTableById(id: string): MMELTable {
const table = model.tables[id];
if (table === undefined) {
return createTable('');
}
return table;
}

const tablehandler: IManageHandler<MMELTable> = {
filterName: 'Table filter',
itemName: 'View tables',
Content: TableItemEditPage,
initObj: createTable(''),
model: model,
getItems: getTableListItems,
removeItems: removeTableListItem,
addItem: obj => addTable(obj),
updateItem: (oldid, obj) => updateTable(oldid, obj),
getObjById: getTableById,
};

return <ListManagePage {...tablehandler} />;
};

export default TableEditPage;
Loading

0 comments on commit 8d366d5

Please sign in to comment.