@launchmenu/core
Version:
An environment for visual keyboard controlled applets
207 lines • 17.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Menu = void 0;
const model_react_1 = require("model-react");
const isItemSelectable_1 = require("../items/isItemSelectable");
const AbstractMenu_1 = require("./AbstractMenu");
const createCallbackHook_1 = require("../../utils/createCallbackHook");
const onMenuChangAction_1 = require("../../actions/types/onMenuChange/onMenuChangAction");
const baseSettings_1 = require("../../application/settings/baseSettings/baseSettings");
const createCategoryGetter_1 = require("./standardConfig/createCategoryGetter");
// TODO: start using the MenuItemCategorizer to separate/offload concerns
/**
* A menu class to control menu items and their state,
* optimized for small item sets.
*/
class Menu extends AbstractMenu_1.AbstractMenu {
constructor(context, items, categoryConfig) {
var _a;
super(context);
// Tracking menu items
this.rawCategories = [{ items: [], category: undefined }];
this.categories = new model_react_1.Field([]);
this.items = new model_react_1.Field([]); // Flat structure containing items (as IMenuItems) and categories headers (as IMenuItems)
let config;
if (items instanceof Array)
config = categoryConfig;
else
config = items;
// Create the category config
const menuSettings = context.settings.get(baseSettings_1.baseSettings).menu;
this.categoryConfig = {
getCategory: (config === null || config === void 0 ? void 0 : config.getCategory) || createCategoryGetter_1.createCategoryGetter(context),
sortCategories: (config === null || config === void 0 ? void 0 : config.sortCategories) ||
(categories => categories.map(({ category }) => category)),
maxCategoryItemCount: (_a = config === null || config === void 0 ? void 0 : config.maxCategoryItemCount) !== null && _a !== void 0 ? _a : menuSettings.maxMenuSize.get(),
};
// Add the default items
if (items instanceof Array)
this.addItems(items);
}
// Item management
/**
* Adds an item to the menu
* @param item The item to add
* @param index The index to add the item at within its category (defaults to the last index; Infinity)
*/
addItem(item, index = Infinity) {
const added = this.addItemWithoutUpdate(item, this.rawCategories, index);
this.updateItemsList();
// Call the menu change listener
if (added)
onMenuChangAction_1.onMenuChangeAction.get([item]).onMenuChange(this, true);
}
/**
* Adds all the items from the given array at once (slightly more efficient than adding one by one)
* @param items The generator to get items from
*/
addItems(items) {
const addedItems = items.filter(item => this.addItemWithoutUpdate(item, this.rawCategories));
this.updateItemsList();
// Call the menu change listener
onMenuChangAction_1.onMenuChangeAction.get(addedItems).onMenuChange(this, true);
}
/**
* Adds an item to the menu without updating the item list
* @param item The item to add
* @param destination The list to add the item to
* @param index The index to add the item at within its category (defaults to the last index; Infinity)
* @returns Added whether the item was added
*/
addItemWithoutUpdate(item, destination, index = Infinity) {
// Create a hook to move the item when the category is updated
const [categoryChangeCallback, destroyHook] = createCallbackHook_1.createCallbackHook(() => {
const categoryData = destination.find(({ category: c }) => c == category);
const inMenu = categoryData === null || categoryData === void 0 ? void 0 : categoryData.items.includes(item);
if (!inMenu)
return;
const categoryChanged = category != this.categoryConfig.getCategory(item);
if (categoryChanged) {
this.removeItems([item], category);
this.addItem(item);
}
else
this.categoryConfig.getCategory(item, categoryChangeCallback);
}, 0); // TODO: store the destroyHook somewhere and call it when item gets removed
// Obtain the category
const category = this.categoryConfig.getCategory(item, categoryChangeCallback);
const categoryIndex = destination.findIndex(({ category: c }) => c == category);
// Add the item to a new or existing category
if (categoryIndex == -1) {
destination.push({ category, items: [item] });
}
else {
const { items } = destination[categoryIndex];
if (items.length >= this.categoryConfig.maxCategoryItemCount)
return false;
items.splice(index, 0, item);
}
return true;
}
/**
* Removes an item from the menu
* @param item The item to remove
* @returns Whether the item was in the menu (and now removed)
*/
removeItem(item) {
return this.removeItems([item]);
}
/**
* Removes all the items from the given array at once (slightly more efficient than removing one by one)
* @param item The item to remove
* @param oldCategory The category that item was in (null to use the items' latest category)
* @returns Whether any item was in the menu (and now removed)
*/
removeItems(items, oldCategory = null) {
let removed = [];
const selectedItems = this.selected.get();
items.forEach(item => {
const category = oldCategory != null ? oldCategory : this.categoryConfig.getCategory(item);
const categoryIndex = this.rawCategories.findIndex(({ category: c }) => c == category);
// Add the item to a new or existing category
if (categoryIndex != -1) {
const { items } = this.rawCategories[categoryIndex];
const index = items.indexOf(item);
if (index != -1) {
items.splice(index, 1);
// Don't remove categories with items or the default category
if (items.length == 0 && category)
this.rawCategories.splice(categoryIndex, 1);
removed.push(item);
// Make sure the item isn't the selected and or cursor item
if (selectedItems.includes(item))
this.setSelected(item, false);
}
}
});
if (removed.length > 0) {
this.updateItemsList();
// Call the menu change listener
onMenuChangAction_1.onMenuChangeAction.get(items).onMenuChange(this, false);
return true;
}
return false;
}
/**
* Synchronizes the item list to be up to date with the categories data
*/
updateItemsList() {
const order = this.categoryConfig.sortCategories(this.rawCategories);
// Combine the items and categories into a single list
const items = [];
const categories = [];
order.forEach(category => {
const categoryData = this.rawCategories.find(({ category: c }) => c == category);
if (categoryData) {
categories.push({ category, items: categoryData.items });
if (category)
items.push(category.item, ...categoryData.items);
else
items.push(...categoryData.items);
}
});
this.categories.set(categories);
this.items.set(items);
this.deselectRemovedCursor();
}
/**
* Checks whether the cursor item is still present, and deselects it if not
*/
deselectRemovedCursor() {
const items = this.items.get();
const cursor = this.cursor.get();
updateCursor: if (cursor == null || !items.includes(cursor)) {
for (let i = 0; i < items.length; i++)
if (isItemSelectable_1.isItemSelectable(items[i])) {
this.setCursor(items[i]);
break updateCursor;
}
this.setCursor(null);
}
}
// Item retrieval
/**
* Retrieves the items of the menu
* @param hook The hook to subscribe to changes
* @returns The menu items
*/
getItems(hook) {
if (this.isDestroyed(hook))
// Whenever the menu is destroyed, we no longer inform about item changes
return this.items.get();
return this.items.get(hook);
}
/**
* Retrieves the item categories of the menu
* @param hook The hook to subscribe to changes
* @returns The categories and their items
*/
getCategories(hook) {
if (this.isDestroyed(hook))
// Whenever the menu is destroyed, we no longer inform about category changes
return this.categories.get();
return this.categories.get(hook);
}
}
exports.Menu = Menu;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTWVudS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9tZW51cy9tZW51L01lbnUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EsNkNBQTZDO0FBRzdDLGdFQUEyRDtBQUUzRCxpREFBNEM7QUFFNUMsdUVBQWtFO0FBQ2xFLDBGQUFzRjtBQUV0Rix1RkFBa0Y7QUFDbEYsZ0ZBQTJFO0FBRTNFLHlFQUF5RTtBQUV6RTs7O0dBR0c7QUFDSCxNQUFhLElBQUssU0FBUSwyQkFBWTtJQTZCbEMsWUFDSSxPQUFtQixFQUNuQixLQUFvRCxFQUNwRCxjQUFvQzs7UUFFcEMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBL0JuQixzQkFBc0I7UUFDWixrQkFBYSxHQUFHLENBQUMsRUFBQyxLQUFLLEVBQUUsRUFBRSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUMsQ0FHeEQsQ0FBQztRQUNNLGVBQVUsR0FBRyxJQUFJLG1CQUFLLENBQUMsRUFBeUIsQ0FBQyxDQUFDO1FBQ2xELFVBQUssR0FBRyxJQUFJLG1CQUFLLENBQUMsRUFBaUIsQ0FBQyxDQUFDLENBQUMseUZBQXlGO1FBMEJySSxJQUFJLE1BQXVDLENBQUM7UUFDNUMsSUFBSSxLQUFLLFlBQVksS0FBSztZQUFFLE1BQU0sR0FBRyxjQUFjLENBQUM7O1lBQy9DLE1BQU0sR0FBRyxLQUFLLENBQUM7UUFFcEIsNkJBQTZCO1FBQzdCLE1BQU0sWUFBWSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLDJCQUFZLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDN0QsSUFBSSxDQUFDLGNBQWMsR0FBRztZQUNsQixXQUFXLEVBQUUsQ0FBQSxNQUFNLGFBQU4sTUFBTSx1QkFBTixNQUFNLENBQUUsV0FBVyxLQUFJLDJDQUFvQixDQUFDLE9BQU8sQ0FBQztZQUNqRSxjQUFjLEVBQ1YsQ0FBQSxNQUFNLGFBQU4sTUFBTSx1QkFBTixNQUFNLENBQUUsY0FBYztnQkFDdEIsQ0FB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