Creatorofstuff/coding
Coder
Hello world this is my morphic.js copy and this is also a react copy too it’s a mix of reach and morphic if your wondering what morphic.js then I suggest you read this and use this
This is morphic.js: GitHub - jmoenig/morphic.js: a lively web GUI In morphic.txt u Will learn how to use it
This is snap(which was created by morphic): Snap! Build Your Own Blocks
This is morphic.js: GitHub - jmoenig/morphic.js: a lively web GUI In morphic.txt u Will learn how to use it
This is snap(which was created by morphic): Snap! Build Your Own Blocks
JavaScript:
const MyUI = (() => {
class Pos {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Component {
constructor(x, y, width, height, color = 'gray', isDraggable = false, isTemplate = false) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this._color = color;
this.isDraggable = isDraggable;
this.isTemplate = isTemplate;
this.children = [];
this.state = {};
this.props = {};
this.init();
}
get color() {
return this._color;
}
set color(value) {
this._color = value;
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
if (this.isDraggable) {
ctx.strokeStyle = 'black';
ctx.strokeRect(this.x, this.y, this.width, this.height);
}
this.children.forEach(child => child.drawNew(ctx));
}
drawNew(ctx) {
this.draw(ctx);
}
handleMouseEvent(eventType, event) {
switch (eventType) {
case 'click':
if (this.onMouseClick) this.onMouseClick();
break;
case 'rightClick':
if (this.onRightMouseClick) this.onRightMouseClick();
break;
case 'leftClick':
if (this.onLeftMouseClick) this.onLeftMouseClick();
break;
}
if (this.isDraggable && eventType === 'mousedown' && !this.isTemplate) {
this.startDragging(event);
}
this.children.forEach(child => child.handleMouseEvent(eventType, event));
}
handleTouchEvent(eventType, event) {
const touch = event.touches ? event.touches[0] : event;
switch (eventType) {
case 'touchstart':
if (this.isDraggable && !this.isTemplate) {
this.startDragging(event);
}
break;
case 'touchmove':
if (this.isDraggable) {
this.handleDragging(event);
}
break;
case 'touchend':
if (this.isDraggable) {
this.stopDragging();
}
break;
}
this.children.forEach(child => child.handleTouchEvent(eventType, event));
}
startDragging(event) {
const canvas = event.target;
const canvasRect = canvas.getBoundingClientRect();
const touch = event.touches ? event.touches[0] : event;
this.offsetX = touch.clientX - canvasRect.left - this.x;
this.offsetY = touch.clientY - canvasRect.top - this.y;
const onMove = (moveEvent) => {
const touch = moveEvent.touches ? moveEvent.touches[0] : moveEvent;
this.x = touch.clientX - canvasRect.left - this.offsetX;
this.y = touch.clientY - canvasRect.top - this.offsetY;
this.componentDidUpdate();
};
const onEnd = () => {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onEnd);
document.removeEventListener('touchmove', onMove);
document.removeEventListener('touchend', onEnd);
};
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onEnd);
document.addEventListener('touchmove', onMove);
document.addEventListener('touchend', onEnd);
}
stopDragging() {
// Implement if needed
}
handleDragging(event) {
// Implement if needed
}
add(component) {
component.parent = this;
this.children.push(component);
this.componentDidUpdate();
}
moveTo(pos) {
this.x = pos.x;
this.y = pos.y;
this.componentDidUpdate();
}
moveBy(pos) {
this.x += pos.x;
this.y += pos.y;
this.componentDidUpdate();
}
setState(newState) {
const prevState = { ...this.state };
this.state = { ...this.state, ...newState };
this.componentDidUpdate(prevState, this.state);
}
setProps(newProps) {
const prevProps = { ...this.props };
this.props = { ...this.props, ...newProps };
this.componentDidUpdate(prevProps, this.props);
}
init() {
this.componentDidMount();
}
componentDidMount() {
// Override to add behavior after component is mounted
}
componentDidUpdate(prevStateOrProps, newStateOrProps) {
// Override to add behavior after component state or props change
}
componentWillUnmount() {
// Override to add behavior before component is unmounted
}
reactToTemplateCopy() {
// Override to add behavior when a copy from a template is created
}
selectForEdit() {
return this;
}
prepareToBeGrabbed(component) {
// Override to add behavior before the component is grabbed
}
reactToGrabOf(grabbedComponent) {
// Override to add behavior after a component is grabbed
}
}
class Canvas {
constructor(canvasElement) {
this.ctx = canvasElement.getContext('2d');
this.components = [];
this.setupEventListeners(canvasElement);
this.draggedComponent = null;
}
setupEventListeners(canvasElement) {
canvasElement.addEventListener('click', (e) => this.handleClick(e));
canvasElement.addEventListener('contextmenu', (e) => this.handleRightClick(e));
canvasElement.addEventListener('mousedown', (e) => this.handleLeftClick(e));
canvasElement.addEventListener('touchstart', (e) => this.handleTouchStart(e));
canvasElement.addEventListener('touchmove', (e) => this.handleTouchMove(e));
canvasElement.addEventListener('touchend', (e) => this.handleTouchEnd(e));
}
handleClick(event) {
this.handleMouseEvent('click', event);
}
handleRightClick(event) {
event.preventDefault();
this.handleMouseEvent('rightClick', event);
}
handleLeftClick(event) {
this.handleMouseEvent('leftClick', event);
}
handleTouchStart(event) {
event.preventDefault();
this.handleTouchEvent('touchstart', event);
}
handleTouchMove(event) {
event.preventDefault();
this.handleTouchEvent('touchmove', event);
}
handleTouchEnd(event) {
event.preventDefault();
this.handleTouchEvent('touchend', event);
}
handleMouseEvent(type, event) {
const { offsetX, offsetY } = event;
let handled = false;
for (let i = this.components.length - 1; i >= 0; i--) {
const component = this.components[i];
if (
offsetX >= component.x &&
offsetX <= component.x + component.width &&
offsetY >= component.y &&
offsetY <= component.y + component.height
) {
if (component.isTemplate && !component.isDraggable) {
const copy = this.createComponentFromTemplate(component);
this.addComponent(copy);
component.prepareToBeGrabbed(copy);
copy.startDragging(event);
} else {
component.handleMouseEvent(type, event);
}
handled = true;
break;
}
}
if (type === 'mousedown' && !handled) {
this.draggedComponent = null;
}
}
handleTouchEvent(type, event) {
const touch = event.touches[0];
const { clientX, clientY } = touch || {};
if (clientX === undefined || clientY === undefined) {
return;
}
let handled = false;
for (let i = this.components.length - 1; i >= 0; i--) {
const component = this.components[i];
if (
clientX >= component.x &&
clientX <= component.x + component.width &&
clientY >= component.y &&
clientY <= component.y + component.height
) {
if (component.isTemplate && !component.isDraggable) {
const copy = this.createComponentFromTemplate(component);
this.addComponent(copy);
component.prepareToBeGrabbed(copy);
copy.startDragging(event);
} else {
component.handleTouchEvent(type, event);
}
handled = true;
break;
}
}
if (type === 'touchstart' && !handled) {
this.draggedComponent = null;
}
}
createComponentFromTemplate(template) {
const copy = new MyUI.Component(template.x, template.y, template.width, template.height, template.color, template.isDraggable, true);
copy.reactToTemplateCopy();
return copy;
}
addComponent(component) {
this.components.push(component);
this.render();
}
removeComponent(component) {
const index = this.components.indexOf(component);
if (index > -1) {
this.components.splice(index, 1);
component.componentWillUnmount();
}
}
render() {
this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
this.components.forEach(component => component.drawNew(this.ctx));
}
}
class Button extends Component {
constructor(x, y, width, height, label = '', color = 'lightgray', isDraggable = false) {
super(x, y, width, height, color, isDraggable);
this.label = label;
}
draw(ctx) {
super.draw(ctx);
ctx.fillStyle = 'black';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(this.label, this.x + this.width / 2, this.y + this.height / 2);
}
}
class TextBox extends Component {
constructor(x, y, width, height, text = '', textColor = 'black', isDraggable = false) {
super(x, y, width, height, 'white', isDraggable);
this.text = text;
this.textColor = textColor;
}
draw(ctx) {
super.draw(ctx);
ctx.fillStyle = this.textColor;
ctx.font = '16px Arial';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.fillText(this.text, this.x + 5, this.y + 5);
}
}
class Slider extends Component {
constructor(x, y, width, height, min = 0, max = 100, value = 50, handleSize = 10, isDraggable = false) {
super(x, y, width, height, 'lightgray', isDraggable);
this.min = min;
this.max = max;
this.value = value;
this.handleSize = handleSize;
}
draw(ctx) {
super.draw(ctx);
ctx.fillStyle = 'lightgray';
ctx.fillRect(this.x, this.y + this.height / 2 - 5, this.width, 10);
ctx.fillStyle = 'gray';
const handleX = this.x + (this.value - this.min) / (this.max - this.min) * (this.width - this.handleSize);
ctx.fillRect(handleX, this.y + this.height / 2 - this.handleSize / 2, this.handleSize, this.handleSize);
}
handleMouseEvent(type, event) {
super.handleMouseEvent(type, event);
if (type === 'mousedown') {
this.handleDragging(event);
}
}
handleTouchEvent(type, event) {
super.handleTouchEvent(type, event);
if (type === 'touchstart') {
this.handleDragging(event);
}
}
handleDragging(event) {
const touch = event.touches ? event.touches[0] : event;
this.value = Math.min(this.max, Math.max(this.min, Math.round((touch.clientX - this.x) / this.width * (this.max - this.min) + this.min)));
this.componentDidUpdate();
}
}
class Box extends Component {
constructor(x, y, size, color = 'gray', isDraggable = false) {
super(x, y, size, size, color, isDraggable);
}
}
class CircleBox extends Component {
constructor(x, y, radius, color = 'gray', isDraggable = false) {
super(x, y, radius * 2, radius * 2, color, isDraggable);
this.radius = radius;
}
draw(ctx) {
ctx.beginPath();
ctx.arc(this.x + this.radius, this.y + this.radius, this.radius, 0, 2 * Math.PI);
ctx.fillStyle = this.color;
ctx.fill();
if (this.isDraggable) {
ctx.strokeStyle = 'black';
ctx.stroke();
}
}
}
class Triangle extends Component {
constructor(x, y, size, color = 'gray', isDraggable = false) {
super(x, y, size, size, color, isDraggable);
this.size = size;
}
draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.x, this.y + this.size);
ctx.lineTo(this.x + this.size / 2, this.y);
ctx.lineTo(this.x + this.size, this.y + this.size);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
if (this.isDraggable) {
ctx.strokeStyle = 'black';
ctx.stroke();
}
}
}
class Pen extends Component {
constructor(x, y, size = 20, color = 'black', isDraggable = false) {
super(x, y, size, size, color, isDraggable);
this.size = size;
}
draw(ctx) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x + this.size / 4, this.y, this.size / 2, this.size);
if (this.isDraggable) {
ctx.strokeStyle = 'black';
ctx.strokeRect(this.x + this.size / 4, this.y, this.size / 2, this.size);
}
}
}
class Input extends Component {
constructor(x, y, width, height, placeholder = '', value = '', isDraggable = false) {
super(x, y, width, height, 'white', isDraggable);
this.placeholder = placeholder;
this.value = value;
this.isActive = false;
this.cursorPosition = this.value.length;
}
draw(ctx) {
super.draw(ctx);
ctx.fillStyle = 'black';
ctx.font = '16px Arial';
ctx.textAlign = 'left';
ctx.textBaseline = 'middle';
const text = this.value || this.placeholder;
const displayText = text.length > 10 ? text.slice(-10) : text;
ctx.fillText(displayText, this.x + 5, this.y + this.height / 2);
if (this.isActive) {
const cursorX = this.x + 5 + ctx.measureText(displayText.slice(0, this.cursorPosition)).width;
ctx.beginPath();
ctx.moveTo(cursorX, this.y + 5);
ctx.lineTo(cursorX, this.y + this.height - 5);
ctx.stroke();
}
}
handleMouseEvent(type, event) {
super.handleMouseEvent(type, event);
if (type === 'click') {
this.isActive = true;
this.cursorPosition = this.value.length;
document.addEventListener('keydown', this.handleKeyDown);
}
}
handleKeyDown = (event) => {
if (!this.isActive) return;
if (event.key === 'Enter') {
this.isActive = false;
document.removeEventListener('keydown', this.handleKeyDown);
return;
}
if (event.key === 'Backspace') {
if (this.cursorPosition > 0) {
this.value = this.value.slice(0, this.cursorPosition - 1) + this.value.slice(this.cursorPosition);
this.cursorPosition--;
}
} else if (event.key.length === 1) {
this.value = this.value.slice(0, this.cursorPosition) + event.key + this.value.slice(this.cursorPosition);
this.cursorPosition++;
} else if (event.key === 'ArrowLeft') {
this.cursorPosition = Math.max(0, this.cursorPosition - 1);
} else if (event.key === 'ArrowRight') {
this.cursorPosition = Math.min(this.value.length, this.cursorPosition + 1);
}
event.preventDefault();
}
}
return {
Component,
Canvas,
Button,
TextBox,
Slider,
Box,
CircleBox,
Triangle,
Pen,
Input
};
})();
Last edited: