Skip to content
Open
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
85 changes: 22 additions & 63 deletions core/trashcan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// Unused import preserved for side-effects. Remove if unneeded.
import * as browserEvents from './browser_events.js';
import {ComponentManager} from './component_manager.js';
import * as Css from './css.js';
import {DeleteArea} from './delete_area.js';
import type {Abstract} from './events/events_abstract.js';
import './events/events_trashcan_open.js';
Expand All @@ -28,7 +29,6 @@ import type {UiMetrics} from './metrics_manager.js';
import * as uiPosition from './positionable_helpers.js';
import * as registry from './registry.js';
import type * as blocks from './serialization/blocks.js';
import {SPRITE} from './sprites.js';
import * as dom from './utils/dom.js';
import {Rect} from './utils/rect.js';
import {Size} from './utils/size.js';
Expand Down Expand Up @@ -150,61 +150,21 @@ export class Trashcan
</g>
*/
this.svgGroup = dom.createSvgElement(Svg.G, {'class': 'blocklyTrash'});
let clip;
const rnd = String(Math.random()).substring(2);
clip = dom.createSvgElement(
Svg.CLIPPATH,
{'id': 'blocklyTrashBodyClipPath' + rnd},
this.svgLid = dom.createSvgElement(
Svg.G,
{'class': 'blocklyTrashLid'},
this.svgGroup,
);
dom.createSvgElement(
Svg.RECT,
{'width': WIDTH, 'height': BODY_HEIGHT, 'y': LID_HEIGHT},
clip,
);
this.svgLid.innerHTML = `<path d="M 2,9 v 6 h 42 v -6 h -10.5 l -3,-3 h -15 l -3,3 z" />`;
const body = dom.createSvgElement(
Svg.IMAGE,
{
'width': SPRITE.width,
'x': -SPRITE_LEFT,
'height': SPRITE.height,
'y': -SPRITE_TOP,
'clip-path': 'url(#blocklyTrashBodyClipPath' + rnd + ')',
},
Svg.G,
{'class': 'blocklyTrashBody'},
this.svgGroup,
);
body.setAttributeNS(
dom.XLINK_NS,
'xlink:href',
this.workspace.options.pathToMedia + SPRITE.url,
);

clip = dom.createSvgElement(
Svg.CLIPPATH,
{'id': 'blocklyTrashLidClipPath' + rnd},
this.svgGroup,
);
dom.createSvgElement(
Svg.RECT,
{'width': WIDTH, 'height': LID_HEIGHT},
clip,
);
this.svgLid = dom.createSvgElement(
Svg.IMAGE,
{
'width': SPRITE.width,
'x': -SPRITE_LEFT,
'height': SPRITE.height,
'y': -SPRITE_TOP,
'clip-path': 'url(#blocklyTrashLidClipPath' + rnd + ')',
},
this.svgGroup,
);
this.svgLid.setAttributeNS(
dom.XLINK_NS,
'xlink:href',
this.workspace.options.pathToMedia + SPRITE.url,
);
body.innerHTML = `
<rect width="36" height="20" x="5" y="18" />
<rect width="36" height="42" x="5" y="18" rx="4" ry="4" />
`;

// bindEventWithChecks_ quashes events too aggressively. See:
// https://groups.google.com/forum/#!topic/blockly/QF4yB9Wx00s
Expand Down Expand Up @@ -475,12 +435,6 @@ export class Trashcan

this.setLidAngle(this.lidOpen * MAX_LID_ANGLE);

// Linear interpolation between min and max.
const opacity = OPACITY_MIN + this.lidOpen * (OPACITY_MAX - OPACITY_MIN);
if (this.svgGroup) {
this.svgGroup.style.opacity = `${opacity}`;
}

if (this.lidOpen > this.minOpenness && this.lidOpen < 1) {
this.lidTask = setTimeout(
this.animateLid.bind(this),
Expand Down Expand Up @@ -717,14 +671,19 @@ const ANIMATION_LENGTH = 80;
/** The number of frames in the animation. */
const ANIMATION_FRAMES = 4;

/** The minimum (resting) opacity of the trashcan and lid. */
const OPACITY_MIN = 0.4;

/** The maximum (hovered) opacity of the trashcan and lid. */
const OPACITY_MAX = 0.8;

/**
* The maximum angle the trashcan lid can opens to. At the end of the open
* animation the lid will be open to this angle.
*/
const MAX_LID_ANGLE = 45;

Css.register(`
.blocklyTrash {
fill: #888;
opacity: 0.4;
transition: opacity .08s linear;
}
.blocklyTrash:hover {
opacity: 0.8;
}
`);
142 changes: 40 additions & 102 deletions core/zoom_controls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import * as eventUtils from './events/utils.js';
import type {IPositionable} from './interfaces/i_positionable.js';
import type {UiMetrics} from './metrics_manager.js';
import * as uiPosition from './positionable_helpers.js';
import {SPRITE} from './sprites.js';
import * as Touch from './touch.js';
import * as dom from './utils/dom.js';
import {Rect} from './utils/rect.js';
Expand Down Expand Up @@ -102,13 +101,12 @@ export class ZoomControls implements IPositionable {
// Each filter/pattern needs a unique ID for the case of multiple Blockly
// instances on a page. Browser behaviour becomes undefined otherwise.
// https://neil.fraser.name/news/2015/11/01/
const rnd = String(Math.random()).substring(2);
this.createZoomOutSvg(rnd);
this.createZoomInSvg(rnd);
this.createZoomOutSvg();
this.createZoomInSvg();
if (this.workspace.isMovable()) {
// If we zoom to the center and the workspace isn't movable we could
// loose blocks at the edges of the workspace.
this.createZoomResetSvg(rnd);
this.createZoomResetSvg();
}
return this.svgGroup;
}
Expand Down Expand Up @@ -238,12 +236,8 @@ export class ZoomControls implements IPositionable {

/**
* Create the zoom in icon and its event handler.
*
* @param rnd The random string to use as a suffix in the clip path's ID.
* These IDs must be unique in case there are multiple Blockly instances
* on the same page.
*/
private createZoomOutSvg(rnd: string) {
private createZoomOutSvg() {
/* This markup will be generated and added to the .svgGroup:
<g class="blocklyZoom">
<clipPath id="blocklyZoomoutClipPath837493">
Expand All @@ -259,35 +253,10 @@ export class ZoomControls implements IPositionable {
{'class': 'blocklyZoom blocklyZoomOut'},
this.svgGroup,
);
const clip = dom.createSvgElement(
Svg.CLIPPATH,
{'id': 'blocklyZoomoutClipPath' + rnd},
this.zoomOutGroup,
);
dom.createSvgElement(
Svg.RECT,
{
'width': 32,
'height': 32,
},
clip,
);
const zoomoutSvg = dom.createSvgElement(
Svg.IMAGE,
{
'width': SPRITE.width,
'height': SPRITE.height,
'x': -64,
'y': -92,
'clip-path': 'url(#blocklyZoomoutClipPath' + rnd + ')',
},
this.zoomOutGroup,
);
zoomoutSvg.setAttributeNS(
dom.XLINK_NS,
'xlink:href',
this.workspace.options.pathToMedia + SPRITE.url,
);
this.zoomOutGroup.innerHTML = `
<circle r="15" cx="16" cy="16" />
<path d="m 9.6,16 h12.8" />
`;

// Attach listener.
this.boundEvents.push(
Expand All @@ -302,12 +271,8 @@ export class ZoomControls implements IPositionable {

/**
* Create the zoom out icon and its event handler.
*
* @param rnd The random string to use as a suffix in the clip path's ID.
* These IDs must be unique in case there are multiple Blockly instances
* on the same page.
*/
private createZoomInSvg(rnd: string) {
private createZoomInSvg() {
/* This markup will be generated and added to the .svgGroup:
<g class="blocklyZoom">
<clipPath id="blocklyZoominClipPath837493">
Expand All @@ -323,35 +288,11 @@ export class ZoomControls implements IPositionable {
{'class': 'blocklyZoom blocklyZoomIn'},
this.svgGroup,
);
const clip = dom.createSvgElement(
Svg.CLIPPATH,
{'id': 'blocklyZoominClipPath' + rnd},
this.zoomInGroup,
);
dom.createSvgElement(
Svg.RECT,
{
'width': 32,
'height': 32,
},
clip,
);
const zoominSvg = dom.createSvgElement(
Svg.IMAGE,
{
'width': SPRITE.width,
'height': SPRITE.height,
'x': -32,
'y': -92,
'clip-path': 'url(#blocklyZoominClipPath' + rnd + ')',
},
this.zoomInGroup,
);
zoominSvg.setAttributeNS(
dom.XLINK_NS,
'xlink:href',
this.workspace.options.pathToMedia + SPRITE.url,
);
this.zoomInGroup.innerHTML = `
<circle r="15" cx="16" cy="16" />
<path d="m 16,8.6 v12.8" />
<path d="m 9.6,16 h12.8" />
`;

// Attach listener.
this.boundEvents.push(
Expand Down Expand Up @@ -383,12 +324,8 @@ export class ZoomControls implements IPositionable {

/**
* Create the zoom reset icon and its event handler.
*
* @param rnd The random string to use as a suffix in the clip path's ID.
* These IDs must be unique in case there are multiple Blockly instances
* on the same page.
*/
private createZoomResetSvg(rnd: string) {
private createZoomResetSvg() {
/* This markup will be generated and added to the .svgGroup:
<g class="blocklyZoom">
<clipPath id="blocklyZoomresetClipPath837493">
Expand All @@ -404,27 +341,14 @@ export class ZoomControls implements IPositionable {
{'class': 'blocklyZoom blocklyZoomReset'},
this.svgGroup,
);
const clip = dom.createSvgElement(
Svg.CLIPPATH,
{'id': 'blocklyZoomresetClipPath' + rnd},
this.zoomResetGroup,
);
dom.createSvgElement(Svg.RECT, {'width': 32, 'height': 32}, clip);
const zoomresetSvg = dom.createSvgElement(
Svg.IMAGE,
{
'width': SPRITE.width,
'height': SPRITE.height,
'y': -92,
'clip-path': 'url(#blocklyZoomresetClipPath' + rnd + ')',
},
this.zoomResetGroup,
);
zoomresetSvg.setAttributeNS(
dom.XLINK_NS,
'xlink:href',
this.workspace.options.pathToMedia + SPRITE.url,
);
this.zoomResetGroup.innerHTML = `
<circle r="11.5" cx="16" cy="16" />
<circle r="4.3" cx="16" cy="16" class="center" />
<path d="m 28,16 h3" />
<path d="m 1,16 h3" />
<path d="m 16,28 v3" />
<path d="m 16,1 v3" />
`;

// Attach event listeners.
this.boundEvents.push(
Expand Down Expand Up @@ -479,15 +403,29 @@ export class ZoomControls implements IPositionable {

/** CSS for zoom controls. See css.js for use. */
Css.register(`
.blocklyZoom>image, .blocklyZoom>svg>image {
.blocklyZoom {
opacity: .4;
pointer-events: bounding-box;
}

.blocklyZoom>image:hover, .blocklyZoom>svg>image:hover {
.blocklyZoom:hover {
opacity: .6;
}

.blocklyZoom>image:active, .blocklyZoom>svg>image:active {
.blocklyZoom:active {
opacity: .8;
}

.blocklyZoom>* {
fill:#fff;
fill-opacity:0.005;
stroke:#888;
stroke-width:2;
stroke-linecap:round;
}
.blocklyZoom>circle.center {
fill: #888;
fill-opacity: 1;
stroke-width: 0;
}
`);