Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
},
"dependencies": {
"@blockly/field-colour": "5.0.5",
"@blockly/field-grid-dropdown": "^5.0.12",
"@blockly/keyboard-experiment": "0.0.1",
"@blockly/plugin-workspace-search": "9.1.0",
"@crowdin/crowdin-api-client": "^1.33.0",
Expand Down
167 changes: 46 additions & 121 deletions pxtblocks/fields/field_imagedropdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

import * as Blockly from "blockly";
import { FieldCustom, FieldCustomDropdownOptions, parseColour } from "./field_utils";
import { FieldDropdown } from "./field_dropdown";
import { FieldGridDropdown } from "@blockly/field-grid-dropdown";

export interface FieldImageDropdownOptions extends FieldCustomDropdownOptions {
columns?: string;
maxRows?: string;
width?: string;
}

export class FieldImageDropdown extends FieldDropdown implements FieldCustom {
export class FieldImageDropdown extends FieldGridDropdown implements FieldCustom {
public isFieldCustom_ = true;
// Width in pixels
protected width_: number;
Expand All @@ -27,7 +27,7 @@ export class FieldImageDropdown extends FieldDropdown implements FieldCustom {
protected savedPrimary_: string;

constructor(text: string, options: FieldImageDropdownOptions, validator?: Function) {
super(options.data);
super(options.data, undefined, {columns: parseInt(options.columns)});

this.columns_ = parseInt(options.columns);
this.maxRows_ = parseInt(options.maxRows) || 0;
Expand All @@ -42,80 +42,47 @@ export class FieldImageDropdown extends FieldDropdown implements FieldCustom {
* @private
*/
public showEditor_() {
// If there is an existing drop-down we own, this is a request to hide the drop-down.
if (Blockly.DropDownDiv.hideIfOwner(this)) {
return;
}
// If there is an existing drop-down someone else owns, hide it immediately and clear it.
Blockly.DropDownDiv.hideWithoutAnimation();
Blockly.DropDownDiv.clearContent();
// Populate the drop-down with the icons for this field.
let dropdownDiv = Blockly.DropDownDiv.getContentDiv() as HTMLElement;
let contentDiv = document.createElement('div');
// Accessibility properties
contentDiv.setAttribute('role', 'menu');
contentDiv.setAttribute('aria-haspopup', 'true');
super.showEditor_();

const dropdownDiv = Blockly.DropDownDiv.getContentDiv() as HTMLElement;
const contentDiv = dropdownDiv.querySelector("div");
const options = this.getOptions();
let maxButtonHeight: number = 0;
for (let i = 0; i < options.length; i++) {
let content = (options[i] as any)[0]; // Human-readable text or image.
const value = (options[i] as any)[1]; // Language-neutral value.
// Icons with the type property placeholder take up space but don't have any functionality
// Use for special-case layouts
if (content.type == 'placeholder') {
let placeholder = document.createElement('span');
placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
placeholder.style.width = content.width + 'px';
placeholder.style.height = content.height + 'px';
contentDiv.appendChild(placeholder);
continue;
}
let button = document.createElement('button');
button.setAttribute('id', ':' + i); // For aria-activedescendant
button.setAttribute('role', 'menuitem');
button.setAttribute('class', 'blocklyDropDownButton');
button.title = content.alt;

contentDiv.querySelectorAll("img").forEach(el => {
el.removeAttribute("height")
el.removeAttribute("width")
});

Array.from(contentDiv.children).forEach((el, i) => {
const content = (options[i] as any)[0]; // Human-readable text or image.
const div = el as HTMLDivElement;
div.classList.add("blocklyDropDownButton");

// We need to find where this occurs and handle it.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And for the field_images.ts file as well.

// if (content.type == 'placeholder') {
// let placeholder = document.createElement('span');
// placeholder.setAttribute('class', 'blocklyDropDownPlaceholder');
// placeholder.style.width = content.width + 'px';
// placeholder.style.height = content.height + 'px';
// contentDiv.appendChild(placeholder);
// continue;
// }

let buttonSize = content.height;
if (this.columns_) {
buttonSize = ((this.width_ / this.columns_) - 8);
button.style.width = buttonSize + 'px';
button.style.height = buttonSize + 'px';
div.style.width = buttonSize + 'px';
div.style.height = buttonSize + 'px';
} else {
button.style.width = content.width + 'px';
button.style.height = content.height + 'px';
div.style.width = content.width + 'px';
div.style.height = content.height + 'px';
}
if (buttonSize > maxButtonHeight) {
maxButtonHeight = buttonSize;
}
let backgroundColor = this.backgroundColour_;
if (value == this.getValue()) {
// This icon is selected, show it in a different colour
backgroundColor = (this.sourceBlock_ as Blockly.BlockSvg).getColourTertiary();
button.setAttribute('aria-selected', 'true');
}
button.style.backgroundColor = backgroundColor;
button.style.borderColor = this.borderColour_;
Blockly.browserEvents.bind(button, 'click', this, this.buttonClick_);
Blockly.browserEvents.bind(button, 'mouseover', this, () => {
button.setAttribute('class', 'blocklyDropDownButton blocklyDropDownButtonHover');
contentDiv.setAttribute('aria-activedescendant', button.id);
});
Blockly.browserEvents.bind(button, 'mouseout', this, () => {
button.setAttribute('class', 'blocklyDropDownButton');
contentDiv.removeAttribute('aria-activedescendant');
});
let buttonImg = document.createElement('img');
buttonImg.src = content.src;
//buttonImg.alt = icon.alt;
// Upon click/touch, we will be able to get the clicked element as e.target
// Store a data attribute on all possible click targets so we can match it to the icon.
button.setAttribute('data-value', value);
buttonImg.setAttribute('data-value', value);
button.appendChild(buttonImg);
contentDiv.appendChild(button);
}
contentDiv.style.width = this.width_ + 'px';
dropdownDiv.appendChild(contentDiv);
})

if (this.maxRows_) {
// Limit the number of rows shown, but add a partial next row to indicate scrolling
dropdownDiv.style.maxHeight = (this.maxRows_ + 0.4) * (maxButtonHeight + 8) + 'px';
Expand All @@ -126,64 +93,24 @@ export class FieldImageDropdown extends FieldDropdown implements FieldCustom {
// gets removed in onHide_()
dropdownDiv.style.paddingRight = "20px";
}

Blockly.DropDownDiv.setColour(this.backgroundColour_, this.borderColour_);

Blockly.DropDownDiv.showPositionedByField(this, this.onHide_.bind(this));

let source = this.sourceBlock_ as Blockly.BlockSvg;
this.savedPrimary_ = source?.getColour();
if (source?.isShadow()) {
source.setColour(source.getColourTertiary());
} else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', source.getColourTertiary());
}
}

doValueUpdate_(newValue: any): void {
(this as any).selectedOption_ = undefined;
super.doValueUpdate_(newValue);
}

/**
* Callback for when a button is clicked inside the drop-down.
* Should be bound to the FieldIconMenu.
* @param {Event} e DOM event for the click/touch
* @private
*/
protected buttonClick_ = (e: MouseEvent) => {
let value = (e.target as Element).getAttribute('data-value');
if (!value) return;
this.setValue(value);
Blockly.DropDownDiv.hide();
};

/**
* Callback for when the drop-down is hidden.
*/
protected onHide_() {
let content = Blockly.DropDownDiv.getContentDiv() as HTMLElement;
content.removeAttribute('role');
content.removeAttribute('aria-haspopup');
content.removeAttribute('aria-activedescendant');
content.style.width = '';
content.style.paddingRight = '';
content.style.maxHeight = '';

let source = this.sourceBlock_ as Blockly.BlockSvg;
if (source?.isShadow()) {
this.sourceBlock_.setColour(this.savedPrimary_);
} else if (this.borderRect_) {
this.borderRect_.setAttribute('fill', this.savedPrimary_);
}
};
}

Blockly.Css.register(`
.fieldGridDropDownContainer.blocklyMenu {
grid-gap: 0px;
margin: 0px;
}

.blocklyDropDownButton {
display: inline-block;
float: left;
padding: 0;
padding: 0 !important;
margin: 4px;
border-radius: 4px;
outline: none;
Expand All @@ -192,17 +119,15 @@ Blockly.Css.register(`
cursor: pointer;
}

.blocklyDropDownButtonHover {
box-shadow: 0px 0px 0px 4px rgba(255, 255, 255, 0.2);
}

.blocklyDropDownButton:active {
box-shadow: 0px 0px 0px 6px rgba(255, 255, 255, 0.2);
.blocklyDropDownButton > div {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}

.blocklyDropDownButton > img {
.blocklyDropDownButton img {
width: 80%;
height: 80%;
margin-top: 5%
}
`)
Loading