diff --git a/README.md b/README.md index b13e8cc..badb828 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,9 @@ Visit the [life demo](sirpixiejerry.github.io/dop-eye/) to try it yourself. - [x] change element color - [x] rewind to original color - [x] scroll steps -- [ ] improve scroll steps feature and cleanup event listeners -- [ ] add user menu +- [x] add pause button +- [x] add undo button +- [x] add redo button +- [x] add info button +- [ ] add save button - [ ] optimization for mobile devices diff --git a/src/css/style.css b/src/css/style.css index 2a5c446..a49a773 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -31,6 +31,7 @@ body { width: 300px; height: 300px; background-color: rgb(53, 45, 106); + transform: translateX(30px); } svg { @@ -125,3 +126,62 @@ path { transform: translate(-45px, -45px); background-color: yellow; } + +button { + width: 40px; + height: 40px; + border-radius: 8px; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + margin-bottom: 4px; + border: none; + background-color: white; + position: relative; + overflow: hidden; +} + +button:hover { + background-color: rgb(242, 242, 242); +} + +img { + height: 25px; + width: 25px; +} + +#menu { + display: flex; + flex-direction: column; + transform: translate(40px, -150px); +} + +button span { + position: absolute; + border-radius: 8px; + background-color: rgba(0, 0, 0, 0.2); + width: 40px; + height: 40px; + animation: ripple 1s; + opacity: 0; +} + +@keyframes ripple { + from { + opacity: 1; + transform: scale(0); + } + to { + opacity: 0; + transform: scale(10); + } +} + +.active { + background-color: rgb(236, 236, 236); +} + +.active:hover { + background-color: rgb(226, 226, 226); +} diff --git a/src/icons/square-arrow-left-svgrepo-com.svg b/src/icons/square-arrow-left-svgrepo-com.svg new file mode 100644 index 0000000..50e5a78 --- /dev/null +++ b/src/icons/square-arrow-left-svgrepo-com.svg @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/src/icons/square-arrow-right-svgrepo-com.svg b/src/icons/square-arrow-right-svgrepo-com.svg new file mode 100644 index 0000000..fed1cb9 --- /dev/null +++ b/src/icons/square-arrow-right-svgrepo-com.svg @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/src/icons/square-pause-svgrepo-com.svg b/src/icons/square-pause-svgrepo-com.svg new file mode 100644 index 0000000..3e8c8c1 --- /dev/null +++ b/src/icons/square-pause-svgrepo-com.svg @@ -0,0 +1,13 @@ + + + + + \ No newline at end of file diff --git a/src/index.html b/src/index.html index 7c5bf08..3495eae 100644 --- a/src/index.html +++ b/src/index.html @@ -35,6 +35,17 @@ + diff --git a/src/modules/addRippleEffect.ts b/src/modules/addRippleEffect.ts new file mode 100644 index 0000000..b6bd047 --- /dev/null +++ b/src/modules/addRippleEffect.ts @@ -0,0 +1,8 @@ +export const addRippleEffect = (button: HTMLButtonElement): void => { + const ripple = document.createElement('span'); + ripple.classList.add('ripple'); + button.appendChild(ripple); + setTimeout(() => { + ripple.remove(); + }, 600); +}; diff --git a/src/modules/applyColorFromStep.test.ts b/src/modules/applyColorFromStep.test.ts index c5c999b..595450a 100644 --- a/src/modules/applyColorFromStep.test.ts +++ b/src/modules/applyColorFromStep.test.ts @@ -20,6 +20,7 @@ describe('applyColorFromStep', () => { it('should apply color to the specified element', () => { const mockTarget = document.getElementById(mockStep.target); applyColorFromStep(mockStep); - expect(mockTarget!.style.backgroundColor).to.equal(mockStep.color); + if (!mockTarget) throw new Error('mockTarget not found'); + expect(mockTarget.style.backgroundColor).to.equal(mockStep.color); }); }); diff --git a/src/modules/applyColorFromStep.ts b/src/modules/applyColorFromStep.ts index 9b2b078..208ffde 100644 --- a/src/modules/applyColorFromStep.ts +++ b/src/modules/applyColorFromStep.ts @@ -1,6 +1,6 @@ import { colorReflection } from './colorReflection'; import { colorSVG } from './colorSVG'; -import type { StepType } from './handleClickableElement'; +import type { StepType } from './rewindColors'; export const applyColorFromStep = (step: StepType | undefined): void => { if (step === undefined) return; diff --git a/src/modules/handleClickableElement.ts b/src/modules/handleClickableElement.ts index d4ce065..ce22afc 100644 --- a/src/modules/handleClickableElement.ts +++ b/src/modules/handleClickableElement.ts @@ -5,13 +5,7 @@ import { recordColorChange } from './recordColorChange'; import { resetTimer, setStopRewind } from './rewindColors'; import { scrollThroughSteps } from './scrollThroughSteps'; -export type StepType = { - target: string; - color: string; -}; - let isScroll = false; -export const steps: StepType[] = []; type ClickableElement = HTMLElement & { id: string }; diff --git a/src/modules/handleMenuButtons.ts b/src/modules/handleMenuButtons.ts new file mode 100644 index 0000000..28b1bf6 --- /dev/null +++ b/src/modules/handleMenuButtons.ts @@ -0,0 +1,26 @@ +import { addRippleEffect } from './addRippleEffect'; +import { + redoColorChange, + setPauseRewind, + undoColorChange, +} from './rewindColors'; + +export const handleMenuButtons = (): void => { + document.querySelectorAll('button').forEach((button) => { + button.addEventListener('click', (evt) => { + const button = evt.currentTarget as HTMLButtonElement; + addRippleEffect(button); + + if (button.id === 'pause') { + button.classList.toggle('active'); + setPauseRewind(); + } + if (button.id === 'redo') { + redoColorChange(); + } + if (button.id === 'undo') { + undoColorChange(); + } + }); + }); +}; diff --git a/src/modules/index.ts b/src/modules/index.ts index 2c13534..50f78f6 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -3,3 +3,4 @@ export * from './colorSVG'; export * from './colorReflection'; export * from './handleClickableElement'; export * from './recordColorChange'; +export * from './handleMenuButtons'; diff --git a/src/modules/recordColorChange.ts b/src/modules/recordColorChange.ts index 24d259f..4189c17 100644 --- a/src/modules/recordColorChange.ts +++ b/src/modules/recordColorChange.ts @@ -1,4 +1,4 @@ -import { type StepType, steps } from './handleClickableElement'; +import { type StepType, steps, updateStepIndex } from './rewindColors'; const pushStep = (step: StepType): void => { steps.push(step); @@ -11,6 +11,7 @@ export const recordColorChange = (target: Element): void => { target: target.id, color: computedStyle.backgroundColor, }); + updateStepIndex(); } if (target instanceof SVGElement) { const computedStyle = window.getComputedStyle(target); @@ -18,5 +19,6 @@ export const recordColorChange = (target: Element): void => { target: target.id, color: computedStyle.fill, }); + updateStepIndex(); } }; diff --git a/src/modules/rewindColors.ts b/src/modules/rewindColors.ts index bb29bc6..7d44cbb 100644 --- a/src/modules/rewindColors.ts +++ b/src/modules/rewindColors.ts @@ -1,17 +1,41 @@ -import { steps } from './handleClickableElement'; import { applyColorFromStep } from './applyColorFromStep'; +export type StepType = { + target: string; + color: string; +}; + +export const steps: StepType[] = []; +let stepIndex = steps.length - 1; + const DELAY = 1000; const TIMEOUT = 5000; let idleTime: NodeJS.Timeout; let interval: NodeJS.Timeout; let stopRewind = true; +let pauseRewind = false; let initialDelay: number; +export const updateStepIndex = (): void => { + stepIndex = stepIndex + 1; +}; + +export const undoColorChange = (): void => { + if (stepIndex < 0) return; + applyColorFromStep(steps[stepIndex]); + stepIndex = stepIndex - 1; +}; + +export const redoColorChange = (): void => { + if (stepIndex > steps.length - 1) return; + applyColorFromStep(steps[stepIndex]); + stepIndex = stepIndex + 1; +}; + const rewind = (): void => { const removeStep = (): void => { - if (stopRewind) return; + if (stopRewind || pauseRewind) return; if (steps.length > 0) { console.log(steps.length); const removedStep = steps.pop(); @@ -29,8 +53,12 @@ export const setStopRewind = (arg: boolean): void => { stopRewind = arg; }; +export const setPauseRewind = (): void => { + pauseRewind = !pauseRewind; +}; + export const resetTimer = (): void => { - if (stopRewind) return; + if (stopRewind || pauseRewind) return; clearTimeout(idleTime); clearTimeout(interval); initialDelay = DELAY; diff --git a/src/modules/scrollThroughSteps.ts b/src/modules/scrollThroughSteps.ts index cd5214d..28119af 100644 --- a/src/modules/scrollThroughSteps.ts +++ b/src/modules/scrollThroughSteps.ts @@ -1,5 +1,5 @@ import { applyColorFromStep } from './applyColorFromStep'; -import { steps } from './handleClickableElement'; +import { steps } from './rewindColors'; let scrollIndex = 0; diff --git a/src/ts/index.ts b/src/ts/index.ts index 0b8d928..c0f48ea 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -1,5 +1,6 @@ -import { handleClickableElement } from '../modules'; +import { handleClickableElement, handleMenuButtons } from '../modules'; ((): void => { handleClickableElement(); + handleMenuButtons(); })();