Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
feat: "undo / redo" support for attribute changes (#1175)
Browse files Browse the repository at this point in the history
* feat: undo redo attributes and deck transition animation

* feat: input undo redo working together with custom undo redo

* feat: undo and redo with content editable / input events

* refactor: dedicated store for undo and redo

* refactor: rename element innerHTML value used to be pushed to undo on changes

* feat: set color and style undo redo

* chore: prettier

* feat: remove stack for inner html when about to style

* feat: propagate undo redo element for saving purpose

* fix: do not trigger update on undo redo color

* refactor: move helper to utils

* fix: use slotted element as reference (element can be a child)

* feat: reset undo redo stack on leaving editor

* fix: stack undo redo on element changes

* feat: undo redo font family

* feat: undo redo text scale

* feat: undo redo letter spacing and align

* feat: undo redo block

* feat: redo undo border radius

* feat: redo undo box shadow

* feat: redo undo code color

* feat: redo undo reset code color

* feat: redo events throw in component for attributes too

* refactor: duplicate code

* feat: redo undo color

* refactor: update multiple styles at once

* refactor: update multiple properties and styles at once

* fix: definition

* feat: undo redo image style

* feat: undo redo list

* fix: reset undo redo store

* feat: do not stack if same value
  • Loading branch information
peterpeterparker authored Jun 13, 2021
1 parent 0eac94d commit 4315304
Show file tree
Hide file tree
Showing 23 changed files with 817 additions and 244 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {isSlide} from '@deckdeckgo/deck-utils';

import store from '../../../../../stores/busy.store';
import i18n from '../../../../../stores/i18n.store';
import undoRedoStore from '../../../../../stores/undo-redo.store';

import {ImageHelper} from '../../../../../helpers/editor/image.helper';
import {ShapeHelper} from '../../../../../helpers/editor/shape.helper';
Expand Down Expand Up @@ -67,6 +68,26 @@ export class AppActionsElement {

@Event() private resetted: EventEmitter<void>;

private observeElementMutations = () => {
if (undoRedoStore.state.elementInnerHTML === undefined) {
return;
}

if (undoRedoStore.state.elementInnerHTML === this.selectedElement.element.innerHTML) {
return;
}

if (undoRedoStore.state.undo === undefined) {
undoRedoStore.state.undo = [];
}

undoRedoStore.state.undo.push({type: 'input', target: this.selectedElement.element, data: {innerHTML: undoRedoStore.state.elementInnerHTML}});

undoRedoStore.state.elementInnerHTML = this.selectedElement.element.innerHTML;
};

private observer: MutationObserver = new MutationObserver(this.observeElementMutations);

constructor() {
this.debounceResizeSlideContent = debounce(async () => {
await this.resizeSlideContent();
Expand Down Expand Up @@ -133,17 +154,27 @@ export class AppActionsElement {
}

@Method()
touch(element: HTMLElement, autoOpen: boolean = true): Promise<void> {
return new Promise<void>(async (resolve) => {
await this.unSelect();
await this.select(element, autoOpen);
async touch(element: HTMLElement | undefined, autoOpen: boolean = true) {
await this.unSelect();
await this.select(element, autoOpen);

if (element) {
element.focus();
}
if (!element) {
return;
}

resolve();
});
element.focus();

if (this.selectedElement?.type === 'element') {
this.observer.takeRecords();
this.observer.observe(this.selectedElement.element, {attributes: true, childList: true, subtree: true});

undoRedoStore.state.elementInnerHTML = this.selectedElement.element.innerHTML;
return;
}

this.observer.disconnect();

undoRedoStore.state.elementInnerHTML = undefined;
}

@Method()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Component, Element, Event, Watch, EventEmitter, Fragment, h, Host, JSX,
import {isSlide} from '@deckdeckgo/deck-utils';

import editorStore from '../../../../../stores/editor.store';
import undoRedoStore from '../../../../../stores/undo-redo.store';

import {BreadcrumbsStep} from '../../../../../types/editor/breadcrumbs-step';

Expand Down Expand Up @@ -57,6 +58,27 @@ export class AppActionsEditor {

private actionsElementRef!: HTMLAppActionsElementElement;

private destroyUndoRedoListener;

componentDidLoad() {
this.destroyUndoRedoListener = undoRedoStore.onChange('elementInnerHTML', (elementInnerHTML: string | undefined) => {
if (elementInnerHTML === undefined) {
this.el.removeEventListener('click', this.resetElementInnerHTML, false);
return;
}

this.el.addEventListener('click', this.resetElementInnerHTML, {once: true});
});
}

disconnectedCallback() {
this.destroyUndoRedoListener?.();
}

private resetElementInnerHTML = () => {
undoRedoStore.state.elementInnerHTML = undefined;
};

@Watch('fullscreen')
onFullscreenChange() {
this.hideBottomSheet = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import i18n from '../../../../stores/i18n.store';

import {ColorUtils, InitStyleColor} from '../../../../utils/editor/color.utils';
import {SettingsUtils} from '../../../../utils/core/settings.utils';
import {setStyle} from '../../../../utils/editor/undo-redo.utils';

import {Expanded} from '../../../../types/core/settings';

Expand All @@ -26,6 +27,8 @@ export class AppColorTextBackground {
@Prop()
colorType: 'text' | 'background' = 'text';

private colorRef!: HTMLAppColorElement;

@Event() colorChange: EventEmitter<void>;

private initBackground = async (): Promise<InitStyleColor> => {
Expand Down Expand Up @@ -85,11 +88,11 @@ export class AppColorTextBackground {
return;
}

if (this.deck || this.slide) {
this.selectedElement.style.setProperty('--color', selectedColor);
} else {
this.selectedElement.style.color = selectedColor;
}
setStyle(this.selectedElement, {
properties: [{property: this.deck || this.slide ? '--color' : 'color', value: selectedColor}],
type: this.deck ? 'deck' : this.slide ? 'slide' : 'element',
updateUI: async (_value: string) => await this.colorRef.loadColor(),
});

this.colorChange.emit();
}
Expand All @@ -99,11 +102,11 @@ export class AppColorTextBackground {
return;
}

if (this.deck || this.slide) {
this.selectedElement.style.setProperty('--background', selectedColor);
} else {
this.selectedElement.style.background = selectedColor;
}
setStyle(this.selectedElement, {
properties: [{value: selectedColor, property: this.deck || this.slide ? '--background' : 'background'}],
type: this.deck ? 'deck' : this.slide ? 'slide' : 'element',
updateUI: async (_value: string) => await this.colorRef.loadColor(),
});

this.colorChange.emit();
}
Expand All @@ -116,6 +119,7 @@ export class AppColorTextBackground {
<ion-label slot="title">{i18n.state.editor.color}</ion-label>

<app-color
ref={(el) => (this.colorRef = el as HTMLAppColorElement)}
initColor={this.colorType === 'background' ? this.initBackground : this.initColor}
onResetColor={() => this.resetColor()}
defaultColor={this.colorType === 'background' ? '#fff' : '#000'}
Expand Down
11 changes: 11 additions & 0 deletions studio/src/app/components/editor/styles/app-color/app-color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ export class AppColor {
@State()
private colorCSS: string;

private skipNextColorCSSEmit: boolean = false;

@Event()
colorDidChange: EventEmitter<string>;

Expand Down Expand Up @@ -79,6 +81,8 @@ export class AppColor {

this.opacity = opacity ? opacity : 100;

this.skipNextColorCSSEmit = true;

await this.initColorCSS();
}

Expand Down Expand Up @@ -127,6 +131,8 @@ export class AppColor {

$event.stopPropagation();

this.skipNextColorCSSEmit = true;

await this.initColorStateRgb(color.rgb);
await this.initColorCSS();

Expand Down Expand Up @@ -218,6 +224,11 @@ export class AppColor {
}

private async updateColorCSS() {
if (this.skipNextColorCSSEmit) {
this.skipNextColorCSSEmit = false;
return;
}

this.colorDidChange.emit(this.colorCSS);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import i18n from '../../../../../stores/i18n.store';

import {FontsService} from '../../../../../services/editor/fonts/fonts.service';

import {setStyle} from '../../../../../utils/editor/undo-redo.utils';

@Component({
tag: 'app-deck-fonts',
styleUrl: 'app-deck-fonts.scss',
Expand Down Expand Up @@ -51,15 +53,13 @@ export class AppDeckFonts {
return;
}

if (!font) {
this.deckElement.style.removeProperty('font-family');
} else {
this.deckElement.style.setProperty('font-family', font.family);
}
setStyle(this.deckElement, {
properties: [{value: !font ? null : font.family, property: 'font-family'}],
type: 'deck',
updateUI: async () => await this.initSelectedFont(),
});

this.fontsChange.emit();

await this.initSelectedFont();
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import i18n from '../../../../../stores/i18n.store';

import {DeckAction} from '../../../../../types/editor/deck-action';

import {setAttribute} from '../../../../../utils/editor/undo-redo.utils';

@Component({
tag: 'app-deck-transition',
styleUrl: 'app-deck-transition.scss',
Expand Down Expand Up @@ -141,9 +143,16 @@ export class AppDeckTransition {

await this.goToFirstSlide();

this.deckElement.setAttribute(this.device === 'mobile' ? 'direction-mobile' : 'direction', direction);
const resetDevice: 'desktop' | 'mobile' = this.device;

this.selectedDirection = direction;
setAttribute(this.deckElement, {
attribute: this.device === 'mobile' ? 'direction-mobile' : 'direction',
value: direction,
updateUI: (value: string) => {
this.device = resetDevice;
this.selectedDirection = value as 'horizontal' | 'vertical' | 'papyrus';
},
});

this.transitionChange.emit();

Expand Down Expand Up @@ -171,9 +180,11 @@ export class AppDeckTransition {
return;
}

this.deckElement.setAttribute('animation', animation);

this.selectedAnimation = animation;
setAttribute(this.deckElement, {
attribute: 'animation',
value: animation,
updateUI: (value: string) => (this.selectedAnimation = value as 'slide' | 'fade' | 'none'),
});

this.transitionChange.emit();
}
Expand Down
Loading

0 comments on commit 4315304

Please sign in to comment.