diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e69de29 diff --git a/docs/DeveloperDoc.md b/docs/DeveloperDoc.md new file mode 100644 index 0000000..33b935d --- /dev/null +++ b/docs/DeveloperDoc.md @@ -0,0 +1,19 @@ +# Developer Documentation + +## How to render a palette + +`Palette.ts` constructs a palette based on a JSON file that contains a list +of the cells in the palette. An example is found in +[`src/keyboards/bmw_palette.json`](../src/keyboards/bmw_palette.json). The +`cells` object is the list of all of the cells. Each cell has a `type` key and +an `options` key. The `type` value indicates which Preact component should be +used to render this cell. The `options` contains information to be passed to the +component. + +## How to add a new cell type + +When a new `type` value is introduced, developers need to: + +1. Create a new component to render the new cell type; +2. In `GlobalData.ts`, update `cellTypeRegistry` to add the entry that maps the +new type value to the actual component. diff --git a/index.html b/index.html index 5badbb7..6de338c 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,17 @@ - - - - - Adaptive Palette - - -
- + + + + + Adaptive Palette + + +

Palette Based on JSON

+
+ +
+ diff --git a/jest.config.cjs b/jest.config.cjs index 9f5e3f3..257690e 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -1,4 +1,5 @@ module.exports = { + setupFiles: ["./setupFetchForJest.ts"], verbose: true, preset: "ts-jest", testEnvironment: "jsdom", @@ -10,5 +11,8 @@ module.exports = { }, testPathIgnorePatterns: ["/node_modules/", "/dist/"], transformIgnorePatterns: ["/dist/"], - moduleFileExtensions: ["mjs", "js", "jsx", "ts", "tsx", "json", "node"] + moduleFileExtensions: ["mjs", "js", "jsx", "ts", "tsx", "json", "node"], + moduleNameMapper: { + "^.+\\.(css|less|scss)$": "babel-jest" + }, }; diff --git a/package-lock.json b/package-lock.json index 2fcf14c..6954308 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,16 @@ { - "name": "aac-keyboard-playground", + "name": "adaptive-palette", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "aac-keyboard-playground", + "name": "adaptive-palette", "dependencies": { + "bliss-svg-builder": "^0.1.0-alpha.4", "htm": "^3.1.1", "preact": "^10.13.1", - "sass": "^1.65.1" + "sass": "^1.65.1", + "whatwg-fetch": "^3.6.19" }, "devDependencies": { "@babel/preset-env": "^7.22.14", @@ -4301,6 +4303,11 @@ "node": ">=8" } }, + "node_modules/bliss-svg-builder": { + "version": "0.1.0-alpha.4", + "resolved": "https://registry.npmjs.org/bliss-svg-builder/-/bliss-svg-builder-0.1.0-alpha.4.tgz", + "integrity": "sha512-wOk69Xb6xjrliEWasJmk8mfA4d3CtorNffO1b4zLVIYvtZ9dPtwV9ehrJloatXIjyxpFHuJTfqElkqW41udEsA==" + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -10849,6 +10856,11 @@ "node": ">=12" } }, + "node_modules/whatwg-fetch": { + "version": "3.6.19", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.19.tgz", + "integrity": "sha512-d67JP4dHSbm2TrpFj8AbO8DnL1JXL5J9u0Kq2xW6d0TFDbCA3Muhdt8orXC22utleTVj7Prqt82baN6RBvnEgw==" + }, "node_modules/whatwg-mimetype": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", diff --git a/package.json b/package.json index e016d1b..6537d8c 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,11 @@ "test": "jest" }, "dependencies": { + "bliss-svg-builder": "^0.1.0-alpha.4", "htm": "^3.1.1", "preact": "^10.13.1", - "sass": "^1.65.1" + "sass": "^1.65.1", + "whatwg-fetch": "^3.6.19" }, "devDependencies": { "@babel/preset-env": "^7.22.14", diff --git a/setupFetchForJest.ts b/setupFetchForJest.ts new file mode 100644 index 0000000..ae00b5e --- /dev/null +++ b/setupFetchForJest.ts @@ -0,0 +1,24 @@ +/** + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +// There is an issue requesting the additions adding the fetch API to jest-dom +// for testing with node. Node.js Core has had an implementation of the fetch +// API since v17.5. However, jest-dom removes it. This comment in the issue +// suggests using whatwg's version of fetch() instead: +// https://github.com/jsdom/jsdom/issues/1724#issuecomment-720727999 + +import { fetch } from "whatwg-fetch"; + +module.exports = { + globals: { + fetch: fetch + } +}; diff --git a/src/ActionBmwCodeCell.scss b/src/ActionBmwCodeCell.scss new file mode 100644 index 0000000..5c3bf64 --- /dev/null +++ b/src/ActionBmwCodeCell.scss @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +.actionBmwCodeCell { + border: 2px solid blue; + background-color: gray; + border-radius: 5px; + padding: 1rem; + font-size: 1rem; + text-align: center; + + &:hover, &:focus { + background-color: lightblue; + color: #0000aa; + } +} diff --git a/src/ActionBmwCodeCell.test.ts b/src/ActionBmwCodeCell.test.ts new file mode 100644 index 0000000..e9fd64e --- /dev/null +++ b/src/ActionBmwCodeCell.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { render, screen } from "@testing-library/preact"; +import "@testing-library/jest-dom"; +import { html } from "htm/preact"; + +import { initAdaptivePaletteGlobals } from "./GlobalData"; +import { ActionBmwCodeCell } from "./ActionBmwCodeCell"; + +describe("ActionBmwCodeDell render tests", () => { + + const TEST_CELL_ID = "uuid-of-some-kind"; + const testCell = { + options: { + "label": "Bliss Language", + "rowStart": "3", + "rowSpan": "2", + "columnStart": "2", + "columnSpan": "1", + "bciAvId": [ 12335, "/", 8499 ] // VERB+EN + } + }; + + beforeAll(async () => { + await initAdaptivePaletteGlobals(); + }); + + test("Single ActionBmwCodeCell rendering", async () => { + + render(html` + <${ActionBmwCodeCell} + id="${TEST_CELL_ID}" + options=${testCell.options} + />` + ); + + // Check the rendered cell + const button = await screen.findByRole("button", {name: testCell.options.label}); + + // Check that the ActionBmwCodeCell/button is rendered and has the correct + // attributes and text. + expect(button).toBeVisible(); + expect(button).toBeValid(); + expect(button.id).toBe(TEST_CELL_ID); + expect(button.getAttribute("class")).toBe("actionBmwCodeCell"); + expect(button.textContent).toBe(testCell.options.label); + + // Check the grid cell styles. + expect(button.style["grid-column"]).toBe("2 / span 1"); + expect(button.style["grid-row"]).toBe("3 / span 2"); + + // Check disabled state (should be enabled) + expect(button.getAttribute("disabled")).toBe(null); + }); + +}); diff --git a/src/ActionBmwCodeCell.ts b/src/ActionBmwCodeCell.ts new file mode 100644 index 0000000..837c068 --- /dev/null +++ b/src/ActionBmwCodeCell.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { html } from "htm/preact"; +import { OptionsType } from "./index.d"; +import { BlissSymbol } from "./BlissSymbol"; +import "./ActionBmwCodeCell.scss"; + + +type ActionBmwCodeCellPropsType = { + id: string, + options: OptionsType +}; + +export function ActionBmwCodeCell (props: ActionBmwCodeCellPropsType) { + const { + columnStart, columnSpan, rowStart, rowSpan, bciAvId, label + } = props.options; + + const gridStyles = ` + grid-column: ${columnStart} / span ${columnSpan}; + grid-row: ${rowStart} / span ${rowSpan}; + `; + + return html` + + `; +} diff --git a/src/BlissSymbol.test.ts b/src/BlissSymbol.test.ts new file mode 100644 index 0000000..a469699 --- /dev/null +++ b/src/BlissSymbol.test.ts @@ -0,0 +1,117 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { render, screen } from "@testing-library/preact"; +import "@testing-library/jest-dom"; +import { html } from "htm/preact"; + +import { initAdaptivePaletteGlobals } from "./GlobalData"; +import { BlissSymbol, GRAPHIC_ROLE } from "./BlissSymbol"; + +describe("BlissSymbol render tests", () => { + const singleBciAvId = { + bciAvId: 12335, + label: "VERB" + }; + + const arrayBciAvId = { + bciAvId: [12335, "/", 8499], + label: "VERB+S" + }; + + const MOCK_LABEL_ID = "mockLabelId"; + const UNKNOWN_BCI_AV_ID = -1; + + beforeAll(async () => { + await initAdaptivePaletteGlobals(); + }); + + test(`BlissSymbol defined by a single BCI_AV_ID (${singleBciAvId.label})`, async () => { + render(html` + <${BlissSymbol} + bciAvId="${singleBciAvId.bciAvId}" + label="${singleBciAvId.label}" + isPresentation=true + />` + ); + const blissSymbolLabelDiv = await screen.findByText(singleBciAvId.label); + expect(blissSymbolLabelDiv).toBeVisible(); + expect(blissSymbolLabelDiv).toBeValid(); + + // Expect an element as the only sibling + const parentChildren = blissSymbolLabelDiv.parentNode.childNodes; + expect(parentChildren.length).toBe(2); + expect(parentChildren[0].nodeName).toBe("svg"); + }); + + test("BlissSymbol when the SVG is unknown", async () => { + render(html` + <${BlissSymbol} + bciAvId="${UNKNOWN_BCI_AV_ID}" + label="${arrayBciAvId.label}" + isPresentation=true + />` + ); + const blissSymbolLabelDiv = await screen.findByText(arrayBciAvId.label); + const svgElement = blissSymbolLabelDiv.parentNode.querySelector("svg"); + const parentChildren = blissSymbolLabelDiv.parentNode.childNodes; + expect(parentChildren.length).toBe(1); + expect(svgElement).toBe(null); + }); + + test(`BlissSymbol defined by an of BCI_AV_IDs (${arrayBciAvId.label})`, async () => { + render(html` + <${BlissSymbol} + bciAvId="${arrayBciAvId.bciAvId}" + label="${arrayBciAvId.label}" + isPresentation=true + />` + ); + const blissSymbolLabelDiv = await screen.findByText(arrayBciAvId.label); + expect(blissSymbolLabelDiv).toBeVisible(); + expect(blissSymbolLabelDiv).toBeValid(); + const parentChildren = blissSymbolLabelDiv.parentNode.childNodes; + expect(parentChildren.length).toBe(2); + expect(parentChildren[0].nodeName).toBe("svg"); + }); + + test("BlissSymbol aria: when svg has no role)", async () => { + render(html` + <${BlissSymbol} + bciAvId="${arrayBciAvId.bciAvId}" + label="${arrayBciAvId.label}" + isPresentation=true + />` + ); + const blissSymbolLabelDiv = await screen.findByText(arrayBciAvId.label); + const svgElement = blissSymbolLabelDiv.parentNode.querySelector("svg"); + expect(svgElement.getAttribute("aria-hidden")).toBe("true"); + expect(svgElement.getAttribute("role")).toBe(null); + expect(svgElement.getAttribute("aria-labelledby")).toBe(null); + }); + + test("BlissSymbol aria: when svg has a graphic role)", async () => { + render(html` + <${BlissSymbol} + bciAvId="${arrayBciAvId.bciAvId}" + label="${arrayBciAvId.label}" + isPresentation=false + labelledBy=${MOCK_LABEL_ID} + />` + ); + const blissSymbolLabelDiv = await screen.findByText(arrayBciAvId.label); + const svgElement = blissSymbolLabelDiv.parentNode.querySelector("svg"); + expect(svgElement.getAttribute("role")).toBe(GRAPHIC_ROLE); + expect(svgElement.getAttribute("aria-labelledby")).toBe(MOCK_LABEL_ID); + expect(svgElement.getAttribute("aria-hidden")).toBe(null); + }); +}); + diff --git a/src/BlissSymbol.ts b/src/BlissSymbol.ts new file mode 100644 index 0000000..e826438 --- /dev/null +++ b/src/BlissSymbol.ts @@ -0,0 +1,61 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { html } from "htm/preact"; +import { getSvgElement } from "./SvgUtils"; +import { BciAvIdType } from "./index.d"; + +export const GRAPHIC_ROLE = "graphic-symbol img"; + +type BlissSymbolPropsType = { + bciAvId: BciAvIdType, + label: string, + // Aria markup information for svg part of the BlissSymbol. The first is + // really a boolean, but the html template function converts it to string + // values. + isPresentation: "true" | "false", + // @id of label element when isPresntation is "false" + labelledBy?: string +} + +export function BlissSymbol (props: BlissSymbolPropsType) { + const { bciAvId, label, isPresentation, labelledBy } = props; + const svgElement = getSvgElement(bciAvId); + + let svgMarkupString = ""; + if (svgElement) { + // Deal with aria markup, depending on whether the SVG + if (isPresentation === "true") { + svgElement.setAttribute("aria-hidden", true); + } else { + svgElement.setAttribute("role", `${GRAPHIC_ROLE}`); + svgElement.setAttribute("aria-labelledby", labelledBy); + } + svgMarkupString = svgElement.outerHTML; + } + + // The coercion to `any` and assignment to `raw` is _only_ for the unit + // tests to avoid the error: + // "Argument of type 'any[]' is not assignable to parameter of type + // 'TemplateStringsArray. Property raw is mssing ..." + // see: https://stackoverflow.com/questions/50706337/importing-html-to-typescript-to-use-as-templatestringliteral#answer-51012181 + // Also TypeScript-ESLint does not allow explicit `any` types; override that + // rule for this case. + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const templateStringArray = [`${svgMarkupString}`] as any; + templateStringArray.raw = [`${svgMarkupString}`]; + + return html` + ${html(templateStringArray)} +
${label}
+ `; +} diff --git a/src/GlobalData.ts b/src/GlobalData.ts new file mode 100644 index 0000000..164665c --- /dev/null +++ b/src/GlobalData.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +"use strict"; + +/** + * Populate and export global data + */ + +import { ActionBmwCodeCell } from "./ActionBmwCodeCell"; + +export const adaptivePaletteGlobals = { + // The map between the BCI-AV IDs and the code consumed by the Bliss SVG + // builder. The map itself is set asynchronously. + blissaryIdMapUrl: "https://raw.githubusercontent.com/hlridge/Bliss-Blissary-BCI-ID-Map/main/blissary_to_bci_mapping.json", + blissaryIdMap: null +}; + +export async function loadBlissaryIdMap () { + const response = await fetch(adaptivePaletteGlobals.blissaryIdMapUrl); + return await response.json(); +} + +export async function initAdaptivePaletteGlobals () { + adaptivePaletteGlobals.blissaryIdMap = await loadBlissaryIdMap(); +} + +/** + * The map between cell types (string) and actual components that render cells + */ +export const cellTypeRegistry = { + "ActionBmwCodeCell": ActionBmwCodeCell +}; diff --git a/src/Palette.scss b/src/Palette.scss new file mode 100644 index 0000000..8a96df1 --- /dev/null +++ b/src/Palette.scss @@ -0,0 +1,18 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +.paletteContainer { + display: grid; + grid-template-columns: auto auto auto; + border: 2px solid #f76707; + border-radius: 5px; + background-color: pink; +} diff --git a/src/Palette.test.ts b/src/Palette.test.ts new file mode 100644 index 0000000..cc585e8 --- /dev/null +++ b/src/Palette.test.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +"use strict"; + +import { render, screen } from "@testing-library/preact"; +import "@testing-library/jest-dom"; +import { html } from "htm/preact"; + +import { initAdaptivePaletteGlobals } from "./GlobalData"; +import { Palette } from "./Palette"; + +describe("Palette component", () => { + + // The test palette defines three cells, but they collectively define a + // palette of four rows and six columns. + const testPalette = { + "name": "Test Palette", + "cells": { + "firstCell": { + "type": "ActionBmwCodeCell", + "options": { + "label": "First Cell", + "bciAvId": [ + 17720, + "/", + 17697 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "secondCell": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Second Cell", + "bciAvId": 23409, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "thirdCell": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Third Cell", + "bciAvId": [ + 25554, + "/", + 12335 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + } + } + }; + const NUM_CELLS = Object.keys(testPalette.cells).length; + + beforeAll(async () => { + await initAdaptivePaletteGlobals(); + }); + + test("Render palette", async() => { + + // render() the palette and then wait until its first cell is available to + // insure that the entire palette is in the DOM. + render(html`<${Palette} json=${testPalette}/>`); + const firstCell = await screen.findByText("First Cell"); + expect(firstCell).toBeInTheDocument(); + + const paletteElement = document.querySelector("div.paletteContainer"); + expect(paletteElement).toBeVisible(); + expect(paletteElement).toBeValid(); + + // There should be 6 columns in the grid and NUM_CELLS children. + expect(paletteElement).toHaveStyle("grid-template-columns: repeat(6, auto);"); + expect(paletteElement.childNodes.length).toBe(NUM_CELLS); + }); +}); diff --git a/src/Palette.ts b/src/Palette.ts new file mode 100644 index 0000000..74092fd --- /dev/null +++ b/src/Palette.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { html } from "htm/preact"; +import { JsonPaletteType } from "./index.d"; +import { cellTypeRegistry } from "./GlobalData"; +import "./Palette.scss"; + +type PalettePropsType = { + json: JsonPaletteType +}; + +/** + * Given a palette defined in a json structure, compute the number of rows + * and columns in that palette. + * + * @param {Object} paletteDefinition - An object that lists the positions, + * heights and widths of the cells in the palette. + * @return {Object} - The row and column counts: `{ numRows: ..., numColumns: ...}`. + */ +function countRowsColumns (paletteDefinition) { + let rowCount = 0; + let colCount = 0; + let rightColumn = 0; + let bottomRow = 0; + const cellIds = Object.keys(paletteDefinition.cells); + cellIds.forEach((id) => { + const cellOptions = paletteDefinition.cells[id].options; + rightColumn = cellOptions.columnStart + cellOptions.columnSpan; + if (rightColumn > colCount) { + colCount = rightColumn; + } + bottomRow = cellOptions.rowStart + cellOptions.rowSpan; + if (bottomRow > rowCount) { + rowCount = bottomRow; + } + }); + return { numRows: rowCount, numColumns: colCount }; +} + +export function Palette (props: PalettePropsType) { + + const paletteDefinition = props.json; + const rowsCols = countRowsColumns(paletteDefinition); + const cellIds = Object.keys(paletteDefinition.cells); + + // Loop to create an array of renderings for each cell + const theCells = []; + cellIds.forEach((id) => { + const aCell = paletteDefinition.cells[id]; + const cellOptions = aCell.options; + const cellComponent = cellTypeRegistry[aCell.type]; + if (!cellComponent) { + console.error(`Error at rendering the cell type "${aCell.type}". Fix it by defining the render component for this cell type at GlobalData.ts -> cellTypeRegistry.`); + } else { + const paletteCell = html` + <${cellComponent} id="${id}" options=${cellOptions} /> + `; + theCells.push(paletteCell); + } + }); + return html` +
+ ${theCells} +
+ `; +} diff --git a/src/PaletteStore.test.ts b/src/PaletteStore.test.ts new file mode 100644 index 0000000..e677ea0 --- /dev/null +++ b/src/PaletteStore.test.ts @@ -0,0 +1,95 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +"use strict"; + +import { PaletteStore } from "./PaletteStore"; + +describe("PaletteStore module", () => { + + const dummyPalette1 = { + "name": "dummyPalette1", + "cells": { + "cellOne": { + "type": "cellOneType", + "options": { + "label": "Singer", + "bciAvId": 16991, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "cellTwo": { + "type": "cellTwoType", + "options": { + "label": "Dancer", + "bciAvId": 19961, + "rowStart": 2, + "rowSpan": 3, + "columnStart": 4, + "columnSpan": 5 + } + } + } + }; + + const dummyPalette2Name = "DummyPalette2"; + const dummyPalette2 = { + "name": "DifferentName", + "cells": { + "dummyCell": { + "type": "dummyCellType", + "options": { + "label": "Choreographer", + "bciAvId": 666, + "rowStart": 2, + "rowSpan": 2, + "columnStart": 2, + "columnSpan": 2 + } + } + } + }; + + const paletteStore = new PaletteStore(); + + test("Empty PaletteStore", () => { + expect(paletteStore.isEmpty()).toBe(true); + }); + + test("Non-empty PaletteStore", () => { + paletteStore.addPalette(dummyPalette1); + expect(paletteStore.isEmpty()).toBe(false); + expect(paletteStore.numPalettes).toBe(1); + expect(paletteStore.paletteList).toEqual(["dummyPalette1"]); + }); + + test("Add another palette", () => { + paletteStore.addPalette(dummyPalette2, dummyPalette2Name); + expect(paletteStore.numPalettes).toBe(2); + expect(paletteStore.paletteList).toEqual(["dummyPalette1", dummyPalette2Name]); + }); + + test("Retrieve a palette", () => { + const retrievedPalette = paletteStore.getNamedPalette(dummyPalette2Name); + expect(retrievedPalette).toBe(dummyPalette2); + }); + + test("Delete a palette", () => { + const numPalettes = paletteStore.numPalettes; + const removedPalette = paletteStore.removePalette(dummyPalette1.name); + expect(removedPalette).toBe(dummyPalette1); + expect(paletteStore.numPalettes).toBe(numPalettes - 1); + expect(paletteStore.getNamedPalette(dummyPalette1.name)).toBeUndefined(); + }); +}); diff --git a/src/PaletteStore.ts b/src/PaletteStore.ts new file mode 100644 index 0000000..323305d --- /dev/null +++ b/src/PaletteStore.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +"use strict"; + +import { JsonPaletteType } from "./index.d"; + +export class PaletteStore { + + // Singleton storage for all palettes + // The contents are named Palette instances; hence, each palette must have + // a unique name. + static paletteMap = {}; + + /** + * Report if the PaletteStore is empty. + * @return: `true` if the store is empty; `false` otherwise. + */ + isEmpty () { + return Object.keys(PaletteStore.paletteMap).length === 0; + } + + /** + * Add a palette to the store, or replace a palette with a new one. If the + * palette's name/identifier matches a palette already in the store, it + * replaces it. + * @param: {Object} palette - The palette to add to the store. + * @param: {Object}.name - The internal name of the palette. + * @param: {String} name - Optional, the preferred name of the palette. + */ + addPalette (palette: JsonPaletteType, paletteName?: string) { + if (!palette) { + return; + } + let palName; + if (paletteName) { + palName = paletteName; + } else if (palette.name) { + palName = palette.name; + } else { + return; + } + PaletteStore.paletteMap[palName] = palette; + console.log(`Palette ${palName} added to the store.`); + } + + /** + * Remove the palette with the given name. + * @param: {String} paletteName - The palette to remove. + * @return {Object} reference to the removed palette. + */ + removePalette (paletteName: string) { + if (this.isEmpty()) { + return null; + } else { + const palette = PaletteStore.paletteMap[paletteName]; + if (palette) { + delete PaletteStore.paletteMap[paletteName]; + console.log(`Palette ${paletteName} removed from the store.`); + } + return palette; + } + } + + /** + * Accessor for the number of palettes in the store. + * @return: {integer} the number of palettes in the store}. + */ + get numPalettes() : number { + return Object.keys(PaletteStore.paletteMap).length; + } + + /** + * Accessor for a list of names of palettes in the store. + * @return: {Array} of palette names. + */ + get paletteList() { + return Object.keys(PaletteStore.paletteMap); + } + + /** + * Accessor for a retrieving the named palette. + * @param: {String} paletteName - The palette to retrieve. + * @return {Object} reference to the named palette, or undefined if no such + * palette. + */ + getNamedPalette(paletteName: string) { + return PaletteStore.paletteMap[paletteName]; + } +} diff --git a/src/SvgUtils.test.ts b/src/SvgUtils.test.ts new file mode 100644 index 0000000..89d4696 --- /dev/null +++ b/src/SvgUtils.test.ts @@ -0,0 +1,59 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ +"use strict"; + +import { initAdaptivePaletteGlobals, adaptivePaletteGlobals } from "./GlobalData"; +import { bciToBlissaryId, bciAvIdToString } from "./SvgUtils"; + +describe("SvgUtils module", () => { + + // The `singleBciAvId` is taken from the BMW json for "CONJ." The + // `bciAvIdArray` is also from the BMW json file using the codes for + // "VERB+EN". The `expectedX` constants are based on a manual lookup of the + // blissary ids. + const singleBciAvId = 23409; // CONJ. + const expectedString = "B823"; + const bciAvIdArray =[ 12335, "/", 8499 ]; // VERB+EN + const expectedConcatenation = "B106/B12"; + const invalidBciAvId = 1; + + beforeAll(async () => { + await initAdaptivePaletteGlobals(); + }); + + test("Retrieve blissary id from BCI-AV-ID", () => { + const { blissaryIdMap } = adaptivePaletteGlobals; + + // Use the 100th entry in the map for testing. There is nothing special + // about the 100th entry. Just as good as any. + const blissaryIdMapEntry = blissaryIdMap[100]; + + const result = bciToBlissaryId(blissaryIdMapEntry.bciAvId); + expect(result.blissaryId).toBe(blissaryIdMapEntry.blissaryId); + }); + + test("No blissary id for unknown BCI-AV-ID", () => { + expect(bciToBlissaryId(invalidBciAvId)).toBe(undefined); + }); + + test("Create svg builder argument", () => { + let result = bciAvIdToString(singleBciAvId); + expect(result).toBe(expectedString); + + result = bciAvIdToString(bciAvIdArray); + expect(result).toBe(expectedConcatenation); + }); + + test("Unknown BCI-AV-ID", () => { + expect(() => { bciAvIdToString(invalidBciAvId); }).toThrow(); + }); + +}); diff --git a/src/SvgUtils.ts b/src/SvgUtils.ts new file mode 100644 index 0000000..5cb38ba --- /dev/null +++ b/src/SvgUtils.ts @@ -0,0 +1,98 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +import { BlissSVGBuilder } from "bliss-svg-builder"; +import { BciAvIdType } from "./index.d"; +import { adaptivePaletteGlobals } from "./GlobalData"; + +/** + * Convert the given `BciAvIdType` to a SVG builder code string. If the + * `BciAvIdType`argument is an array of BCI-AV-IDs and punctuation, concatenate + * the array into a string of builder code strings and punctuation marks. + * @param {BciAvIdType} bciAvId - The BciAvIdType to convert. + * @return {String} - The concatenation of the builder codes and punctuation, + * e.g., "B106/B12". + */ +export function bciAvIdToString (bciAvId: BciAvIdType) { + let finalCode = ""; + if (typeof bciAvId === "number") { + const { blissSvgBuilderCode } = bciToBlissaryId(bciAvId); + finalCode = blissSvgBuilderCode; + } + // `bicAvId` is an array + else { + bciAvId.forEach((item) => { + if (typeof item === "number") { + const { blissSvgBuilderCode } = bciToBlissaryId(item); + finalCode = `${finalCode}${blissSvgBuilderCode}`; + } else { + finalCode = `${finalCode}${item}`; + } + }); + } + return finalCode; +} + +/** + * Create and return the builder from a string based on the given BciAvIdType. + * If the BciAvIdType is invalid, `null` is returned. + * @param {BciAvIdType} bciAvId - A single BCI-AV-ID (a number) or an array of + * such ids and characters, e.g. + * `[ 12335, "/", 8499 ]` + * @return {BlissSVGBuilder} - The corresponding SVG markup, or `null`. + */ + +function getSvgBuilder (bciAvId: BciAvIdType) { + let builder; + try { + const svgBuilderArgument = bciAvIdToString(bciAvId); + builder = new BlissSVGBuilder(svgBuilderArgument); + } + catch (err) { + console.error(err); + console.error(`Unknown bci-av-id = ${bciAvId}`); + builder = null; + } + return builder; +} + +/** + * Get the SVG markup as a string based on the given single BCI-AV-ID. + * or an array of BCI-AV-IDs and other characters + * + * @param {BciAvIdType} bciAvId - A single BCI-AV-ID (a number) or an array of + * such ids and characters, e.g. + * `[ 12335, "/", 8499 ]` + * @return {String} - The corresponding SVG markup, or `undefined`. + */ +export function getSvgMarkupString (bciAvId: BciAvIdType) { + const builder = getSvgBuilder(bciAvId); + return ( builder ? builder.svgCode : undefined ); +} + +/** + * Get the SVG markup as a DOM element based on the given single BCI-AV-ID. + * or an array of BCI-AV-IDs and other characters + * + * @param {BciAvIdType} bciAvId - A single BCI-AV-ID (a number) or an array of + * such ids and characters, e.g. + * `[ 12335, "/", 8499 ]` + * @return {Element} - The corresponding SVG markup, or `undefined`. + */ +export function getSvgElement (bciAvId: BciAvIdType) { + const builder = getSvgBuilder(bciAvId); + return ( builder ? builder.svgElement : undefined ); +} + +export function bciToBlissaryId (bciAvId: number) { + const { blissaryIdMap } = adaptivePaletteGlobals; + return blissaryIdMap.find((entry) => entry.bciAvId === bciAvId); +} diff --git a/src/index.d.ts b/src/index.d.ts new file mode 100644 index 0000000..dc5f109 --- /dev/null +++ b/src/index.d.ts @@ -0,0 +1,31 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ + +export type BciAvIdType = number | (string|number)[]; + +export type OptionsType = { + label: string, + columnStart: number, + columnSpan: number, + rowStart: number, + rowSpan: number, + bciAvId: BciAvIdType +}; + +export type JsonPaletteType = { + name: string, + cells: { + [key: string]: { + type: string, + options: OptionsType + } + } +}; diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..3b29a2e --- /dev/null +++ b/src/index.js @@ -0,0 +1,20 @@ +/* + * Copyright 2023 Inclusive Design Research Centre, OCAD University + * All rights reserved. + * + * Licensed under the New BSD license. You may not use this file except in + * compliance with this License. + * + * You may obtain a copy of the License at + * https://github.com/inclusive-design/adaptive-palette/blob/main/LICENSE + */ +import { render } from "preact"; +import { html } from "htm/preact"; +import { initAdaptivePaletteGlobals } from "./GlobalData.ts"; + +// Initialize any globals used elsewhere in the code. +await initAdaptivePaletteGlobals(); + +import { Palette } from "./Palette"; +import bmwJson from "./keyboards/bmw_palette.json"; +render (html`<${Palette} json=${bmwJson}/>`, document.getElementById("bmwKeyCodes")); diff --git a/src/keyboards/bmw_palette.json b/src/keyboards/bmw_palette.json new file mode 100644 index 0000000..230c684 --- /dev/null +++ b/src/keyboards/bmw_palette.json @@ -0,0 +1,1235 @@ +{ + "name": "BMW Palette", + "cells": { + "dem-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "DEM.", + "bciAvId": [ + 17720, + "/", + 17697 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "conj-4f033219-c564-47ac-9c0a-a5f945643d95": { + "type": "ActionBmwCodeCell", + "options": { + "label": "CONJ.", + "bciAvId": 23409, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "adverb-01aa64bd-f5d3-4e44-bf22-2b9931d30385": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ADVERB", + "bciAvId": [ + 25554, + "/", + 12335 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "prep-7935d53a-da5f-4b96-96be-ff9239c8bdb7": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PREP.", + "bciAvId": [ + 12324, + "/", + 17717 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "adj-8756633b-9061-4189-8795-0704c068610d": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ADJ.", + "bciAvId": 25554, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "adj-er-0b84a3b7-4567-4b87-bf41-80b1e817dced": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ADJ.+ER", + "bciAvId": 24879, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "adj-est-354976af-5647-43ac-968c-22d7271d3242": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ADJ.+EST", + "bciAvId": 24944, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "noun-ef0216fb-ca66-4e6f-8818-23369e84baf1": { + "type": "ActionBmwCodeCell", + "options": { + "label": "NOUN", + "bciAvId": 17717, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "noun-pl-ea758842-64c0-45ea-ae13-a59a21afbfec": { + "type": "ActionBmwCodeCell", + "options": { + "label": "NOUN PL.", + "bciAvId": [ + 17717, + ";", + 9011 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "n-person-42e7ecb7-3b23-475d-8d7d-5bbd9ddcc9c2": { + "type": "ActionBmwCodeCell", + "options": { + "label": "N PERSON", + "bciAvId": 16161, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "n-per-pl-2c18e44a-77a5-4d9f-af80-ffb31b76e784": { + "type": "ActionBmwCodeCell", + "options": { + "label": "N.PER PL", + "bciAvId": [ + 16161, + ";", + 9011 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "abs-time-94fb6d8a-be1e-4c31-a493-3bb738c06292": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ABS TIME", + "bciAvId": [ + "HC8N:0,8;S4:2,10", + "/", + 17732 + ], + "rowStart": 3, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "object-4ef53712-f191-4b51-9abd-c17775fee623": { + "type": "ActionBmwCodeCell", + "options": { + "label": "OBJECT", + "bciAvId": [ + 24727, + "/", + 12335 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "poss-643295e3-8f96-4577-876c-44b9ab3a3ca0": { + "type": "ActionBmwCodeCell", + "options": { + "label": "POSS.", + "bciAvId": 24925, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "verb-c79ac5f5-4556-45f8-bbe4-4f340a976dc1": { + "type": "ActionBmwCodeCell", + "options": { + "label": "VERB", + "bciAvId": 12335, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "verb-s-04b0ec73-e346-491a-a600-eade8845ceb1": { + "type": "ActionBmwCodeCell", + "options": { + "label": "VERB+S", + "bciAvId": [ + 12335, + "/", + 8499 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "verb-ing-53792e17-c9e3-40cf-9ea6-5c59e8b3dd45": { + "type": "ActionBmwCodeCell", + "options": { + "label": "VERB+ING", + "bciAvId": [ + 12335, + "/", + 14390 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "verb-ed-7eee8e58-f00e-4e39-84b0-dabc6d092cc7": { + "type": "ActionBmwCodeCell", + "options": { + "label": "VERB+ED", + "bciAvId": [ + 12335, + "/", + 15975 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "verb-en-edba8ab6-20ee-41ac-a12f-e4bdc869783d": { + "type": "ActionBmwCodeCell", + "options": { + "label": "VERB+EN", + "bciAvId": [ + 12335, + "/", + 13949 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "to-verb-f0f40343-ecfb-416e-8633-545dcf3a53ae": { + "type": "ActionBmwCodeCell", + "options": { + "label": "TO+VERB", + "bciAvId": 13860, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "what-14178f43-fb50-47c8-954e-2bf45e5668bd": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WHAT", + "bciAvId": 18229, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "interj-5af6365b-aba2-45da-8123-f11f09e1a36e": { + "type": "ActionBmwCodeCell", + "options": { + "label": "INTERJ.", + "bciAvId": [ + 24961, + "/", + 14947 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "minspeak-5bc52e92-fa56-4212-8a0a-3328921aac1b": { + "type": "ActionBmwCodeCell", + "options": { + "label": "MINSPEAK", + "bciAvId": [ + 14647, + "/", + 15972, + "/", + 15172 + ], + "rowStart": 4, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "force-1e174168-21b1-4ace-aa9a-5a199bbbdf74": { + "type": "ActionBmwCodeCell", + "options": { + "label": "FORCE", + "bciAvId": 22914, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "number-aa8f59de-874a-432a-829d-435f376fe39d": { + "type": "ActionBmwCodeCell", + "options": { + "label": "NUMBER", + "bciAvId": 15742, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "poem-35538cb4-84c1-4124-ae98-d11ac65c6701": { + "type": "ActionBmwCodeCell", + "options": { + "label": "POEM", + "bciAvId": 16210, + "rowStart": 4, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "i-3b3e2a2b-f1d3-48a2-9ad7-ad545f46b8bf": { + "type": "ActionBmwCodeCell", + "options": { + "label": "I+", + "bciAvId": 14916, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "we-c45b6407-11f9-4cb8-b9a8-3f5a6b0a2e21": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WE+", + "bciAvId": 18212, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "help-4c2b2a64-a24b-46e9-9fc1-2276483c0000": { + "type": "ActionBmwCodeCell", + "options": { + "label": "HELP", + "bciAvId": 14704, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "give-59cdc0bf-62a0-4b50-b28f-13dc865c329f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "GIVE", + "bciAvId": 14440, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "commun-2892c6e0-541d-483c-9575-55a46b338161": { + "type": "ActionBmwCodeCell", + "options": { + "label": "COMMUN.", + "bciAvId": 13390, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "tool-56ab9f10-2c8e-4771-a591-38b0f2f9383c": { + "type": "ActionBmwCodeCell", + "options": { + "label": "TOOL", + "bciAvId": 17755, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "country-f4628dd3-8836-4a28-b5c6-05c83b63c4bb": { + "type": "ActionBmwCodeCell", + "options": { + "label": "COUNTRY", + "bciAvId": 13424, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "limitime-030b7120-7cd9-47ba-ab91-756771cb8fe1": { + "type": "ActionBmwCodeCell", + "options": { + "label": "LIMITIME", + "bciAvId": 15212, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "year-8445112d-dbe9-4daa-b569-7c617cab962e": { + "type": "ActionBmwCodeCell", + "options": { + "label": "YEAR", + "bciAvId": 18289, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "month-374ec467-3a8a-4558-9d48-9bf8480d2389": { + "type": "ActionBmwCodeCell", + "options": { + "label": "MONTH", + "bciAvId": 15649, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "day-150d0564-f1c3-42e0-8ca3-cb4d108706bb": { + "type": "ActionBmwCodeCell", + "options": { + "label": "DAY", + "bciAvId": 13639, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "many-8ff9aa07-a2b4-42b8-91de-d7351012fa1f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "MANY", + "bciAvId": 15671, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "part-3116ee09-2758-4f94-9a2e-f89b791750ec": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PART", + "bciAvId": 15972, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "relation-26190847-5c06-4de8-8de0-21e3a5a5df8a": { + "type": "ActionBmwCodeCell", + "options": { + "label": "RELATION", + "bciAvId": 16479, + "rowStart": 5, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "you-cc2e5d39-d4f6-4abf-9142-dc5c9c9c74f9": { + "type": "ActionBmwCodeCell", + "options": { + "label": "YOU+", + "bciAvId": 18465, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "they-07e23d12-122d-4447-84e1-243e064b4f4a": { + "type": "ActionBmwCodeCell", + "options": { + "label": "THEY+", + "bciAvId": 17713, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "receive-bfd58238-a5fd-4bf2-9c99-ecfc459c8004": { + "type": "ActionBmwCodeCell", + "options": { + "label": "RECEIVE", + "bciAvId": 14435, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "write-b72cac4d-78bd-4991-af10-d7c7de3db8fa": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WRITE", + "bciAvId": 18285, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "eye-655c4dda-4c5c-4bc5-8eb8-fd559543426e": { + "type": "ActionBmwCodeCell", + "options": { + "label": "EYE", + "bciAvId": 14133, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "repeat-4c74e357-ad31-4a36-b400-2b01bb5e1827": { + "type": "ActionBmwCodeCell", + "options": { + "label": "REPEAT", + "bciAvId": 16487, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "water-b1e5d4b7-755e-4558-83d9-50229ab32674": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WATER", + "bciAvId": 18209, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "weight-724643a6-e078-4f3e-bdb8-7cce2fec7f59": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WEIGHT", + "bciAvId": 18221, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "put-a3fcf40c-31a2-4e40-81e1-a427871c33d8": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PUT", + "bciAvId": 16440, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "food-d68a7160-bddb-4dec-8489-555695ac9bb7": { + "type": "ActionBmwCodeCell", + "options": { + "label": "FOOD", + "bciAvId": 14377, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "without-6e88e3d7-aa0b-4cfb-888d-27b8f35a8893": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WITHOUT", + "bciAvId": 15474, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "also-111a5418-17ef-45aa-9df3-289f968f7ac4": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ALSO", + "bciAvId": 12374, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "equal-9fa1dbe5-dd59-447a-bc3e-9bb153aa21dd": { + "type": "ActionBmwCodeCell", + "options": { + "label": "EQUAL", + "bciAvId": 16713, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "opposite-c46147ad-225a-40aa-8626-cc574e1534c4": { + "type": "ActionBmwCodeCell", + "options": { + "label": "OPPOSITE", + "bciAvId": 15927, + "rowStart": 6, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "he-b81d1d0b-70b8-438d-af6c-713d92b9e9b8": { + "type": "ActionBmwCodeCell", + "options": { + "label": "HE+", + "bciAvId": 14687, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "preverb-2f077d35-8c0c-4157-b7cb-f60f9c8f7fe5": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PREVERB", + "bciAvId": [ + 13867, + "/", + 12335 + ], + "rowStart": 7, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "quiet-8ef79523-7f29-4846-8009-0631a287d300": { + "type": "ActionBmwCodeCell", + "options": { + "label": "QUIET", + "bciAvId": 16447, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "want-381f0b43-d06a-4995-aa4f-694b0a9acef6": { + "type": "ActionBmwCodeCell", + "options": { + "label": "WANT", + "bciAvId": 18035, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "electric-e926338e-8c28-47cc-98cc-589701c9455f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ELECTRIC", + "bciAvId": 13918, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "religion-ff210bf2-dff7-45d4-a3cd-2e0054d38fdf": { + "type": "ActionBmwCodeCell", + "options": { + "label": "RELIGION", + "bciAvId": 16483, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "think-9202f637-5168-4dad-91d4-5937d5a64af7": { + "type": "ActionBmwCodeCell", + "options": { + "label": "THINK", + "bciAvId": 17718, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "young-9bf0bc8f-f3cd-4b4b-8a22-39bbf851d2dc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "YOUNG", + "bciAvId": 18467, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "umbrella-3b2f3668-a2f8-4ca0-a2c0-43531c3bc257": { + "type": "ActionBmwCodeCell", + "options": { + "label": "UMBRELLA", + "bciAvId": 17966, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "if-bce236a3-7fa2-427d-b94f-21b88972d3e6": { + "type": "ActionBmwCodeCell", + "options": { + "label": "IF", + "bciAvId": 14927, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "outworld-aab99745-4394-4931-9184-1e7b94936122": { + "type": "ActionBmwCodeCell", + "options": { + "label": "OUTWORLD", + "bciAvId": 15411, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "past-f71b570d-9d13-45d9-ae84-6cc8a3f78fb1": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PAST", + "bciAvId": 15975, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "present-d0202e43-5833-4368-ae67-ae19979a5345": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PRESENT", + "bciAvId": 16246, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "future-0922f3e1-0b4f-442b-8676-7f08b1fb95fa": { + "type": "ActionBmwCodeCell", + "options": { + "label": "FUTURE", + "bciAvId": 14417, + "rowStart": 7, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "she-c2d41bd5-1241-4ecb-8325-4ee952f61c4b": { + "type": "ActionBmwCodeCell", + "options": { + "label": "SHE+", + "bciAvId": 16949, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "prevrb-s-db5032f7-85a0-4aa6-a0f7-a1d88e6b0b81": { + "type": "ActionBmwCodeCell", + "options": { + "label": "PREVRB+S", + "bciAvId": [ + 13867, + "/", + 12335 + ], + "rowStart": 8, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "am-be-170ca4ff-a27d-4415-90e5-f455284e2081": { + "type": "ActionBmwCodeCell", + "options": { + "label": "AM/BE", + "bciAvId": 12639, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "stop-49139386-f90e-4090-a583-2238b2800ab6": { + "type": "ActionBmwCodeCell", + "options": { + "label": "STOP", + "bciAvId": 17268, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "directn-39b27a98-b4c3-4e05-8e53-5920fe2fc350": { + "type": "ActionBmwCodeCell", + "options": { + "label": "DIRECTN", + "bciAvId": 13682, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "forgive-b7064cbe-b9ae-426a-a1ff-3734e83dcb01": { + "type": "ActionBmwCodeCell", + "options": { + "label": "FORGIVE", + "bciAvId": 14388, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "go-de09bb6d-5fc3-468f-a229-899c24191a43": { + "type": "ActionBmwCodeCell", + "options": { + "label": "GO", + "bciAvId": 14449, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "have-cbb5a678-337d-4950-9903-548237aaac7f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "HAVE", + "bciAvId": 14685, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "journey-d812c8a6-e44a-4a61-b695-99e7af3a53cd": { + "type": "ActionBmwCodeCell", + "options": { + "label": "JOURNEY", + "bciAvId": 17779, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "king-7c83c207-c8e2-4f0a-bd94-9324034e895f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "KING", + "bciAvId": [ + 15416, + "/", + 16420, + ";", + 16420, + ":", + "8,0", + ";", + 14960, + ";", + 14960, + ":", + "16,0", + ";", + 13901, + ";", + 13901, + ":", + "8,0" + ], + "rowStart": 8, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "love-a55485c2-1989-442b-9225-ad1e3a554f4f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "LOVE", + "bciAvId": 15399, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "house-e477a5a7-d5f7-4797-beaf-7cb789150400": { + "type": "ActionBmwCodeCell", + "options": { + "label": "HOUSE", + "bciAvId": 14905, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "return-c8f52313-0e9a-43ec-821d-584c0fdb2e0f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "RETURN", + "bciAvId": 16500, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "legsfeet-0ac7f23d-dbb5-4775-a646-218c929e4e2b": { + "type": "ActionBmwCodeCell", + "options": { + "label": "LEGSFEET", + "bciAvId": 15189, + "rowStart": 8, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "it-96774b2c-217d-4d71-8746-40c5db940297": { + "type": "ActionBmwCodeCell", + "options": { + "label": "IT+", + "bciAvId": 14960, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "no-1e85a462-437c-4212-9462-d94d015d939b": { + "type": "ActionBmwCodeCell", + "options": { + "label": "NO", + "bciAvId": 15722, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "dress-4cd2e7c2-dc5f-4440-9400-599f6229b35e": { + "type": "ActionBmwCodeCell", + "options": { + "label": "DRESS", + "bciAvId": 13876, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "zebra-92a6d126-93a8-4826-b743-d9e25ef13de5": { + "type": "ActionBmwCodeCell", + "options": { + "label": "ZEBRA", + "bciAvId": 18474, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "exclaim-ec8acaa0-6a94-4f38-85fb-0a3f25ddf4ff": { + "type": "ActionBmwCodeCell", + "options": { + "label": "EXCLAIM", + "bciAvId": 14947, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "can-5c6e8089-4849-48ff-9024-927b9ea85593": { + "type": "ActionBmwCodeCell", + "options": { + "label": "CAN", + "bciAvId": 13114, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "value-92e354b2-c941-45cb-879c-39a4c9fbb225": { + "type": "ActionBmwCodeCell", + "options": { + "label": "VALUE", + "bciAvId": 13949, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "but-a746b2e8-d321-4dba-975a-480ea545bf9e": { + "type": "ActionBmwCodeCell", + "options": { + "label": "BUT", + "bciAvId": 13094, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + }, + "near-e8df8d9f-acfa-4e58-9649-19a3fa3b2333": { + "type": "ActionBmwCodeCell", + "options": { + "label": "NEAR", + "bciAvId": 15697, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 9, + "columnSpan": 1 + } + }, + "maybe-10c18ebb-3654-4aba-af71-97020c0140d3": { + "type": "ActionBmwCodeCell", + "options": { + "label": "MAYBE", + "bciAvId": 15436, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 10, + "columnSpan": 1 + } + }, + "money-4476a5e4-7d43-4edd-aa6a-fcc2662bb43b": { + "type": "ActionBmwCodeCell", + "options": { + "label": "MONEY", + "bciAvId": 15484, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 11, + "columnSpan": 1 + } + }, + "furnitur-78175c77-ccf3-4961-8596-f4ab754a8525": { + "type": "ActionBmwCodeCell", + "options": { + "label": "FURNITUR", + "bciAvId": 14416, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 12, + "columnSpan": 1 + } + }, + "yes-53ddefed-df43-4af0-a647-b2ad96dd4d4a": { + "type": "ActionBmwCodeCell", + "options": { + "label": "YES", + "bciAvId": 18294, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 13, + "columnSpan": 1 + } + }, + "say-f2f249a1-e2df-4498-88b2-9320fe166782": { + "type": "ActionBmwCodeCell", + "options": { + "label": "SAY", + "bciAvId": 16728, + "rowStart": 9, + "rowSpan": 1, + "columnStart": 14, + "columnSpan": 1 + } + }, + "air-6ff59c9a-6148-4884-b2f8-53b268684367": { + "type": "ActionBmwCodeCell", + "options": { + "label": "AIR", + "bciAvId": 12356, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "open-d2b253c8-25a4-4f39-b8b0-409367d99712": { + "type": "ActionBmwCodeCell", + "options": { + "label": "OPEN", + "bciAvId": 15921, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "not-8545b4f7-f461-4fa0-8806-1d7ccddf9cae": { + "type": "ActionBmwCodeCell", + "options": { + "label": "NOT", + "bciAvId": 15733, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "finish-45e3435c-29da-45d6-a1e7-360d7bd8f645": { + "type": "ActionBmwCodeCell", + "options": { + "label": "FINISH", + "bciAvId": 14181, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + }, + "cause-d7358d5e-6f4f-4f2b-ae32-be44b7e1329d": { + "type": "ActionBmwCodeCell", + "options": { + "label": "CAUSE", + "bciAvId": 13134, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 5, + "columnSpan": 1 + } + }, + "birth-5c3d5e67-cb44-40b6-ada0-f7769fac5c4f": { + "type": "ActionBmwCodeCell", + "options": { + "label": "BIRTH", + "bciAvId": 12843, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 6, + "columnSpan": 1 + } + }, + "sexual-2abe1fdc-4232-413c-bcbc-9a1e03363071": { + "type": "ActionBmwCodeCell", + "options": { + "label": "SEXUAL", + "bciAvId": 16933, + "rowStart": 10, + "rowSpan": 1, + "columnStart": 7, + "columnSpan": 1 + } + }, + "lang-94779e28-1f3f-41a2-8a0a-cb9d543c4fb3": { + "type": "ActionBmwCodeCell", + "options": { + "label": "LANG.", + "bciAvId": [ + 15172, + "/", + 14883 + ], + "rowStart": 10, + "rowSpan": 1, + "columnStart": 8, + "columnSpan": 1 + } + } + } +} diff --git a/vite.config.ts b/vite.config.ts index 49bed50..22d44aa 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,6 +11,7 @@ export default defineConfig({ devSourcemap: true }, build: { - sourcemap: true + sourcemap: true, + target: "esnext" } });