Skip to content

Commit

Permalink
feat: move backpack to use interface for backpackability (#2314)
Browse files Browse the repository at this point in the history
* feat: move backpack to use interface for backpackability

* chore: PR comments

* fix: nits

* chore: update README

* chore: use version number
  • Loading branch information
BeksOmega authored Apr 10, 2024
1 parent c2abe4d commit 5d699d2
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 6 deletions.
16 changes: 16 additions & 0 deletions plugins/workspace-backpack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ Blockly.Msg['EMPTY_BACKPACK'] = 'Opróżnij plecak'; // Polish
- `addBlock`: Adds Block to backpack.
- `addBlocks`: Adds Blocks to backpack.
- `removeBlock`: Removes Block to backpack.
- `containsBlock`: Returns whether the Block is in the backpack.
- `addBackpackable`: Adds Backpackable to backpack.
- `addBackpackables`: Adds Backpackable to backpack.
- `removeBackpackable`: Removes Backpackable to backpack.
- `containsBackpackable`: Returns whether the Backpackable is in the backpack.
- `addItem`: Adds item to backpack.
- `removeItem`: Removes item from the backpack.
- `setContents`: Sets backpack contents.
Expand Down Expand Up @@ -201,6 +206,17 @@ The Backpack Flyout uses the registered Flyout for either
for `Blockly.Trashcan`. If a custom class is registered for either of these
types, then the Backpack Flyout may need to be tested for compatibility.

### Draggables

If you have a custom draggable object, you can make it possible to add it to
the backpack by implementing the [`Backpackable`][backpackable] interface.

This interface requires the draggable to have a method that converts it into
[`FlyoutItemInfo`][flyout-info]. As of Blockly core v11 flyouts only support displaying blocks, buttons, and labels.

## License

Apache 2.0

[flyout-info]: https://developers.google.com/blockly/reference/js/blockly.utils_namespace.toolbox_namespace.flyoutiteminfo_typealias.md
[backpackable]: https://github.com/google/blockly-samples/blob/master/plugins/workspace-backpack/src/backpack.ts
67 changes: 61 additions & 6 deletions plugins/workspace-backpack/src/backpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as Blockly from 'blockly/core';
import {registerContextMenus} from './backpack_helpers';
import {BackpackOptions, parseOptions} from './options';
import {BackpackChange, BackpackOpen} from './ui_events';
import {Backpackable, isBackpackable} from './backpackable';

/**
* Class for backpack that can be used save blocks from the workspace for
Expand Down Expand Up @@ -453,8 +454,8 @@ export class Backpack
* being dragged.
*/
onDrop(dragElement: Blockly.IDraggable) {
if (dragElement instanceof Blockly.BlockSvg) {
this.addBlock(dragElement);
if (isBackpackable(dragElement)) {
this.addBackpackable(dragElement);
}
}

Expand Down Expand Up @@ -525,7 +526,11 @@ export class Backpack
* provided block.
*/
containsBlock(block: Blockly.Block): boolean {
return this.contents_.indexOf(this.blockToJsonString(block)) !== -1;
if (isBackpackable(block)) {
return this.containsBackpackable(block);
} else {
return this.contents_.indexOf(this.blockToJsonString(block)) !== -1;
}
}

/**
Expand All @@ -534,7 +539,11 @@ export class Backpack
* @param block The block to be added to the backpack.
*/
addBlock(block: Blockly.Block) {
this.addItem(this.blockToJsonString(block));
if (isBackpackable(block)) {
this.addBackpackable(block);
} else {
this.addItem(this.blockToJsonString(block));
}
}

/**
Expand All @@ -544,7 +553,13 @@ export class Backpack
* backpack.
*/
addBlocks(blocks: Blockly.Block[]) {
this.addItems(blocks.map(this.blockToJsonString));
for (const block of blocks) {
if (isBackpackable(block)) {
this.addBackpackable(block);
} else {
this.addItem(this.blockToJsonString(block));
}
}
}

/**
Expand All @@ -553,7 +568,47 @@ export class Backpack
* @param block The block to be removed from the backpack.
*/
removeBlock(block: Blockly.Block) {
this.removeItem(this.blockToJsonString(block));
if (isBackpackable(block)) {
this.removeBackpackable(block);
} else {
this.removeItem(this.blockToJsonString(block));
}
}

/**
* @param backpackable The backpackable we want to check for existence within
* the backpack.
* @return whether the backpack contains a duplicate of the provided
* backpackable.
*/
containsBackpackable(backpackable: Backpackable): boolean {
return backpackable
.toFlyoutInfo()
.every((info) => this.contents_.indexOf(JSON.stringify(info)) !== -1);
}

/**
* @param backpackable The backpackable to add to the backpack.
*/
addBackpackable(backpackable: Backpackable) {
this.addBackpackables([backpackable]);
}

/** @param backpackables The backpackables to add to the backpack. */
addBackpackables(backpackables: Backpackable[]) {
this.addItems(
backpackables
.map((b) => b.toFlyoutInfo())
.reduce((acc, curr) => [...acc, ...curr])
.map((info) => JSON.stringify(info)),
);
}

/** @param backpackable The backpackable to remove from the backpack. */
removeBackpackable(backpackable: Backpackable) {
for (const info of backpackable.toFlyoutInfo()) {
this.removeItem(JSON.stringify(info));
}
}

/**
Expand Down
28 changes: 28 additions & 0 deletions plugins/workspace-backpack/src/backpackable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @license
* Copyright 2024 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import * as Blockly from 'blockly/core';

/** Defines if an object can be added to the backpack. */
export interface Backpackable {
/**
* @return A representation of this object as a FlyoutItemInfo array, so that
* it can be displayed in the backpack.
*
* This method should remove any unique or useless state data (e.g. IDs or
* coordinates).
*/
toFlyoutInfo(): Blockly.utils.toolbox.FlyoutItemInfo[];
}

/**
* @param obj The object we want to check is a Backpackable or not.
* @return Whether the given object conforms to the Backpackable interface.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function isBackpackable(obj: any): obj is Backpackable {
return obj.toFlyoutInfo !== undefined;
}

0 comments on commit 5d699d2

Please sign in to comment.