From 7105026110dcc26d240a182c6429282067654432 Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Mon, 30 Oct 2023 16:09:09 -0400 Subject: [PATCH 01/29] feat: select from a set of palettes --- index.html | 36 ++++++++++++- src/keyboards/myfamily.json | 104 ++++++++++++++++++++++++++++++++++++ src/keyboards/people.json | 104 ++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/keyboards/myfamily.json create mode 100644 src/keyboards/people.json diff --git a/index.html b/index.html index 73d8bae..c67ecf5 100644 --- a/index.html +++ b/index.html @@ -4,10 +4,33 @@ + + Adaptive Palette

Palette Based on JSON

+

+ + +

diff --git a/src/keyboards/myfamily.json b/src/keyboards/myfamily.json new file mode 100644 index 0000000..0b5b1e5 --- /dev/null +++ b/src/keyboards/myfamily.json @@ -0,0 +1,104 @@ +{ + "name": "My Family Palette", + "cells": { + "aunt-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Aunt", + "bciAvId": 12596, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "mother-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Mother", + "bciAvId": 15662, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "cousinF-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Cousin", + "bciAvId": 25280, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "sister-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Sister", + "bciAvId": 16994 , + "rowStart": 2, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "me-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Me", + "bciAvId": 14916, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "brother-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Brother", + "bciAvId": 12909, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "cousinM-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Cousin", + "bciAvId": 25279, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "father-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Father", + "bciAvId": 14157, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "uncle-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Father", + "bciAvId": 17967, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + } + } +} diff --git a/src/keyboards/people.json b/src/keyboards/people.json new file mode 100644 index 0000000..0fcce15 --- /dev/null +++ b/src/keyboards/people.json @@ -0,0 +1,104 @@ +{ + "name": "Peole", + "cells": { + "couple-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Couple", + "bciAvId": 13426, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "woman-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Woman", + "bciAvId": 18269, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "assistant-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Assistant", + "bciAvId": 14706, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "girl-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Girl", + "bciAvId": 14439 , + "rowStart": 2, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "person-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Person", + "bciAvId": 16161, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "boy-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Boy", + "bciAvId": 12888, + "rowStart": 2, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "crowd-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Crowd", + "bciAvId": 14646, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "man-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Man", + "bciAvId": 15416, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "police-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBmwCodeCell", + "options": { + "label": "Police", + "bciAvId": 16217, + "rowStart": 3, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + } + } +} From 6f25a5fb8d6f797bcb706255c8ad18b159644ed0 Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Tue, 31 Oct 2023 13:57:45 -0400 Subject: [PATCH 02/29] fix: palette json definition files for "people" and "myfamily" --- src/keyboards/myfamily.json | 2 +- src/keyboards/people.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/keyboards/myfamily.json b/src/keyboards/myfamily.json index 0b5b1e5..b439d2b 100644 --- a/src/keyboards/myfamily.json +++ b/src/keyboards/myfamily.json @@ -92,7 +92,7 @@ "uncle-30a32c78-56fe-4622-9fba-0416b68d72fc": { "type": "ActionBmwCodeCell", "options": { - "label": "Father", + "label": "Uncle", "bciAvId": 17967, "rowStart": 3, "rowSpan": 1, diff --git a/src/keyboards/people.json b/src/keyboards/people.json index 0fcce15..ef73f7e 100644 --- a/src/keyboards/people.json +++ b/src/keyboards/people.json @@ -85,7 +85,7 @@ "bciAvId": 15416, "rowStart": 3, "rowSpan": 1, - "columnStart": 3, + "columnStart": 2, "columnSpan": 1 } }, @@ -96,7 +96,7 @@ "bciAvId": 16217, "rowStart": 3, "rowSpan": 1, - "columnStart": 2, + "columnStart": 3, "columnSpan": 1 } } From f7530a86e4c3a05ff4b26a8b5f52a2a2c481227b Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Mon, 13 Nov 2023 16:50:55 -0500 Subject: [PATCH 03/29] feat: added main "palette of palettes" for choosing other palettes --- index.html | 11 ++++++++++- src/GlobalData.ts | 8 ++++++-- src/Palette.ts | 6 +++++- src/keyboards/people.json | 2 +- 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index c67ecf5..be1aca3 100644 --- a/index.html +++ b/index.html @@ -23,14 +23,19 @@

Palette Based on JSON

+

Palette Chooser

+
+
+
+
Adaptive Palette @@ -40,14 +32,14 @@

Palette Chooser

- Adaptive Palette

Palette Based on JSON

Palette Chooser

-

- - -

-

- Note: the symbol for "singing" does not select any other palette. It - appears here only as a test of using the SVGBuilder to create the symbol. +

Select a palette: (Note: the symbol for "singing" does not select any + other palette. It appears here only as a test of using the SVGBuilder + to create the symbol.

@@ -34,31 +19,7 @@

Palette Chooser

diff --git a/src/GlobalData.ts b/src/GlobalData.ts index c24f9a4..aafb1cd 100644 --- a/src/GlobalData.ts +++ b/src/GlobalData.ts @@ -55,7 +55,8 @@ export async function initAdaptivePaletteGlobals () { adaptivePaletteGlobals.blissaryIdMap = await loadBlissaryIdMap(); } -// TODO: possibly move this function into the PaletteStore object +// TODO: possibly move this function into the PaletteStore object, OR just +// use "import path/to/json/file.json". export async function getPaletteJson (jsonFile) { const response = await fetch(jsonFile); console.debug("response is '" + ( response ? "non-null" : "null" )); diff --git a/src/index.js b/src/index.js index dd28c4a..6309ecd 100644 --- a/src/index.js +++ b/src/index.js @@ -17,6 +17,11 @@ import "./index.scss"; await initAdaptivePaletteGlobals(); import { Palette } from "./Palette"; -import bmwJson from "./keyboards/bmw_palette.json"; +// Set up palette for choosing palettes +import paletteChooser from "./keyboards/palettes.json"; +render (html`<${Palette} json=${paletteChooser}/>`, document.getElementById("paletteChooserViaBliss")); + +// Start with the BMW palette +import bmwJson from "./keyboards/bmw_palette.json"; render(html`<${Palette} json=${bmwJson}/>`, document.getElementById("bmwKeyCodes")); diff --git a/src/keyboards/myfamily.json b/src/keyboards/myfamily.json index c7d21db..8ba12a5 100644 --- a/src/keyboards/myfamily.json +++ b/src/keyboards/myfamily.json @@ -40,7 +40,7 @@ "rowStart": 3, "rowSpan": 1, "columnStart": 1, - "columnSpan": 1 + "columnSpan": 5 } }, "mother-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -50,8 +50,8 @@ "bciAvId": 15662, "rowStart": 3, "rowSpan": 1, - "columnStart": 2, - "columnSpan": 1 + "columnStart": 6, + "columnSpan": 5 } }, "cousinF-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -61,8 +61,8 @@ "bciAvId": 25280, "rowStart": 3, "rowSpan": 1, - "columnStart": 3, - "columnSpan": 1 + "columnStart": 11, + "columnSpan": 4 } }, "sister-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -73,7 +73,7 @@ "rowStart": 4, "rowSpan": 1, "columnStart": 1, - "columnSpan": 1 + "columnSpan": 5 } }, "me-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -83,8 +83,8 @@ "bciAvId": 14916, "rowStart": 4, "rowSpan": 1, - "columnStart": 2, - "columnSpan": 1 + "columnStart": 6, + "columnSpan": 5 } }, "brother-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -94,8 +94,8 @@ "bciAvId": 12909, "rowStart": 4, "rowSpan": 1, - "columnStart": 3, - "columnSpan": 1 + "columnStart": 11, + "columnSpan": 4 } }, "cousinM-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -106,7 +106,7 @@ "rowStart": 5, "rowSpan": 1, "columnStart": 1, - "columnSpan": 1 + "columnSpan": 5 } }, "father-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -116,8 +116,8 @@ "bciAvId": 14157, "rowStart": 5, "rowSpan": 1, - "columnStart": 2, - "columnSpan": 1 + "columnStart": 6, + "columnSpan": 5 } }, "uncle-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -127,8 +127,8 @@ "bciAvId": 17967, "rowStart": 5, "rowSpan": 1, - "columnStart": 3, - "columnSpan": 1 + "columnStart": 11, + "columnSpan": 4 } } } diff --git a/src/keyboards/people.json b/src/keyboards/people.json index 8248efc..d539a44 100644 --- a/src/keyboards/people.json +++ b/src/keyboards/people.json @@ -40,7 +40,7 @@ "rowStart": 3, "rowSpan": 1, "columnStart": 1, - "columnSpan": 1 + "columnSpan": 5 } }, "woman-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -50,8 +50,8 @@ "bciAvId": 18269, "rowStart": 3, "rowSpan": 1, - "columnStart": 2, - "columnSpan": 1 + "columnStart": 6, + "columnSpan": 5 } }, "assistant-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -61,8 +61,8 @@ "bciAvId": 14706, "rowStart": 3, "rowSpan": 1, - "columnStart": 3, - "columnSpan": 1 + "columnStart": 11, + "columnSpan": 4 } }, "girl-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -73,7 +73,7 @@ "rowStart": 4, "rowSpan": 1, "columnStart": 1, - "columnSpan": 1 + "columnSpan": 5 } }, "person-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -83,8 +83,8 @@ "bciAvId": 16161, "rowStart": 4, "rowSpan": 1, - "columnStart": 2, - "columnSpan": 1 + "columnStart": 6, + "columnSpan": 5 } }, "boy-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -94,8 +94,8 @@ "bciAvId": 12888, "rowStart": 4, "rowSpan": 1, - "columnStart": 3, - "columnSpan": 1 + "columnStart": 11, + "columnSpan": 4 } }, "crowd-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -106,7 +106,7 @@ "rowStart": 5, "rowSpan": 1, "columnStart": 1, - "columnSpan": 1 + "columnSpan": 5 } }, "man-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -116,8 +116,8 @@ "bciAvId": 15416, "rowStart": 5, "rowSpan": 1, - "columnStart": 2, - "columnSpan": 1 + "columnStart": 6, + "columnSpan": 5 } }, "police-30a32c78-56fe-4622-9fba-0416b68d72fc": { @@ -127,8 +127,8 @@ "bciAvId": 16217, "rowStart": 5, "rowSpan": 1, - "columnStart": 3, - "columnSpan": 1 + "columnStart": 11, + "columnSpan": 4 } } } From 9555c7929c684b87d9f7ba6613cc50e71476bcbd Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Tue, 30 Jan 2024 17:11:37 -0500 Subject: [PATCH 10/29] feat: implement basic navigation/layer tracking with back button --- index.html | 7 +-- src/ActionBranchToPalette.ts | 12 +++-- src/CommandGoBackCell.ts | 80 +++++++++++++++++++++++++++ src/GlobalData.ts | 9 +++- src/NavigationStack.ts | 102 +++++++++++++++++++++++++++++++++++ src/index.js | 5 +- src/keyboards/palettes.json | 52 ++++++++++++++++++ 7 files changed, 253 insertions(+), 14 deletions(-) create mode 100644 src/CommandGoBackCell.ts create mode 100644 src/NavigationStack.ts create mode 100644 src/keyboards/palettes.json diff --git a/index.html b/index.html index ba44767..429f466 100644 --- a/index.html +++ b/index.html @@ -9,15 +9,12 @@

Palette Based on JSON

Palette Chooser

-

Select a palette: (Note: the symbol for "singing" does not select any - other palette. It appears here only as a test of using the SVGBuilder - to create the symbol. -

+

Select a palette:

-
+
diff --git a/src/ActionBranchToPalette.ts b/src/ActionBranchToPalette.ts index 7a22608..6055eed 100644 --- a/src/ActionBranchToPalette.ts +++ b/src/ActionBranchToPalette.ts @@ -11,7 +11,7 @@ import { render } from "preact"; import { html } from "htm/preact"; -import { BlissCellType } from "./index.d"; +import { BlissSymbolCellType } from "./index.d"; import { adaptivePaletteGlobals, getPaletteJson } from "./GlobalData"; import { Palette } from "./Palette"; import { BlissSymbol } from "./BlissSymbol"; @@ -33,16 +33,15 @@ const paletteNameAndFile = { // TODO: this is identical to `ActionBmwCodeCellPropsType`. Should it be? type ActionBranchToPalettePropsType = { id: string, - options: BlissCellType + options: BlissSymbolCellType }; /* * Event handler for an ActionBranchToPalette button/cell that, when clicked, * finds and renders the palette referenced by this cell. - * TODO: re-write this with Preact's signal system */ const navigateToPalette = async (event) => { - const { paletteStore } = adaptivePaletteGlobals; + const { paletteStore, navigationStack } = adaptivePaletteGlobals; const button = event.currentTarget; speak(button.innerText); @@ -54,7 +53,10 @@ const navigateToPalette = async (event) => { paletteStore.addPalette(paletteDefinition); } if (paletteDefinition) { - render (html`<${Palette} json=${paletteDefinition}/>`, document.getElementById("bmwKeyCodes")); + const mainPaletteDisplayArea = document.getElementById("mainPaletteDisplayArea"); + navigationStack.push(navigationStack.currentPalette); + render (html`<${Palette} json=${paletteDefinition}/>`, mainPaletteDisplayArea); + navigationStack.currentPalette = paletteDefinition; } else { console.error(`navigateToPalette(): Unable to locate the palette definition for ${branchToPaletteName}`); diff --git a/src/CommandGoBackCell.ts b/src/CommandGoBackCell.ts new file mode 100644 index 0000000..f7d0a68 --- /dev/null +++ b/src/CommandGoBackCell.ts @@ -0,0 +1,80 @@ +/* + * 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 { BlissSymbolCellType } from "./index.d"; +import { adaptivePaletteGlobals, getPaletteJson } from "./GlobalData"; +import { Palette } from "./Palette"; +import { BlissSymbol } from "./BlissSymbol"; +import { speak } from "./GlobalUtils"; +import "./ActionBmwCodeCell.scss"; + +// Msp of palette name and their files. TODO: put this in a better place, +// GlobalData? +const paletteNameAndFile = { + "My Family Palette": "./src/keyboards/myfamily.json", + "People": "./src/keyboards/people.json", + "BMW Palette": "./src/keyboards/bmw_palette.json" +}; + +// TODO: this is identical to `ActionBmwCodeCellPropsType`. Should it be? +type CommandGoBackCellPropsType = { + id: string, + options: BlissSymbolCellType +}; + +/* + * Event handler for an CommandGoBackCellPropsType button/cell that, when clicked, + * goes back one palette. + */ +const goBackToPalette = async (event) => { + const { paletteStore, navigationStack } = adaptivePaletteGlobals; + const button = event.currentTarget; + speak(button.innerText); + + const paletteToGoBackTo = navigationStack.peek(); + let paletteDefinition = paletteStore.getNamedPalette(paletteToGoBackTo); + if (!paletteDefinition) { + const paletteFile = paletteNameAndFile[paletteToGoBackTo.name]; + paletteDefinition = await getPaletteJson(`${paletteFile}`); + paletteStore.addPalette(paletteDefinition); + } + if (paletteDefinition) { + navigationStack.pop(); + const mainPaletteDisplayArea = document.getElementById("mainPaletteDisplayArea"); + render (html`<${Palette} json=${paletteDefinition}/>`, mainPaletteDisplayArea); + navigationStack.currentPalette = paletteDefinition; + } + else { + console.error(`goBackToPalette(): Unable to locate the palette definition for ${paletteToGoBackTo}`); + } +}; + +export function CommandGoBackCell (props: CommandGoBackCellPropsType) { + + 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/GlobalData.ts b/src/GlobalData.ts index 07d5d56..d887147 100644 --- a/src/GlobalData.ts +++ b/src/GlobalData.ts @@ -23,28 +23,33 @@ import { EncodingType } from "./index.d"; */ import { ActionBmwCodeCell } from "./ActionBmwCodeCell"; import { ActionBranchToPalette } from "./ActionBranchToPalette"; +import { CommandGoBackCell } from "./CommandGoBackCell"; import { ContentBmwEncoding } from "./ContentBmwEncoding"; import { CommandClearEncoding } from "./CommandClearEncoding"; import { CommandDelLastEncoding } from "./CommandDelLastEncoding"; import { PaletteStore } from "./PaletteStore"; +import { NavigationStack } from "./NavigationStack"; export const cellTypeRegistry = { "ActionBmwCodeCell": ActionBmwCodeCell, "ActionBranchToPalette": ActionBranchToPalette, + "CommandGoBackCell": CommandGoBackCell, "ContentBmwEncoding": ContentBmwEncoding, "CommandClearEncoding": CommandClearEncoding, "CommandDelLastEncoding": CommandDelLastEncoding }; /** - * Load the map between the BCI-AV IDs and the code consumed by the Bliss SVG builder + * Load the map between the BCI-AV IDs and the code consumed by the Bliss SVG + * and create the PaletterStore and NavigationStack objects. */ 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, - paletteStore: new PaletteStore() + paletteStore: new PaletteStore(), + navigationStack: new NavigationStack() }; export async function loadBlissaryIdMap () { diff --git a/src/NavigationStack.ts b/src/NavigationStack.ts new file mode 100644 index 0000000..089766b --- /dev/null +++ b/src/NavigationStack.ts @@ -0,0 +1,102 @@ +/* + * Copyright 2024 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 { JsonPaletteType } from "./index.d"; + +export class NavigationStack { + + // The actual stack keeping track of where the user was. The top-most palette + // is the most recent one the user was at before navigating to a new + // layer/palette. + navigateBackStack: Array; + + // The current palette in the palette display area + currPalette: JsonPaletteType; + + /** + * Initialize the navigation stack to have zero entries. + */ + constructor() { + this.navigateBackStack = []; + } + + /** + * Report if the navigation stack is empty. + * @return: `true` if the stack is empty; `false` otherwise. + */ + isEmpty () { + return this.navigateBackStack.length === 0; + } + + /** + * Puah a palette onto the top of the navigation stack. + * @param: {Palette} palette - The palette to push. If `null` or `undefined`, + * the navigation stack is left untouched. + */ + push (palette: JsonPaletteType) { + if (!palette) { + return; + } + this.navigateBackStack.push(palette); + } + + /** + * Pop and return the most recently pushed palette from the top of the + * navigation stack. + * @return {Palette} - reference to the popped palette; null if the stack is + * empty. + */ + pop () { + if (this.isEmpty()) { + return null; + } else { + return this.navigateBackStack.pop(); + } + } + + /** + * Return the palette at the top of the stack without changing the stack it. + * If an index is given, the palette at that index is returned. Note that an + * index of zero denotes the top of the stack. + * @param {integer} stackIndex - Optional: How far down the stack to peek, + * where zero is the top of the stack (default). + * If out of range, `null` is returned. + * @return {Palette} - Reference to the palette at the top of the stack or at + * the given index; null if no palette is available at the + * the given index. + */ + peek (stackIndex:number = 0) { + // Flip the index value since Array.push() puts the item at the end + // of the array. + const index = (this.navigateBackStack.length - stackIndex) - 1; + let palette = null; + if (index >= 0) { + palette = this.navigateBackStack[index]; + } + return palette; + } + + /** + * Accessor for setting the currently displayed palette. + * @return: {JsonPaletteType} - the current palette. + */ + set currentPalette (palette: JsonPaletteType) { + this.currPalette = palette; + } + + /** + * Accessor for getting the currently displayed palette. + * @return: {JsonPaletteType} - The current palette. + */ + get currentPalette(): JsonPaletteType { + return this.currPalette; + } +} diff --git a/src/index.js b/src/index.js index 6309ecd..b02d353 100644 --- a/src/index.js +++ b/src/index.js @@ -10,7 +10,7 @@ */ import { render } from "preact"; import { html } from "htm/preact"; -import { initAdaptivePaletteGlobals } from "./GlobalData"; +import { initAdaptivePaletteGlobals, adaptivePaletteGlobals} from "./GlobalData"; import "./index.scss"; // Initialize any globals used elsewhere in the code. @@ -24,4 +24,5 @@ render (html`<${Palette} json=${paletteChooser}/>`, document.getElementById("pal // Start with the BMW palette import bmwJson from "./keyboards/bmw_palette.json"; -render(html`<${Palette} json=${bmwJson}/>`, document.getElementById("bmwKeyCodes")); +adaptivePaletteGlobals.navigationStack.currentPalette = bmwJson; +render(html`<${Palette} json=${bmwJson}/>`, document.getElementById("mainPaletteDisplayArea")); diff --git a/src/keyboards/palettes.json b/src/keyboards/palettes.json new file mode 100644 index 0000000..893994a --- /dev/null +++ b/src/keyboards/palettes.json @@ -0,0 +1,52 @@ +{ + "name": "Palettes", + "cells": { + "myfamily-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBranchToPalette", + "options": { + "label": "My Family", + "branchTo": "My Family Palette", + "bciAvId": 20572, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 1, + "columnSpan": 1 + } + }, + "people-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBranchToPalette", + "options": { + "label": "People", + "branchTo": "People", + "bciAvId": 16161, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 2, + "columnSpan": 1 + } + }, + "bmw-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "ActionBranchToPalette", + "options": { + "label": "BMW", + "branchTo": "BMW Palette", + "bciAvId": 1, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 3, + "columnSpan": 1 + } + }, + "back-up-30a32c78-56fe-4622-9fba-0416b68d72fc": { + "type": "CommandGoBackCell", + "options": { + "label": "Back Up", + "bciAvId": 12612, + "rowStart": 1, + "rowSpan": 1, + "columnStart": 4, + "columnSpan": 1 + } + } + } +} From 15b899f8d68b95553dfd635fdf8032317e4af399 Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Thu, 1 Feb 2024 15:47:39 -0500 Subject: [PATCH 11/29] fix: change style class of "back button" to be a command style --- src/CommandGoBackCell.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CommandGoBackCell.ts b/src/CommandGoBackCell.ts index f7d0a68..b848175 100644 --- a/src/CommandGoBackCell.ts +++ b/src/CommandGoBackCell.ts @@ -72,7 +72,7 @@ export function CommandGoBackCell (props: CommandGoBackCellPropsType) { return html` From a7c6d835eefe901a0f9f855da1d9d9100fce4da9 Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Mon, 5 Feb 2024 11:24:43 -0500 Subject: [PATCH 12/29] feat: unit tests for basic navigation/layer tracking --- src/ActionBranchToPalette.test.ts | 65 +++++++++++++++ src/ActionBranchToPalette.ts | 10 +-- src/CommandGoBackCell.test.ts | 64 ++++++++++++++ src/CommandGoBackCell.ts | 9 +- src/NavigationStack.test.ts | 134 ++++++++++++++++++++++++++++++ src/NavigationStack.ts | 46 +++++++--- 6 files changed, 305 insertions(+), 23 deletions(-) create mode 100644 src/ActionBranchToPalette.test.ts create mode 100644 src/CommandGoBackCell.test.ts create mode 100644 src/NavigationStack.test.ts diff --git a/src/ActionBranchToPalette.test.ts b/src/ActionBranchToPalette.test.ts new file mode 100644 index 0000000..65f2245 --- /dev/null +++ b/src/ActionBranchToPalette.test.ts @@ -0,0 +1,65 @@ +/* + * Copyright 2024 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 { ActionBranchToPalette } from "./ActionBranchToPalette"; + +describe("ActionBranchToPalette render tests", () => { + + const TEST_CELL_ID = "uuid-of-some-kind"; + const goToPaletteCell = { + options: { + "label": "Animals", + "branchTo": "Animals", + "rowStart": "100", + "rowSpan": "12", + "columnStart": "33", + "columnSpan": "11", + "bciAvId": [ 16161, "/", 9011 ] + } + }; + + beforeAll(async () => { + await initAdaptivePaletteGlobals(); + }); + + test("ActionBranchToPalette rendering", async () => { + + render(html` + <${ActionBranchToPalette} + id="${TEST_CELL_ID}" + options=${goToPaletteCell.options} + />` + ); + + // Check the rendered cell + const button = await screen.findByRole("button", {name: goToPaletteCell.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("actionBranchToPaletteCell"); + expect(button.textContent).toBe(goToPaletteCell.options.label); + + // Check the grid cell styles. + expect(button.style["grid-column"]).toBe(`${goToPaletteCell.options.columnStart} / span ${goToPaletteCell.options.columnSpan}`); + expect(button.style["grid-row"]).toBe(`${goToPaletteCell.options.rowStart} / span ${goToPaletteCell.options.rowSpan}`); + + // Check disabled state (should be enabled) + expect(button.getAttribute("disabled")).toBe(null); + }); +}); diff --git a/src/ActionBranchToPalette.ts b/src/ActionBranchToPalette.ts index 6055eed..12e3473 100644 --- a/src/ActionBranchToPalette.ts +++ b/src/ActionBranchToPalette.ts @@ -16,11 +16,7 @@ import { adaptivePaletteGlobals, getPaletteJson } from "./GlobalData"; import { Palette } from "./Palette"; import { BlissSymbol } from "./BlissSymbol"; import { speak } from "./GlobalUtils"; -import "./ActionBmwCodeCell.scss"; - -function debugProps(x) { - console.debug("DEBUGPROPS(): %O", x); -} +import "./ActionBranchToPalette.scss"; // Msp of palette name and their files. TODO: put this in a better place, // GlobalData? @@ -64,8 +60,6 @@ const navigateToPalette = async (event) => { }; export function ActionBranchToPalette (props: ActionBranchToPalettePropsType) { - debugProps(props); - const { columnStart, columnSpan, rowStart, rowSpan, branchTo, bciAvId, label } = props.options; @@ -77,7 +71,7 @@ export function ActionBranchToPalette (props: ActionBranchToPalettePropsType) { return html` diff --git a/src/CommandGoBackCell.test.ts b/src/CommandGoBackCell.test.ts new file mode 100644 index 0000000..f76af1e --- /dev/null +++ b/src/CommandGoBackCell.test.ts @@ -0,0 +1,64 @@ +/* + * Copyright 2024 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 { CommandGoBackCell } from "./CommandGoBackCell"; + +describe("ActionBmwCodeDell render tests", () => { + + const TEST_CELL_ID = "uuid-of-some-kind"; + const goBackCell = { + options: { + "label": "Back Up", + "rowStart": "3", + "rowSpan": "2", + "columnStart": "2", + "columnSpan": "1", + "bciAvId": 12612 + } + }; + + beforeAll(async () => { + await initAdaptivePaletteGlobals(); + }); + + test("CommandGoBackCell rendering", async () => { + + render(html` + <${CommandGoBackCell} + id="${TEST_CELL_ID}" + options=${goBackCell.options} + />` + ); + + // Check the rendered cell + const button = await screen.findByRole("button", {name: goBackCell.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("btn-command"); + expect(button.textContent).toBe(goBackCell.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/CommandGoBackCell.ts b/src/CommandGoBackCell.ts index b848175..d675b78 100644 --- a/src/CommandGoBackCell.ts +++ b/src/CommandGoBackCell.ts @@ -49,10 +49,11 @@ const goBackToPalette = async (event) => { paletteStore.addPalette(paletteDefinition); } if (paletteDefinition) { - navigationStack.pop(); - const mainPaletteDisplayArea = document.getElementById("mainPaletteDisplayArea"); - render (html`<${Palette} json=${paletteDefinition}/>`, mainPaletteDisplayArea); - navigationStack.currentPalette = paletteDefinition; + navigationStack.popAndSetCurrent(paletteDefinition); + render ( + html`<${Palette} json=${paletteDefinition}/>`, + document.getElementById("mainPaletteDisplayArea") + ); } else { console.error(`goBackToPalette(): Unable to locate the palette definition for ${paletteToGoBackTo}`); diff --git a/src/NavigationStack.test.ts b/src/NavigationStack.test.ts new file mode 100644 index 0000000..716cce2 --- /dev/null +++ b/src/NavigationStack.test.ts @@ -0,0 +1,134 @@ +/* + * Copyright 2024 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 { NavigationStack } from "./NavigationStack"; + +const testPalette1 = { + "name": "testPalette1", + "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 testPalette2 = { + "name": "DifferentName", + "cells": { + "dummyCell": { + "type": "dummyCellType", + "options": { + "label": "Choreographer", + "bciAvId": 666, + "rowStart": 2, + "rowSpan": 2, + "columnStart": 2, + "columnSpan": 2 + } + } + } +}; + +describe("NavigationStack module - basics", () => { + + const navigation = new NavigationStack(); + + test("Empty NavigationStack", () => { + expect(navigation.isEmpty()).toBe(true); + expect(navigation.currentPalette).toBe(null); + }); + + test("Current palette accessors", () => { + navigation.currentPalette = testPalette1; + expect(navigation.currentPalette).toBe(testPalette1); + navigation.currentPalette = null; + expect(navigation.currentPalette).toBe(null); + }); + + test("Flush and reset the navigation stack", () => { + navigation.flushReset(testPalette2); + expect(navigation.isEmpty()).toBe(true); + expect(navigation.currentPalette).toBe(testPalette2); + }); +}); + +describe("NavigationStack module - pushing and popping", () => { + + const navigation = new NavigationStack(); + + beforeEach (() => { + navigation.flushReset(null); + }); + + test("Non-empty NavigationStack", () => { + navigation.push(testPalette1); + expect(navigation.isEmpty()).toBe(false); + expect(navigation.peek()).toBe(testPalette1); + expect(navigation.currentPalette).toBe(null); + }); + + test("Pop the top of the stack", () => { + navigation.push(testPalette1); + const topPalette = navigation.pop(); + expect(topPalette).toBe(testPalette1); + expect(navigation.isEmpty()).toBe(true); + // The current palette should be unaffected by a pop operation. + expect(navigation.currentPalette).toBe(null); + }); + + test("Multiple layers and a current palette", () => { + navigation.push(testPalette1); + navigation.push(testPalette2); + navigation.currentPalette = testPalette1; + expect(navigation.isEmpty()).toBe(false); + expect(navigation.peek()).toBe(testPalette2); + expect(navigation.peek(1)).toBe(testPalette1); + expect(navigation.currentPalette).toBe(testPalette1); + }); + + test("Check invalid peek()", () => { + navigation.push(testPalette1); + navigation.push(testPalette2); + expect(navigation.isEmpty()).toBe(false); + expect(navigation.peek(-1)).toBe(undefined); + expect(navigation.peek(1024)).toBe(undefined); + }); + + test("Check pop and set current utility function", () => { + navigation.currentPalette = testPalette1; + navigation.push(testPalette1); + navigation.push(testPalette2); + const poppedPalette = navigation.popAndSetCurrent(testPalette2); + expect(poppedPalette).toBe(testPalette2); + expect(navigation.peek()).toBe(testPalette1); + expect(navigation.currentPalette).toBe(testPalette2); + }); + +}); diff --git a/src/NavigationStack.ts b/src/NavigationStack.ts index 089766b..2ca4f9b 100644 --- a/src/NavigationStack.ts +++ b/src/NavigationStack.ts @@ -26,6 +26,7 @@ export class NavigationStack { */ constructor() { this.navigateBackStack = []; + this.currPalette = null; } /** @@ -38,8 +39,9 @@ export class NavigationStack { /** * Puah a palette onto the top of the navigation stack. - * @param: {Palette} palette - The palette to push. If `null` or `undefined`, - * the navigation stack is left untouched. + * @param: {JsonPaletteType} palette - The palette to push. If `null` or + * `undefined`, the navigation stack is + * left untouched. */ push (palette: JsonPaletteType) { if (!palette) { @@ -51,7 +53,7 @@ export class NavigationStack { /** * Pop and return the most recently pushed palette from the top of the * navigation stack. - * @return {Palette} - reference to the popped palette; null if the stack is + * @return {JsonPaletteType} - reference to the popped palette; null if the stack is * empty. */ pop () { @@ -63,27 +65,49 @@ export class NavigationStack { } /** - * Return the palette at the top of the stack without changing the stack it. - * If an index is given, the palette at that index is returned. Note that an - * index of zero denotes the top of the stack. + * Return the palette at the top of the stack without changing the stack + * tself. If an index is given, the palette at that index is returned. Note + * that an index of zero denotes the top of the stack. * @param {integer} stackIndex - Optional: How far down the stack to peek, * where zero is the top of the stack (default). * If out of range, `null` is returned. - * @return {Palette} - Reference to the palette at the top of the stack or at - * the given index; null if no palette is available at the - * the given index. + * @return {JsonPaletteType} - Reference to the palette at the top of the + * stack or at the given index; undefine if no + * the given stack index is invalid -- negative or + * greater than the size of the stack. */ peek (stackIndex:number = 0) { // Flip the index value since Array.push() puts the item at the end - // of the array. + // of the array. If `stackIndex` is zero or negative, + let palette = undefined; const index = (this.navigateBackStack.length - stackIndex) - 1; - let palette = null; if (index >= 0) { palette = this.navigateBackStack[index]; } return palette; } + /** + * Pop/return the most recently pushed palette and set the currently displayed + * palette to the given one. + * @param {JsonPaletteType} - The palette that is currently displayed, or + * is about to be displayed. + * @return {JsonPaletteType} - The most recently visited palette. + */ + popAndSetCurrent (currentPalette: JsonPaletteType) { + this.currentPalette = currentPalette; + return this.pop(); + } + + /** + * Empty the navigation stack and reset the current palette displayed. + * @param {JsonPaletteType} - The palette that is currently displayed. + */ + flushReset (currentPalette: JsonPaletteType) { + this.currentPalette = currentPalette; + this.navigateBackStack.length = 0; + } + /** * Accessor for setting the currently displayed palette. * @return: {JsonPaletteType} - the current palette. From 8776a1b27de5726930150e2ade326e9a8b1d55dd Mon Sep 17 00:00:00 2001 From: Joseph Scheuhammer Date: Fri, 9 Feb 2024 11:43:55 -0500 Subject: [PATCH 13/29] feat: implement navigation by layers (demo) --- index.html | 12 ++-- src/ActionBranchToPalette.ts | 9 +-- src/CommandGoBackCell.ts | 7 +- src/GlobalData.ts | 12 ++-- src/index.js | 18 +++-- src/keyboards/backup_palette.json | 16 +++++ src/keyboards/myfamily.json | 12 ++++ src/keyboards/palettes.json | 106 ++++++++++++++++++------------ src/keyboards/people.json | 12 ++++ 9 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 src/keyboards/backup_palette.json diff --git a/index.html b/index.html index 429f466..29e6b3d 100644 --- a/index.html +++ b/index.html @@ -7,13 +7,13 @@ Adaptive Palette -

Palette Based on JSON

-

Palette Chooser

-

Select a palette:

-
-
+

Palettes Based on JSON

+
+
+
+
+
-