diff --git a/front/src/interfaces/Popup.interface.ts b/front/src/interfaces/Popup.interface.ts new file mode 100644 index 00000000..35402f11 --- /dev/null +++ b/front/src/interfaces/Popup.interface.ts @@ -0,0 +1,5 @@ +export interface IPopup { + handleClick(mouseX: number, mouseY: number, positionX: number, positionY: number, width: number, textInfo: string, linkInfo: string, context: CanvasRenderingContext2D, canvas: HTMLCanvasElement): void; + openPopup(context: CanvasRenderingContext2D, positionX: number, positionY: number, width: number, textInfo: string): void; + handlePopupLink(mouseX: number, mouseY: number, linkInfo: string, context: CanvasRenderingContext2D) : void; +} \ No newline at end of file diff --git a/front/src/services/board/drawer/factories/Base.factory.ts b/front/src/services/board/drawer/factories/Base.factory.ts index 9237fbc9..7be45494 100644 --- a/front/src/services/board/drawer/factories/Base.factory.ts +++ b/front/src/services/board/drawer/factories/Base.factory.ts @@ -4,33 +4,34 @@ import { type IPosition } from '../../../../interfaces/Position.interface' import CommonBases from '../Common.bases' import TextPipe from '../../../../pipes/Text.pipe' import { type TDrawer } from '../../../../types/Drawer' +import PopupManager from '../../../canvas/Popup.manager'; const BaseFactory: TBaseFactory = { - ...CommonBases, - ...StateFactory, - - create (drawer: TDrawer): void { - this.drawer = drawer - this.positionX = isNaN(drawer.entity!.positionX) ? this.positionX : drawer.entity!.positionX - this.positionY = isNaN(drawer.entity!.positionY) ? this.positionY : drawer.entity!.positionY - }, - - isSelected ({ x, y }: IPosition): boolean { - return this.drawer!.context!.isPointInPath(this.path, x, y) - }, - - updatePosition (position: IPosition): void { - this.positionX = position.x - this.positionY = position.y - }, - - position (withOffset: number = 0): IPosition { - return { - x: this.positionX - withOffset, y: this.positionY - withOffset - } - }, + ...CommonBases, + ...StateFactory, + + create (drawer: TDrawer): void { + this.drawer = drawer + this.positionX = isNaN(drawer.entity!.positionX) ? this.positionX : drawer.entity!.positionX + this.positionY = isNaN(drawer.entity!.positionY) ? this.positionY : drawer.entity!.positionY + }, + + isSelected ({ x, y }: IPosition): boolean { + return this.drawer!.context!.isPointInPath(this.path, x, y) + }, + + updatePosition (position: IPosition): void { + this.positionX = position.x + this.positionY = position.y + }, + + position (withOffset: number = 0): IPosition { + return { + x: this.positionX - withOffset, y: this.positionY - withOffset + } + }, - draw (): void { + draw (): void { const context = this.drawer!.context! const rectangle = new Path2D() @@ -48,9 +49,9 @@ const BaseFactory: TBaseFactory = { context.roundRect(this.positionX, this.positionY, this.width, this.height, [10]) if (this.selected) { - context.strokeStyle = this.selectedColor + context.strokeStyle = this.selectedColor } else if (this.onHover) { - context.strokeStyle = this.onHoverColor + context.strokeStyle = this.onHoverColor } context.stroke(rectangle) @@ -67,13 +68,6 @@ const BaseFactory: TBaseFactory = { const buttonHeight = 20; const buttonRadius = 5; - const popupX = this.positionX + this.width + 40; - const popupY = this.positionY; - const popupWidth = 250; - const popupHeight = 250; - - let isPopupVisible = false; - context.fillStyle = "white"; context.beginPath(); context.moveTo(buttonX, buttonY + buttonRadius); @@ -86,50 +80,30 @@ const BaseFactory: TBaseFactory = { context.lineTo(buttonX + buttonRadius, buttonY); context.quadraticCurveTo(buttonX, buttonY, buttonX, buttonY + buttonRadius); context.fill(); + context.font = "16px Arial"; context.fillStyle = "black"; context.fillText("Info", buttonX + 10, buttonY + 15); let textInfo = ''; + let linkInfo = ''; if (this.backgroundColor == "#304570") { - textInfo = 'A Docker network\nis an essential\ncomponent of the\nDocker ecosystem\nthat manages\ncontainer connectivity.'; - + textInfo = 'A Docker network\nis an essential\ncomponent of the\nDocker ecosystem\nthat manages\ncontainer connectivity.' + linkInfo = 'https://docs.docker.com/network/'; } else if (this.backgroundColor == "#1f2937") { textInfo = 'A Docker service\nis a software unit\nfor deploying, managing,\nand scaling containers,\nsimplifying the\nmanagement of\napplications in distributed\nenvironments.' - + linkInfo = 'https://docs.docker.com/engine/swarm/how-swarm-mode-works/services/'; } else { textInfo = 'A Docker volume\nis a storage mechanism\nthat enables Docker\ncontainers to access\npersistent and shared\ndata.' + linkInfo = 'https://docs.docker.com/storage/volumes/'; } - canvas.addEventListener("click", (event) => { - const mouseX = event.clientX - canvas.getBoundingClientRect().left; - const mouseY = event.clientY - canvas.getBoundingClientRect().top; - - if (mouseX >= buttonX && mouseX <= buttonX + buttonWidth && mouseY >= buttonY && mouseY <= buttonY + buttonHeight) { - if (isPopupVisible) { - context.clearRect(popupX, popupY, popupWidth, popupHeight); - isPopupVisible = false; - } else { - context.fillStyle = "white"; - context.strokeStyle = "black"; - context.lineWidth = 2; - context.strokeRect(popupX, popupY, popupWidth, popupHeight); - - context.fillRect(popupX, popupY, popupWidth, popupHeight); - context.fillStyle = "black"; - - const lines = textInfo.split("\n"); - - for (let i = 0; i < lines.length; i++) { - context.fillText(lines[i], popupX + 10, popupY + 25 + i * 25); - } - - context.fillText("Click to close", popupX + 10, popupY + 240); - isPopupVisible = true; - } - } + const mouseX = event.clientX - canvas.getBoundingClientRect().left + const mouseY = event.clientY - canvas.getBoundingClientRect().top + + PopupManager.handleClick(mouseX, mouseY, this.positionX, this.positionY, this.width, textInfo, linkInfo, context, canvas); }); context.fillStyle = this.titleColor @@ -143,7 +117,7 @@ const BaseFactory: TBaseFactory = { if (this.afterDraw != null) this.afterDraw() this.path = rectangle - } + } } export default BaseFactory diff --git a/front/src/services/canvas/Popup.manager.ts b/front/src/services/canvas/Popup.manager.ts new file mode 100644 index 00000000..f5e1c371 --- /dev/null +++ b/front/src/services/canvas/Popup.manager.ts @@ -0,0 +1,70 @@ +import { type TPopupManager } from '../../types/canvas/Popup.manager' +import { type IPopup } from '../../interfaces/Popup.interface' + +const PopupManager: TPopupManager & IPopup = { + isPopupOpen: false, + popupWidth: 250, + popupHeight: 250, + popupX: 0, + popupY: 0, + learnMoreX: 0, + learnMoreY: 0, + + handleClick(mouseX: number, mouseY: number, positionX: number, positionY: number, width:number, textInfo: string, linkInfo: string, context: CanvasRenderingContext2D, canvas: HTMLCanvasElement) { + const buttonX = positionX + width - 60; + const buttonY = positionY + 10; + const buttonWidth = 50; + const buttonHeight = 20; + this.popupX = positionX + width + 40; + this.popupY = positionY; + this.learnMoreX = this.popupX + 10; + this.learnMoreY = this.popupY + 240; + + if (mouseX >= buttonX && mouseX <= buttonX + buttonWidth && mouseY >= buttonY && mouseY <= buttonY + buttonHeight) { + if (!this.isPopupOpen) { + this.openPopup(context, positionX, positionY, width, textInfo); + this.isPopupOpen = true; + } + } else if (mouseX >= this.learnMoreX && mouseX <= this.learnMoreX + context.measureText("Learn more").width && mouseY >= this.learnMoreY - 16 && mouseY <= this.learnMoreY) { + this.handlePopupLink(mouseX, mouseY, linkInfo, context) + } else { + this.isPopupOpen = false; + } + }, + + openPopup(context: CanvasRenderingContext2D, positionX: number, positionY: number, width: number, textInfo: string) { + this.isPopupOpen = true; + context.fillStyle = "white"; + context.strokeStyle = "black"; + context.lineWidth = 2; + context.strokeRect(this.popupX, positionY, this.popupWidth, this.popupHeight); + context.fillRect(this.popupX, this.popupY, this.popupWidth, this.popupHeight); + context.fillStyle = "black"; + + const lines = textInfo.split("\n"); + + for (let i = 0; i < lines.length; i++) { + context.fillText(lines[i], this.popupX + 10, this.popupY + 25 + i * 25); + } + + context.font = "16px Arial"; + context.fillText("Learn more", this.learnMoreX, this.learnMoreY); + + context.strokeStyle = "black"; + context.lineWidth = 2; + context.beginPath(); + context.moveTo(this.learnMoreX, this.learnMoreY + 3); + context.lineTo(this.learnMoreX + context.measureText("Learn more").width, this.learnMoreY + 3); + context.stroke(); + }, + + handlePopupLink(mouseX: number, mouseY: number, linkInfo: string, context: CanvasRenderingContext2D) { + if (mouseX >= this.learnMoreX && mouseX <= this.learnMoreX + context.measureText("Learn more").width && + mouseY >= this.learnMoreY - 16 && mouseY <= this.learnMoreY) { + + window.open(linkInfo, "_blank"); + } + }, +} + +export default PopupManager; \ No newline at end of file diff --git a/front/src/types/canvas/Popup.manager.ts b/front/src/types/canvas/Popup.manager.ts new file mode 100644 index 00000000..5903e87b --- /dev/null +++ b/front/src/types/canvas/Popup.manager.ts @@ -0,0 +1,9 @@ +export type TPopupManager = { + isPopupOpen: boolean; + popupWidth: number; + popupHeight: number; + popupX: number, + popupY: number, + learnMoreX: number, + learnMoreY: number, +} \ No newline at end of file