mirror of
https://github.com/google/blockly.git
synced 2026-01-15 12:57:12 +01:00
Merge pull request #2594 from BeksOmega/demos/CustomFields
Added custom fields demo.
This commit is contained in:
File diff suppressed because one or more lines are too long
91
demos/custom-fields/blocks.js
Normal file
91
demos/custom-fields/blocks.js
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Turtle field demo blocks..
|
||||
* @author bekawestberg@gmail.com (Beka Westberg)
|
||||
*/
|
||||
|
||||
Blockly.Blocks['turtle_basic'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField('simple turtle');
|
||||
this.appendDummyInput()
|
||||
.setAlign(Blockly.ALIGN_CENTRE)
|
||||
.appendField(new CustomFields.FieldTurtle(), 'TURTLE');
|
||||
this.setStyle('loop_blocks');
|
||||
this.setCommentText('Demonstrates a turtle field with no validator.');
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['turtle_nullifier'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField('no trademarks');
|
||||
this.appendDummyInput()
|
||||
.setAlign(Blockly.ALIGN_CENTRE)
|
||||
.appendField(new CustomFields.FieldTurtle(null, null, null, this.validate)
|
||||
, 'TURTLE');
|
||||
this.setStyle('loop_blocks');
|
||||
this.setCommentText('Validates combinations of names and hats to null' +
|
||||
' (invalid) if they could be considered infringe-y. This turns the' +
|
||||
' turtle field red. Infringe-y combinations are: (Leonardo, Mask),' +
|
||||
' (Yertle, Crown), and (Franklin, Propeller).');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
if ((newValue.turtleName == 'Leonardo' && newValue.hat == 'Mask') ||
|
||||
(newValue.turtleName == 'Yertle' && newValue.hat == 'Crown') ||
|
||||
(newValue.turtleName == 'Franklin') && newValue.hat == 'Propeller') {
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['turtle_changer'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.setAlign(Blockly.ALIGN_CENTRE)
|
||||
.appendField('force hats');
|
||||
this.appendDummyInput()
|
||||
.appendField(new CustomFields.FieldTurtle(
|
||||
'Dots', 'Crown', 'Yertle', this.validate), 'TURTLE');
|
||||
this.setStyle('loop_blocks');
|
||||
this.setCommentText('Validates the input so that certain names always' +
|
||||
' have specific hats. The name-hat combinations are: (Leonardo, Mask),' +
|
||||
' (Yertle, Crown), (Franklin, Propeller).');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
switch(newValue.turtleName) {
|
||||
case 'Leonardo':
|
||||
newValue.hat = 'Mask';
|
||||
break;
|
||||
case 'Yertle':
|
||||
newValue.hat = 'Crown';
|
||||
break;
|
||||
case 'Franklin':
|
||||
newValue.hat = 'Propeller';
|
||||
break;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
};
|
||||
61
demos/custom-fields/custom-fields.css
Normal file
61
demos/custom-fields/custom-fields.css
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.customFieldsTurtleWidget {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget button {
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
background-color: #fff;
|
||||
opacity: .6;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .arrow {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .text {
|
||||
color: #fff;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .randomize {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.blocklyNonEditableText text,
|
||||
.blocklyEditableText text {
|
||||
fill: #000;
|
||||
}
|
||||
712
demos/custom-fields/field_turtle.js
Normal file
712
demos/custom-fields/field_turtle.js
Normal file
@@ -0,0 +1,712 @@
|
||||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2019 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview A field used to customize a turtle.
|
||||
* @author bekawestberg@gmail.com (Beka Westberg)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// You must provide the constructor for your custom field.
|
||||
goog.provide('CustomFields.FieldTurtle');
|
||||
|
||||
// You must require the abstract field class to inherit from.
|
||||
goog.require('Blockly.Field');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.dom');
|
||||
|
||||
|
||||
// Generally field's values should be optional, and have logical defaults.
|
||||
// If this is not possible (for example image fields can't have logical
|
||||
// defaults) the field should throw a clear error when a value is not provided.
|
||||
// Editable fields also generally accept validators, so we will accept a
|
||||
// validator.
|
||||
CustomFields.FieldTurtle = function(
|
||||
opt_pattern, opt_hat, opt_turtleName, opt_validator) {
|
||||
|
||||
// The turtle field contains an object as its value, so we need to compile
|
||||
// the parameters into an object.
|
||||
var value = {};
|
||||
value.pattern = opt_pattern;
|
||||
value.hat = opt_hat;
|
||||
value.turtleName = opt_turtleName;
|
||||
|
||||
var valid = this.doClassValidation_(value);
|
||||
if (valid === null) {
|
||||
// See the doClassValidation_ function for information on the
|
||||
// cachedValidatedValue_ property.
|
||||
value = this.cachedValidatedValue_;
|
||||
value.pattern = value.pattern || CustomFields.FieldTurtle.PATTERNS[0];
|
||||
value.hat = value.hat || CustomFields.FieldTurtle.HATS[0];
|
||||
value.turtleName = value.turtleName || CustomFields.FieldTurtle.NAMES[0];
|
||||
} // Else the original value is fine.
|
||||
|
||||
// A field constructor should always call its parent constructor, because
|
||||
// that helps keep the code organized and DRY.
|
||||
CustomFields.FieldTurtle.superClass_.constructor.call(
|
||||
this, value, opt_validator);
|
||||
this.size_ = new goog.math.Size(72, 40);
|
||||
};
|
||||
goog.inherits(CustomFields.FieldTurtle, Blockly.Field);
|
||||
|
||||
// This allows the field to be constructed using a JSON block definition.
|
||||
CustomFields.FieldTurtle.fromJson = function(options) {
|
||||
// In this case we simply pass the JSON options along to the constructor,
|
||||
// but you can also use this to get message references, and other such things.
|
||||
return new CustomFields.FieldTurtle(
|
||||
options['pattern'],
|
||||
options['hat'],
|
||||
options['turtleName']);
|
||||
};
|
||||
|
||||
// Since this field is editable we must also define serializable as true
|
||||
// (for backwards compatibility reasons serializable is false by default).
|
||||
CustomFields.FieldTurtle.prototype.SERIALIZABLE = true;
|
||||
|
||||
// The cursor property defines what the mouse will look like when the user
|
||||
// hovers over the field. By default the cursor will be whatever
|
||||
// .blocklyDraggable's cursor is defined as (vis. grab). Most fields define
|
||||
// this property as 'default'.
|
||||
CustomFields.FieldTurtle.prototype.CURSOR = 'pointer';
|
||||
|
||||
// These are the different options for our turtle. Being declared this way
|
||||
// means they are static, and not translatable. If you want to do something
|
||||
// similar, but make it translatable you should set up your options like a
|
||||
// dropdown field, with language-neutral keys and human-readable values.
|
||||
CustomFields.FieldTurtle.PATTERNS =
|
||||
['Dots', 'Stripes', 'Hexagons'];
|
||||
CustomFields.FieldTurtle.HATS =
|
||||
['Stovepipe', 'Crown', 'Propeller', 'Mask', 'Fedora'];
|
||||
CustomFields.FieldTurtle.NAMES =
|
||||
['Yertle', 'Franklin', 'Crush', 'Leonardo', 'Bowser', 'Squirtle', 'Oogway'];
|
||||
|
||||
// Used to keep track of our editor event listeners, so they can be
|
||||
// properly disposed of when the field closes. You can keep track of your
|
||||
// listeners however you want, just be sure to dispose of them!
|
||||
CustomFields.FieldTurtle.prototype.editorListeners_ = [];
|
||||
|
||||
// Used to create the DOM of our field.
|
||||
CustomFields.FieldTurtle.prototype.initView = function() {
|
||||
// Because we want to have both a borderRect_ (background) and a
|
||||
// textElement_ (text) we can call the super-function. If we only wanted
|
||||
// one or the other, we could call their individual createX functions.
|
||||
CustomFields.FieldTurtle.superClass_.initView.call(this);
|
||||
|
||||
// Note that the field group is created by the abstract field's init_
|
||||
// function. This means that *all elements* should be children of the
|
||||
// fieldGroup_.
|
||||
this.createView_();
|
||||
};
|
||||
|
||||
// Updates how the field looks depending on if it is editable or not.
|
||||
CustomFields.FieldTurtle.prototype.updateEditable = function() {
|
||||
if (!this.fieldGroup_) {
|
||||
// Not initialized yet.
|
||||
return;
|
||||
}
|
||||
// The default functionality just makes it so the borderRect_ does not
|
||||
// highlight when hovered.
|
||||
Blockly.FieldColour.superClass_.updateEditable.call(this);
|
||||
// Things like this are best applied to the clickTarget_. By default the
|
||||
// click target is the same as getSvgRoot, which by default is the
|
||||
// fieldGroup_.
|
||||
var group = this.getClickTarget_();
|
||||
if (!this.isCurrentlyEditable()) {
|
||||
group.style.cursor = 'not-allowed';
|
||||
} else {
|
||||
group.style.cursor = this.CURSOR;
|
||||
}
|
||||
};
|
||||
|
||||
// Gets the text to display when the block is collapsed
|
||||
CustomFields.FieldTurtle.prototype.getText = function() {
|
||||
var text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
|
||||
if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
|
||||
text += ' hat';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
// Makes sure new field values (given to setValue) are valid, meaning
|
||||
// something this field can legally "hold". Class validators can either change
|
||||
// the input value, or return null if the input value is invalid. Called by
|
||||
// the setValue() function.
|
||||
CustomFields.FieldTurtle.prototype.doClassValidation_ = function(newValue) {
|
||||
// Undefined signals that we want the value to remain unchanged. This is a
|
||||
// special feature of turtle fields, but could be useful for other
|
||||
// multi-part fields.
|
||||
if (newValue.pattern == undefined) {
|
||||
newValue.pattern = this.displayValue_ && this.displayValue_.pattern;
|
||||
// We only want to allow patterns that are part of our pattern list.
|
||||
// Anything else is invalid, so we return null.
|
||||
} else if (CustomFields.FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
|
||||
newValue.pattern = null;
|
||||
}
|
||||
|
||||
if (newValue.hat == undefined) {
|
||||
newValue.hat = this.displayValue_ && this.displayValue_.hat;
|
||||
} else if (CustomFields.FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
|
||||
newValue.hat = null;
|
||||
}
|
||||
|
||||
if (newValue.turtleName == undefined) {
|
||||
newValue.turtleName = this.displayValue_ && this.displayValue_.turtleName;
|
||||
} else if (CustomFields.FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
|
||||
newValue.turtleName = null;
|
||||
}
|
||||
|
||||
// Always be sure to return!
|
||||
if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
|
||||
// This is a strategy for dealing with defaults on multi-part values.
|
||||
// The class validator sets individual properties of the object to null
|
||||
// to indicate that they are invalid, and then caches that object to the
|
||||
// cachedValidatedValue_ property. This way the field can, for
|
||||
// example, properly handle an invalid pattern, combined with a valid hat.
|
||||
// This can also be done with local validators.
|
||||
this.cachedValidatedValue_ = newValue;
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
};
|
||||
|
||||
// Saves the new field value. Called by the setValue function.
|
||||
CustomFields.FieldTurtle.prototype.doValueUpdate_ = function(newValue) {
|
||||
// The default function sets this field's this.value_ property to the
|
||||
// newValue, and its this.isDirty_ property to true. The isDirty_ property
|
||||
// tells the setValue function whether the field needs to be re-rendered.
|
||||
CustomFields.FieldTurtle.superClass_.doValueUpdate_.call(this, newValue);
|
||||
this.displayValue_ = newValue;
|
||||
// Since this field has custom UI for invalid values, we also want to make
|
||||
// sure it knows it is now valid.
|
||||
this.isValueInvalid_ = false;
|
||||
};
|
||||
|
||||
// Notifies that the field that the new value was invalid. Called by
|
||||
// setValue function. Can either be triggered by the class validator, or the
|
||||
// local validator.
|
||||
CustomFields.FieldTurtle.prototype.doValueInvalid_ = function(invalidValue) {
|
||||
// By default this function is no-op, meaning if the new value is invalid
|
||||
// the field simply won't be updated. This field has custom UI for invalid
|
||||
// values, so we override this function.
|
||||
|
||||
// We want the value to be displayed like normal.
|
||||
// But we want to flag it as invalid, so the render_ function knows to
|
||||
// make the borderRect_ red.
|
||||
this.displayValue_ = invalidValue;
|
||||
this.isDirty_ = true;
|
||||
this.isValueInvalid_ = true;
|
||||
};
|
||||
|
||||
// Updates the field's on-block display based on the current display value.
|
||||
CustomFields.FieldTurtle.prototype.render_ = function() {
|
||||
var value = this.displayValue_;
|
||||
|
||||
// Always do editor updates inside render. This makes sure the editor
|
||||
// always displays the correct value, even if a validator changes it.
|
||||
if (this.editor_) {
|
||||
this.editor_.patternText.textContent = value.pattern;
|
||||
this.editor_.hatText.textContent = value.hat;
|
||||
this.editor_.turtleNameText.textContent = value.turtleName;
|
||||
}
|
||||
|
||||
this.stovepipe_.style.display = 'none';
|
||||
this.crown_.style.display = 'none';
|
||||
this.mask_.style.display = 'none';
|
||||
this.propeller_.style.display = 'none';
|
||||
this.fedora_.style.display = 'none';
|
||||
switch(value.hat) {
|
||||
case 'Stovepipe':
|
||||
this.stovepipe_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,12)');
|
||||
this.textElement_.setAttribute('transform', 'translate(60,20)');
|
||||
break;
|
||||
case 'Crown':
|
||||
this.crown_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,9)');
|
||||
this.textElement_.setAttribute('transform', 'translate(60,16)');
|
||||
break;
|
||||
case 'Mask':
|
||||
this.mask_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,1.2)');
|
||||
this.textElement_.setAttribute('transform', 'translate(60,4)');
|
||||
break;
|
||||
case 'Propeller':
|
||||
this.propeller_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
|
||||
this.textElement_.setAttribute('transform', 'translate(60,12)');
|
||||
break;
|
||||
case 'Fedora':
|
||||
this.fedora_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
|
||||
this.textElement_.setAttribute('transform', 'translate(60,12)');
|
||||
break;
|
||||
}
|
||||
|
||||
switch(value.pattern) {
|
||||
case 'Dots':
|
||||
this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
|
||||
break;
|
||||
case 'Stripes':
|
||||
this.shellPattern_.setAttribute('fill', 'url(#stripes)');
|
||||
break;
|
||||
case 'Hexagons':
|
||||
this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
|
||||
break;
|
||||
}
|
||||
|
||||
// Always modify the textContent_ rather than the textElement_. This
|
||||
// allows fields to append DOM to the textElement (e.g. the angle field).
|
||||
this.textContent_.nodeValue = value.turtleName;
|
||||
|
||||
if (this.isValueInvalid_) {
|
||||
this.borderRect_.style.fill = '#f99';
|
||||
this.borderRect_.style.fillOpacity = 1;
|
||||
} else {
|
||||
this.borderRect_.style.fill = '#fff';
|
||||
this.borderRect_.style.fillOpacity = 0.6;
|
||||
}
|
||||
|
||||
this.updateSize_();
|
||||
};
|
||||
|
||||
// Used to update the size of the field. This function's logic could be simply
|
||||
// included inside render_ (it is not called anywhere else), but it is
|
||||
// usually separated to keep code more organized.
|
||||
CustomFields.FieldTurtle.prototype.updateSize_ = function() {
|
||||
var size = this.movableGroup_.getBBox();
|
||||
if (this.borderRect_) {
|
||||
this.borderRect_.setAttribute('width',
|
||||
size.width + Blockly.BlockSvg.SEP_SPACE_X);
|
||||
this.borderRect_.setAttribute('height', size.height + 9);
|
||||
}
|
||||
// Note how both the width and the height can be dynamic.
|
||||
this.size_.width = size.width;
|
||||
this.size_.height = size.height + 10 +
|
||||
(Blockly.BlockSvg.INLINE_PADDING_Y * 2);
|
||||
};
|
||||
|
||||
// Called when the field is clicked. It is usually used to show an editor,
|
||||
// but it can also be used for other things e.g. the checkbox field uses
|
||||
// this function to check/uncheck itself.
|
||||
CustomFields.FieldTurtle.prototype.showEditor_ = function() {
|
||||
this.editor_ = this.dropdownCreate_();
|
||||
Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);
|
||||
|
||||
// These allow us to have the editor match the block's colour.
|
||||
var fillColour = this.sourceBlock_.getColour();
|
||||
// This is technically a package function, meaning it could change.
|
||||
var borderColours = this.sourceBlock_.getColourBorder();
|
||||
var borderColour = borderColours.colourBorder || borderColours.colourDark;
|
||||
Blockly.DropDownDiv.setColour(fillColour, borderColour);
|
||||
|
||||
// Always pass the dropdown div a dispose function so that you can clean
|
||||
// up event listeners when the editor closes.
|
||||
Blockly.DropDownDiv.showPositionedByField(
|
||||
this, this.dropdownDispose_.bind(this));
|
||||
};
|
||||
|
||||
// Creates the UI of the editor, and adds event listeners to it.
|
||||
CustomFields.FieldTurtle.prototype.dropdownCreate_ = function() {
|
||||
var createRow = function(table) {
|
||||
var row = table.appendChild(document.createElement('tr'));
|
||||
row.className = 'row';
|
||||
return row;
|
||||
};
|
||||
var createLeftArrow = function(row) {
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'arrow';
|
||||
var leftArrow = document.createElement('button');
|
||||
leftArrow.setAttribute('type', 'button');
|
||||
leftArrow.textContent = '<';
|
||||
cell.appendChild(leftArrow);
|
||||
row.appendChild(cell);
|
||||
return cell;
|
||||
};
|
||||
var createTextNode = function(row, text) {
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'text';
|
||||
var text = document.createTextNode(text);
|
||||
cell.appendChild(text);
|
||||
row.appendChild(cell);
|
||||
return cell;
|
||||
};
|
||||
var createRightArrow = function(row) {
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'arrow';
|
||||
var rightArrow = document.createElement('button');
|
||||
rightArrow.setAttribute('type', 'button');
|
||||
rightArrow.textContent = '>';
|
||||
cell.appendChild(rightArrow);
|
||||
row.appendChild(cell);
|
||||
return cell;
|
||||
};
|
||||
var createArrowListener = function(variable, array, direction) {
|
||||
return function() {
|
||||
var currentIndex = array.indexOf(this.displayValue_[variable]);
|
||||
currentIndex += direction;
|
||||
if (currentIndex <= -1) {
|
||||
currentIndex = array.length - 1;
|
||||
} else if (currentIndex >= array.length) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
var value = {};
|
||||
value[variable] = array[currentIndex];
|
||||
this.setValue(value);
|
||||
};
|
||||
};
|
||||
|
||||
var widget = document.createElement('div');
|
||||
widget.className = 'customFieldsTurtleWidget';
|
||||
|
||||
var table = document.createElement('div');
|
||||
table.className = 'table';
|
||||
widget.appendChild(table);
|
||||
|
||||
var row = createRow(table);
|
||||
var leftArrow = createLeftArrow(row);
|
||||
widget.patternText = createTextNode(row, this.displayValue_.pattern);
|
||||
var rightArrow = createRightArrow(row);
|
||||
this.editorListeners_.push(Blockly.bindEvent_(leftArrow, 'mouseup', this,
|
||||
createArrowListener('pattern', CustomFields.FieldTurtle.PATTERNS, -1)));
|
||||
this.editorListeners_.push(Blockly.bindEvent_(rightArrow, 'mouseup', this,
|
||||
createArrowListener('pattern', CustomFields.FieldTurtle.PATTERNS, 1)));
|
||||
|
||||
row = createRow(table);
|
||||
leftArrow = createLeftArrow(row);
|
||||
widget.hatText = createTextNode(row, this.displayValue_.hat);
|
||||
rightArrow = createRightArrow(row);
|
||||
this.editorListeners_.push(Blockly.bindEvent_(leftArrow, 'mouseup', this,
|
||||
createArrowListener('hat', CustomFields.FieldTurtle.HATS, -1)));
|
||||
this.editorListeners_.push(Blockly.bindEvent_(rightArrow, 'mouseup', this,
|
||||
createArrowListener('hat', CustomFields.FieldTurtle.HATS, 1)));
|
||||
|
||||
row = createRow(table);
|
||||
leftArrow = createLeftArrow(row);
|
||||
widget.turtleNameText = createTextNode(row, this.displayValue_.turtleName);
|
||||
rightArrow = createRightArrow(row);
|
||||
this.editorListeners_.push(Blockly.bindEvent_(leftArrow, 'mouseup', this,
|
||||
createArrowListener('turtleName', CustomFields.FieldTurtle.NAMES, -1)));
|
||||
this.editorListeners_.push(Blockly.bindEvent_(rightArrow, 'mouseup', this,
|
||||
createArrowListener('turtleName', CustomFields.FieldTurtle.NAMES, 1)));
|
||||
|
||||
var randomizeButton = document.createElement('button');
|
||||
randomizeButton.className = 'randomize';
|
||||
randomizeButton.setAttribute('type', 'button');
|
||||
randomizeButton.textContent = 'randomize turtle';
|
||||
this.editorListeners_.push(Blockly.bindEvent_(randomizeButton, 'mouseup', this,
|
||||
function() {
|
||||
var value = {};
|
||||
value.pattern = CustomFields.FieldTurtle.PATTERNS[
|
||||
Math.floor(Math.random() * CustomFields.FieldTurtle.PATTERNS.length)];
|
||||
|
||||
value.hat = CustomFields.FieldTurtle.HATS[
|
||||
Math.floor(Math.random() * CustomFields.FieldTurtle.HATS.length)];
|
||||
|
||||
value.turtleName = CustomFields.FieldTurtle.NAMES[
|
||||
Math.floor(Math.random() * CustomFields.FieldTurtle.NAMES.length)];
|
||||
|
||||
this.setValue(value);
|
||||
}));
|
||||
widget.appendChild(randomizeButton);
|
||||
|
||||
return widget;
|
||||
};
|
||||
|
||||
// Cleans up any event listeners that were attached to the now hidden editor.
|
||||
CustomFields.FieldTurtle.prototype.dropdownDispose_ = function() {
|
||||
for (var i = this.editorListeners_.length, listener;
|
||||
listener = this.editorListeners_[i]; i--) {
|
||||
Blockly.unbindEvent_(listener);
|
||||
this.editorListeners_.pop();
|
||||
}
|
||||
};
|
||||
|
||||
// Updates the field's colour based on the colour of the block. Called by
|
||||
// block.updateColour.
|
||||
CustomFields.FieldTurtle.prototype.updateColour = function() {
|
||||
if (!this.sourceBlock_) {
|
||||
return;
|
||||
}
|
||||
// The getColourX functions are the best way to access the colours of a block.
|
||||
var isShadow = this.sourceBlock_.isShadow();
|
||||
var fillColour = isShadow ?
|
||||
this.sourceBlock_.getColourShadow() : this.sourceBlock_.getColour();
|
||||
// This is technically a package function, meaning it could change.
|
||||
var borderColours = this.sourceBlock_.getColourBorder();
|
||||
var borderColour = isShadow ? fillColour :
|
||||
borderColours.colourBorder || borderColours.colourDark;
|
||||
|
||||
var child = this.turtleGroup_.firstChild;
|
||||
while(child) {
|
||||
// If it is a text node, or a non-turtle node.
|
||||
if (child.nodeType == 3 || !child.classList.contains('turtleBody')) {
|
||||
child = child.nextSibling;
|
||||
continue;
|
||||
}
|
||||
child.style.fill = fillColour;
|
||||
child.style.stroke = borderColour;
|
||||
child = child.nextSibling;
|
||||
}
|
||||
};
|
||||
|
||||
// Saves the field's value to an XML node. Allows for custom serialization.
|
||||
CustomFields.FieldTurtle.prototype.toXml = function(fieldElement) {
|
||||
// The default implementation of this function creates a node that looks
|
||||
// like this: (where value is returned by getValue())
|
||||
// <field name="FIELDNAME">value</field>
|
||||
// But this doesn't work for our field because it stores an /object/.
|
||||
|
||||
fieldElement.setAttribute('pattern', this.value_.pattern);
|
||||
fieldElement.setAttribute('hat', this.value_.hat);
|
||||
// The textContent usually contains whatever is closest to the field's
|
||||
// 'value'. The textContent doesn't need to contain anything, but saving
|
||||
// something to it does aid in readability.
|
||||
fieldElement.textContent = this.value_.turtleName;
|
||||
|
||||
// Always return the element!
|
||||
return fieldElement;
|
||||
};
|
||||
|
||||
// Sets the field's value based on an XML node. Allows for custom
|
||||
// de-serialization.
|
||||
CustomFields.FieldTurtle.prototype.fromXml = function(fieldElement) {
|
||||
// Because we had to do custom serialization for this field, we also need
|
||||
// to do custom de-serialization.
|
||||
|
||||
var value = {};
|
||||
value.pattern = fieldElement.getAttribute('pattern');
|
||||
value.hat = fieldElement.getAttribute('hat');
|
||||
value.turtleName = fieldElement.textContent;
|
||||
// The end goal is to call this.setValue()
|
||||
this.setValue(value);
|
||||
};
|
||||
|
||||
// Blockly needs to know the JSON name of this field. Usually this is
|
||||
// registered at the bottom of the field class.
|
||||
Blockly.Field.register('field_turtle', CustomFields.FieldTurtle);
|
||||
|
||||
// Called by initView to create all of the SVGs. This is just used to keep
|
||||
// the code more organized.
|
||||
CustomFields.FieldTurtle.prototype.createView_ = function() {
|
||||
this.movableGroup_ = Blockly.utils.dom.createSvgElement('g',
|
||||
{
|
||||
'transform': 'translate(0,5)'
|
||||
}, this.fieldGroup_);
|
||||
var scaleGroup = Blockly.utils.dom.createSvgElement('g',
|
||||
{
|
||||
'transform': 'scale(1.5)'
|
||||
}, this.movableGroup_);
|
||||
this.turtleGroup_ = Blockly.utils.dom.createSvgElement('g',
|
||||
{
|
||||
// Makes the smaller turtle graphic align with the hats.
|
||||
'class': 'turtleBody'
|
||||
}, scaleGroup);
|
||||
var tail = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M7,27.5H0.188c3.959-2,6.547-2.708,8.776-5.237',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
var legLeft = Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'x': 8.812,
|
||||
'y': 12.506,
|
||||
'width': 4,
|
||||
'height': 10
|
||||
}, this.turtleGroup_);
|
||||
var legRight = Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'x': 28.812,
|
||||
'y': 12.506,
|
||||
'width': 4,
|
||||
'height': 10
|
||||
}, this.turtleGroup_);
|
||||
var head = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M47.991,17.884c0,1.92-2.144,3.477-4.788,3.477a6.262,6.262,0,0,1-2.212-.392c-0.2-.077-1.995,2.343-4.866,3.112a17.019,17.019,0,0,1-6.01.588c-4.413-.053-2.5-3.412-2.745-3.819-0.147-.242,2.232.144,6.126-0.376a7.392,7.392,0,0,0,4.919-2.588c0-1.92,2.144-3.477,4.788-3.477S47.991,15.964,47.991,17.884Z',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
var smile = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M42.223,18.668a3.614,3.614,0,0,0,2.728,2.38',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
var sclera = Blockly.utils.dom.createSvgElement('ellipse',
|
||||
{
|
||||
'cx': 43.435,
|
||||
'cy': 2.61,
|
||||
'rx': 2.247,
|
||||
'ry': 2.61,
|
||||
'fill': '#fff'
|
||||
}, this.turtleGroup_);
|
||||
var pupil = Blockly.utils.dom.createSvgElement('ellipse',
|
||||
{
|
||||
'cx': 44.166,
|
||||
'cy': 3.403,
|
||||
'rx': 1.318,
|
||||
'ry': 1.62
|
||||
}, this.turtleGroup_);
|
||||
var shell = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M33.4,27.5H7.193c0-6,5.866-13.021,13.1-13.021S33.4,21.5,33.4,27.5Z',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
this.shellPattern_ = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'd': 'M33.4,27.5H7.193c0-6,5.866-13.021,13.1-13.021S33.4,21.5,33.4,27.5Z',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
|
||||
this.stovepipe_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '18'
|
||||
}, scaleGroup);
|
||||
this.stovepipe_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/stovepipe.svg');
|
||||
this.crown_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '15'
|
||||
}, scaleGroup);
|
||||
this.crown_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/crown.svg');
|
||||
this.mask_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '14'
|
||||
}, scaleGroup);
|
||||
this.mask_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/mask.svg');
|
||||
this.propeller_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '11'
|
||||
}, scaleGroup);
|
||||
this.propeller_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/propeller.svg');
|
||||
this.fedora_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '12'
|
||||
}, scaleGroup);
|
||||
this.fedora_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/fedora.svg');
|
||||
|
||||
// Even if we're not going to display it right away, we want to create all
|
||||
// of our DOM elements inside this function.
|
||||
this.crown_.style.display = 'none';
|
||||
this.mask_.style.display = 'none';
|
||||
this.propeller_.style.display = 'none';
|
||||
this.fedora_.style.display = 'none';
|
||||
|
||||
this.movableGroup_.appendChild(this.textElement_);
|
||||
this.textElement_.setAttribute('transform', 'translate(60,20)');
|
||||
|
||||
this.defs_ = Blockly.utils.dom.createSvgElement('defs', {}, this.fieldGroup_);
|
||||
this.polkadotPattern_ = Blockly.utils.dom.createSvgElement('pattern',
|
||||
{
|
||||
'id': 'polkadots',
|
||||
'patternUnits': 'userSpaceOnUse',
|
||||
'width': 10,
|
||||
'height': 10
|
||||
}, this.defs_);
|
||||
this.polkadotGroup_ = Blockly.utils.dom.createSvgElement(
|
||||
'g', {}, this.polkadotPattern_);
|
||||
Blockly.utils.dom.createSvgElement('circle',
|
||||
{
|
||||
'cx': 2.5,
|
||||
'cy': 2.5,
|
||||
'r': 2.5,
|
||||
'fill': '#000',
|
||||
'fill-opacity': .3
|
||||
}, this.polkadotGroup_);
|
||||
Blockly.utils.dom.createSvgElement('circle',
|
||||
{
|
||||
'cx': 7.5,
|
||||
'cy': 7.5,
|
||||
'r': 2.5,
|
||||
'fill': '#000',
|
||||
'fill-opacity': .3
|
||||
}, this.polkadotGroup_);
|
||||
|
||||
this.hexagonPattern_ = Blockly.utils.dom.createSvgElement('pattern',
|
||||
{
|
||||
'id': 'hexagons',
|
||||
'patternUnits': 'userSpaceOnUse',
|
||||
'width': 10,
|
||||
'height': 8.68,
|
||||
'patternTransform': 'translate(2) rotate(45)'
|
||||
}, this.defs_);
|
||||
Blockly.utils.dom.createSvgElement('polygon',
|
||||
{
|
||||
'id': 'hex',
|
||||
'points': '4.96,4.4 7.46,5.84 7.46,8.74 4.96,10.18 2.46,8.74 2.46,5.84',
|
||||
'stroke': '#000',
|
||||
'stroke-opacity': .3,
|
||||
'fill-opacity': 0
|
||||
}, this.hexagonPattern_);
|
||||
var use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': 5,
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': -5,
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': 2.5,
|
||||
'y': -4.34
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': -2.5,
|
||||
'y': -4.34
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
|
||||
this.stripesPattern_ = Blockly.utils.dom.createSvgElement('pattern',
|
||||
{
|
||||
'id': 'stripes',
|
||||
'patternUnits': 'userSpaceOnUse',
|
||||
'width': 5,
|
||||
'height': 10,
|
||||
'patternTransform': 'rotate(45)'
|
||||
}, this.defs_);
|
||||
Blockly.utils.dom.createSvgElement('line',
|
||||
{
|
||||
'x1': 0,
|
||||
'y1': 0,
|
||||
'x2': 0,
|
||||
'y2': 10,
|
||||
'stroke-width': 4,
|
||||
'stroke': '#000',
|
||||
'stroke-opacity': .3
|
||||
}, this.stripesPattern_);
|
||||
}
|
||||
BIN
demos/custom-fields/icon.png
Normal file
BIN
demos/custom-fields/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.5 KiB |
148
demos/custom-fields/index.html
Normal file
148
demos/custom-fields/index.html
Normal file
@@ -0,0 +1,148 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Blockly Demo: Custom Fields</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="blocks.js"></script>
|
||||
<script src="field_turtle.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="custom-fields.css">
|
||||
</head>
|
||||
<body onload="start()">
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Custom Fields</h1>
|
||||
|
||||
|
||||
<p>This is a demo of creating custom block fields. In this case the field
|
||||
is used to define a turtle.
|
||||
</p>
|
||||
|
||||
<p>Click on the blocks' comment icons to learn what they are demonstrating.
|
||||
Use the buttons below to see how the fields react to changes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="set random style" onclick="setRandomStyle()">
|
||||
<input type="button" value="toggle shadow" onclick="toggleShadow()">
|
||||
<input type="button" value="toggle enabled" onclick="toggleEnabled()">
|
||||
<input type="button" value="toggle editable" onclick="toggleEditable()">
|
||||
<input type="button" value="toggle collapsed" onclick="toggleCollapsed()">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Export to XML" onclick="toXml()">
|
||||
<input type="button" value="Import from XML" onclick="fromXml()">
|
||||
</p>
|
||||
|
||||
<table style="width: 800px; height: 480px">
|
||||
<tr>
|
||||
<td>
|
||||
<textarea id="importExport"
|
||||
style="width: 200px; height: 100%;"
|
||||
onchange="textAreaChange();"
|
||||
onkeyup="textAreaChange()"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<div id="blocklyDiv" style="width: 600px; height: 100%;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function toXml() {
|
||||
var output = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
output.value = Blockly.Xml.domToPrettyText(xml);
|
||||
output.focus();
|
||||
output.select();
|
||||
}
|
||||
|
||||
function fromXml() {
|
||||
var input = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.textToDom(input.value);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
}
|
||||
|
||||
function setRandomStyle() {
|
||||
var blocks = workspace.getAllBlocks();
|
||||
var styles = Object.keys(Blockly.getTheme().getAllBlockStyles());
|
||||
styles.splice(styles.indexOf(blocks[0].getStyleName()), 1);
|
||||
var style = styles[Math.floor(Math.random() * styles.length)];
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setStyle(style);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShadow() {
|
||||
var blocks = workspace.getAllBlocks();
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setShadow(!block.isShadow());
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEnabled() {
|
||||
var blocks = workspace.getAllBlocks();
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setEnabled(!block.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEditable() {
|
||||
var blocks = workspace.getAllBlocks();
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setEditable(!block.isEditable());
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCollapsed() {
|
||||
var blocks = workspace.getAllBlocks();
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setCollapsed(!block.isCollapsed());
|
||||
}
|
||||
}
|
||||
|
||||
function appendDom() {
|
||||
var blocks = document.getElementById('workspace-blocks');
|
||||
if (blocks.firstElementChild) {
|
||||
Blockly.Xml.appendDomToWorkspace(blocks, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
workspace = Blockly.inject('blocklyDiv', options);
|
||||
appendDom();
|
||||
workspace.scrollCenter();
|
||||
}
|
||||
|
||||
var options = {
|
||||
media: '../../media/',
|
||||
grid: {
|
||||
spacing: 25,
|
||||
length: 3,
|
||||
colour: '#ccc'
|
||||
},
|
||||
move: {
|
||||
scrollbars: true,
|
||||
drag: true,
|
||||
wheel: true,
|
||||
},
|
||||
zoom: {
|
||||
controls: true,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
}
|
||||
/*toolbox: document.getElementById('toolbox')*/
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<xml id="workspace-blocks" xmlns="http://www.w3.org/1999/xhtml" style="display: none">
|
||||
<block type="turtle_basic"></block>
|
||||
<block type="turtle_nullifier" y="120"></block>
|
||||
<block type="turtle_changer" y="230"></block>
|
||||
</xml>
|
||||
</body>
|
||||
</html>
|
||||
1
demos/custom-fields/media/crown.svg
Normal file
1
demos/custom-fields/media/crown.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 15"><defs><style>.a{fill:#fff200;}.b{fill:#f3bd1f;}</style></defs><title>crown</title><polygon class="a" points="38.778 13.941 46.824 9.457 44.8 1.575 43.401 7.589 38.826 4.23 39.147 9.918 33.292 8.096 38.778 13.941"/><circle class="a" cx="33.228" cy="7.972" r="1.194" transform="translate(0.253 16.931) rotate(-28.691)"/><circle class="a" cx="38.785" cy="4.3" r="1.194" transform="translate(2.698 19.148) rotate(-28.691)"/><circle class="a" cx="44.662" cy="1.663" r="1.194" transform="translate(4.685 21.646) rotate(-28.691)"/><polygon class="b" points="37.846 12.954 46.508 8.199 46.154 6.836 36.888 11.921 37.846 12.954"/></svg>
|
||||
|
After Width: | Height: | Size: 687 B |
1
demos/custom-fields/media/fedora.svg
Normal file
1
demos/custom-fields/media/fedora.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 12"><defs><style>.a{fill:#8b5e3c;}.b{fill:#3c2415;}.c{fill:none;stroke:#754c29;stroke-linecap:round;stroke-miterlimit:10;}</style></defs><title>fedora</title><polygon class="a" points="38.548 9.742 38.264 4.844 41.403 5.223 42.508 1.407 47.846 5.918 46.334 7.664 38.548 9.742"/><path class="b" d="M41.133,8.362l1.1,0.236c1.9-1.066,3.844-2.051,4.741-3.392L45.78,4.193A22.044,22.044,0,0,1,41.133,8.362Z"/><path class="c" d="M37.233,11.3A12.1,12.1,0,0,1,40.4,9.09c1.7-.743,3.625-0.71,5.325-1.3a5.62,5.62,0,0,0,2.678-2.5"/></svg>
|
||||
|
After Width: | Height: | Size: 581 B |
1
demos/custom-fields/media/mask.svg
Normal file
1
demos/custom-fields/media/mask.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 14"><defs><style>.a{fill:#00aeef;}</style></defs><title>mask</title><path class="a" d="M43.5,0.94C42.329,1.2,39.032,3.75,39,4.531a21.249,21.249,0,0,0,3.5,1.811,10.847,10.847,0,0,0,4.065,0c0.751-.189,2.812-1.03,2.729-1.893-0.116-1.2-1.7-2.561-2.478-3.007A4.983,4.983,0,0,0,43.5.94Zm0.64,5.29a2.409,2.409,0,0,1-2.251-2.564,2.272,2.272,0,1,1,4.493,0A2.4,2.4,0,0,1,44.139,6.23Z"/><polygon class="a" points="38.996 4.531 42.106 9.154 40.018 13.776 38.996 4.531"/><polygon class="a" points="38.996 4.531 37.456 9.544 33.559 11.437 38.996 4.531"/></svg>
|
||||
|
After Width: | Height: | Size: 602 B |
1
demos/custom-fields/media/propeller.svg
Normal file
1
demos/custom-fields/media/propeller.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 11"><defs><style>.a{fill:#00aeef;}.b{fill:#ed1c24;}.c{fill:#fff200;}</style></defs><title>propeller</title><path class="a" d="M39.974,3.854c-2.022.99-2.948,3.89-1.509,6.828L40.716,9.56A10.329,10.329,0,0,1,39.974,3.854Z"/><path class="b" d="M39.974,3.854c1.975-.967,4.846-0.012,6.319,3L44.277,7.873A8.089,8.089,0,0,0,39.974,3.854Z"/><path class="c" d="M39.974,3.854a8.088,8.088,0,0,1,4.3,4.02l-3.55,1.71A10.011,10.011,0,0,1,39.974,3.854Z"/><path class="c" d="M35.795,4.493a3.81,3.81,0,0,1,1.016-.871A3.548,3.548,0,0,1,38.032,3.1a4.2,4.2,0,0,0,1.483-.434,9.15,9.15,0,0,0,1.245-.92A4.585,4.585,0,0,1,41.926,1.1a3.941,3.941,0,0,1,1.3-.3L43.364,1.1a3.941,3.941,0,0,1-1.031.84,4.585,4.585,0,0,1-1.219.53,9.149,9.149,0,0,0-1.49.419,4.194,4.194,0,0,0-1.252.905,3.549,3.549,0,0,1-1.164.642A3.81,3.81,0,0,1,35.9,4.7Z"/></svg>
|
||||
|
After Width: | Height: | Size: 871 B |
1
demos/custom-fields/media/stovepipe.svg
Normal file
1
demos/custom-fields/media/stovepipe.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18"><defs><style>.a,.c{fill:#231f20;stroke:#231f20;}.a,.b,.c{stroke-linejoin:round;}.b{fill:#fff;stroke:#fff;}.c{stroke-linecap:round;}</style></defs><title>stovepipe</title><rect class="a" x="33.995" y="2.114" width="8.322" height="11.675" transform="translate(0.82 19.139) rotate(-28.453)"/><rect class="b" x="36.206" y="10.835" width="8.322" height="2.394" transform="translate(-0.856 20.686) rotate(-28.453)"/><line class="c" x1="35.164" y1="16.971" x2="47.341" y2="10.373"/></svg>
|
||||
|
After Width: | Height: | Size: 541 B |
Reference in New Issue
Block a user