UNPKG

@launchmenu/core

Version:

An environment for visual keyboard controlled applets

207 lines 17.4 kB
"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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