From 096ea720f5e8a7f5cb9dad34eee30b424c925d71 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 18 Apr 2020 12:45:13 +0200 Subject: [PATCH 1/2] feat(#680): order and small devices Signed-off-by: peterpeterparker --- .../app-actions-element.tsx | 20 ++++++++--- .../app-more-element-actions.tsx | 36 +++++++++++++++++-- studio/src/app/utils/editor/more-action.tsx | 4 ++- studio/src/components.d.ts | 4 +++ 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx b/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx index c437e1f5c..933616940 100644 --- a/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx +++ b/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx @@ -55,7 +55,7 @@ export class AppActionsElement { private align: TextAlign | undefined; @State() - private list: SlotType; + private list: SlotType | undefined; @Event() private blockSlide: EventEmitter; @@ -971,6 +971,8 @@ export class AppActionsElement { componentProps: { notes: this.slide, copy: this.slide || this.shape, + reveal: !this.hideReveal(), + list: this.list !== undefined, }, event: $event, mode: 'ios', @@ -984,6 +986,10 @@ export class AppActionsElement { await this.clone(); } else if (detail.data.action === MoreAction.DELETE) { await this.confirmDeleteElement($event); + } else if (detail.data.action === MoreAction.REVEAL) { + await this.openSingleAction($event, 'app-reveal'); + } else if (detail.data.action === MoreAction.LIST) { + await this.toggleList(); } } }); @@ -991,15 +997,19 @@ export class AppActionsElement { await popover.present(); } + private hideReveal(): boolean { + return this.slide || this.code || this.shape || this.slideNodeName === 'deckgo-slide-youtube'; + } + render() { return ( {this.renderEdit()} {this.renderShapes()} + {this.renderColor()} {this.renderReveal()} {this.renderAlign()} - {this.renderColor()} {this.renderList()} {this.renderImages()} {this.renderCodeOptions()} @@ -1122,7 +1132,7 @@ export class AppActionsElement { } private renderReveal() { - const classReveal: string | undefined = this.slide || this.code || this.shape || this.slideNodeName === 'deckgo-slide-youtube' ? 'hidden' : undefined; + const classReveal: string | undefined = this.hideReveal() ? 'hidden wider-devices' : 'wider-devices'; return ( this.toggleList()} aria-label="Toggle to an unordered list" color="primary" mode="md" class={classListUL}> diff --git a/studio/src/app/popovers/editor/actions/app-more-element-actions/app-more-element-actions.tsx b/studio/src/app/popovers/editor/actions/app-more-element-actions/app-more-element-actions.tsx index 0d2439c5d..c22e0611f 100644 --- a/studio/src/app/popovers/editor/actions/app-more-element-actions/app-more-element-actions.tsx +++ b/studio/src/app/popovers/editor/actions/app-more-element-actions/app-more-element-actions.tsx @@ -3,7 +3,7 @@ import {MoreAction} from '../../../../utils/editor/more-action'; @Component({ tag: 'app-more-element-actions', - styleUrl: 'app-more-element-actions.scss' + styleUrl: 'app-more-element-actions.scss', }) export class AppMoreElementActions { @Element() el: HTMLElement; @@ -14,9 +14,15 @@ export class AppMoreElementActions { @Prop() copy: boolean = false; + @Prop() + reveal: boolean = false; + + @Prop() + list: boolean = false; + private async closePopover(action: MoreAction) { await (this.el.closest('ion-popover') as HTMLIonPopoverElement).dismiss({ - action: action + action: action, }); } @@ -25,6 +31,8 @@ export class AppMoreElementActions {
{this.renderNotes()} {this.renderCopy()} + {this.renderReveal()} + {this.renderList()} {this.renderDelete()}
); @@ -54,6 +62,30 @@ export class AppMoreElementActions { ); } + private renderList() { + if (!this.list) { + return undefined; + } + + return ( + this.closePopover(MoreAction.LIST)} aria-label="List"> +

List

+
+ ); + } + + private renderReveal() { + if (!this.reveal) { + return undefined; + } + + return ( + this.closePopover(MoreAction.REVEAL)} aria-label="Animation"> +

Animation

+
+ ); + } + private renderDelete() { return ( this.closePopover(MoreAction.DELETE)} aria-label="Delete"> diff --git a/studio/src/app/utils/editor/more-action.tsx b/studio/src/app/utils/editor/more-action.tsx index 7cda8ae36..878c036d6 100644 --- a/studio/src/app/utils/editor/more-action.tsx +++ b/studio/src/app/utils/editor/more-action.tsx @@ -9,5 +9,7 @@ export enum MoreAction { NOTES, DELETE, HELP, - OFFLINE + OFFLINE, + LIST, + REVEAL, } diff --git a/studio/src/components.d.ts b/studio/src/components.d.ts index ca2a55fe5..79065c424 100644 --- a/studio/src/components.d.ts +++ b/studio/src/components.d.ts @@ -229,7 +229,9 @@ export namespace Components { } interface AppMoreElementActions { 'copy': boolean; + 'list': boolean; 'notes': boolean; + 'reveal': boolean; } interface AppMoreShareOptions {} interface AppNavigation { @@ -1238,7 +1240,9 @@ declare namespace LocalJSX { } interface AppMoreElementActions { 'copy'?: boolean; + 'list'?: boolean; 'notes'?: boolean; + 'reveal'?: boolean; } interface AppMoreShareOptions {} interface AppNavigation { From 5d49b08d9214f977f4324423abbef5f7980a7973 Mon Sep 17 00:00:00 2001 From: peterpeterparker Date: Sat, 18 Apr 2020 13:15:40 +0200 Subject: [PATCH 2/2] feat(#680): toggle list with popover (new standard UX) Signed-off-by: peterpeterparker --- .../app-actions-element.tsx | 61 ++++++++----------- .../popovers/editor/app-list/app-list.scss | 16 +++++ .../app/popovers/editor/app-list/app-list.tsx | 48 +++++++++++++++ studio/src/app/utils/editor/list.utils.tsx | 16 +++++ studio/src/components.d.ts | 15 +++++ 5 files changed, 121 insertions(+), 35 deletions(-) create mode 100644 studio/src/app/popovers/editor/app-list/app-list.scss create mode 100644 studio/src/app/popovers/editor/app-list/app-list.tsx create mode 100644 studio/src/app/utils/editor/list.utils.tsx diff --git a/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx b/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx index 933616940..ba2749757 100644 --- a/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx +++ b/studio/src/app/components/editor/actions/element/app-actions-element/app-actions-element.tsx @@ -1,4 +1,4 @@ -import {Component, Element, Event, EventEmitter, h, Listen, Method, State, Prop} from '@stencil/core'; +import {Component, Element, Event, EventEmitter, h, Listen, Method, Prop, State} from '@stencil/core'; import {modalController, OverlayEventDetail, popoverController} from '@ionic/core'; import {Subject, Subscription} from 'rxjs'; @@ -14,6 +14,7 @@ import {RevealSlotUtils} from '../../../../../utils/editor/reveal-slot.utils'; import {SlotType} from '../../../../../utils/editor/slot-type'; import {SlotUtils} from '../../../../../utils/editor/slot.utils'; import {AlignUtils, TextAlign} from '../../../../../utils/editor/align.utils'; +import {ListUtils} from '../../../../../utils/editor/list.utils'; import {EditAction} from '../../../../../utils/editor/edit-action'; import {MoreAction} from '../../../../../utils/editor/more-action'; @@ -55,7 +56,7 @@ export class AppActionsElement { private align: TextAlign | undefined; @State() - private list: SlotType | undefined; + private list: SlotType.OL | SlotType.UL | undefined; @Event() private blockSlide: EventEmitter; @@ -243,18 +244,6 @@ export class AppActionsElement { return element && element.nodeName && element.nodeName.toLowerCase() === SlotType.DRAG_RESIZE_ROTATE; } - private isElementList(element: HTMLElement): SlotType { - if (!SlotUtils.isNodeList(element)) { - return undefined; - } - - if (SlotUtils.isNodeRevealList(element)) { - return element && element.getAttribute('list-tag') === SlotType.UL ? SlotType.UL : SlotType.OL; - } else { - return element && element.nodeName && element.nodeName.toLowerCase() === SlotType.OL ? SlotType.OL : SlotType.UL; - } - } - private isElementImage(element: HTMLElement): boolean { return element && element.nodeName && element.nodeName.toLowerCase() === SlotType.IMG; } @@ -570,7 +559,7 @@ export class AppActionsElement { await modal.present(); } - private async openSingleAction($event: UIEvent, component: 'app-reveal' | 'app-align') { + private async openSingleAction($event: UIEvent, component: 'app-reveal' | 'app-align' | 'app-list') { if (this.slide) { return; } @@ -590,6 +579,8 @@ export class AppActionsElement { await this.toggleReveal(detail.data.reveal); } else if (detail.data && component === 'app-align') { await this.updateAlignAttribute(detail.data.align); + } else if (detail.data && component === 'app-list') { + await this.toggleList(detail.data.list); } }); @@ -749,7 +740,7 @@ export class AppActionsElement { this.align = await AlignUtils.getAlignment(element); - this.list = this.isElementList(element); + this.list = await ListUtils.isElementList(element); if (element) { element.addEventListener('paste', this.cleanOnPaste, false); @@ -879,23 +870,19 @@ export class AppActionsElement { }); } - private toggleList(): Promise { + private toggleList(destinationListType: SlotType.OL | SlotType.UL): Promise { return new Promise(async (resolve) => { if (!this.selectedElement || !this.list) { resolve(); return; } - const destinationListType: SlotType = this.list === SlotType.UL ? SlotType.OL : SlotType.UL; - if (SlotUtils.isNodeRevealList(this.selectedElement)) { await this.updateRevealListAttribute(destinationListType); } else { await this.toggleSlotType(destinationListType); } - this.list = destinationListType; - resolve(); }); } @@ -989,7 +976,7 @@ export class AppActionsElement { } else if (detail.data.action === MoreAction.REVEAL) { await this.openSingleAction($event, 'app-reveal'); } else if (detail.data.action === MoreAction.LIST) { - await this.toggleList(); + await this.openSingleAction($event, 'app-list'); } } }); @@ -1157,26 +1144,30 @@ export class AppActionsElement { color="primary" mode="md" class={classAlign}> - + {this.align !== undefined ? ( + + ) : ( + + )} Alignment
); } private renderList() { - const classListOL: string | undefined = this.list === SlotType.OL ? 'wider-devices' : 'wider-devices hidden'; - const classListUL: string | undefined = this.list === SlotType.UL ? 'wider-devices' : 'wider-devices hidden'; + const classList: string | undefined = this.list === undefined ? 'hidden wider-devices' : 'wider-devices'; - return [ - this.toggleList()} aria-label="Toggle to an unordered list" color="primary" mode="md" class={classListUL}> - - Unordered list - , - this.toggleList()} aria-label="Toggle to an ordered list" color="primary" mode="md" class={classListOL}> - - Ordered list - , - ]; + return ( + this.openSingleAction($event, 'app-list')} + aria-label="Edit ordered or unordered list" + color="primary" + mode="md" + class={classList}> + + List + + ); } private renderMore() { diff --git a/studio/src/app/popovers/editor/app-list/app-list.scss b/studio/src/app/popovers/editor/app-list/app-list.scss new file mode 100644 index 000000000..1e718308d --- /dev/null +++ b/studio/src/app/popovers/editor/app-list/app-list.scss @@ -0,0 +1,16 @@ +app-list { + @import "../../../../global/theme/editor/editor-popover"; + + ion-item { + --background-activated: transparent; + --background-focused: transparent; + --background-hover: transparent; + + cursor: pointer; + + &:hover, + &.active { + --color: var(--ion-color-primary); + } + } +} diff --git a/studio/src/app/popovers/editor/app-list/app-list.tsx b/studio/src/app/popovers/editor/app-list/app-list.tsx new file mode 100644 index 000000000..3e19bed43 --- /dev/null +++ b/studio/src/app/popovers/editor/app-list/app-list.tsx @@ -0,0 +1,48 @@ +import {Component, Element, h, Prop, State} from '@stencil/core'; + +import {SlotType} from '../../../utils/editor/slot-type'; +import {ListUtils} from '../../../utils/editor/list.utils'; + +@Component({ + tag: 'app-list', + styleUrl: 'app-list.scss', +}) +export class AppList { + @Element() el: HTMLElement; + + @Prop() + selectedElement: HTMLElement; + + @State() + private currentList: SlotType.OL | SlotType.UL | undefined; + + async componentWillLoad() { + this.currentList = await ListUtils.isElementList(this.selectedElement); + } + + private async closePopover(list: SlotType.OL | SlotType.UL) { + await (this.el.closest('ion-popover') as HTMLIonPopoverElement).dismiss({ + list: list, + }); + } + + private async selectList(list: SlotType.OL | SlotType.UL) { + await this.closePopover(list); + } + + render() { + return ( + + this.selectList(SlotType.UL)} class={this.currentList == SlotType.UL ? 'active' : undefined}> + + Unordered list + + + this.selectList(SlotType.OL)} class={this.currentList == SlotType.OL ? 'active' : undefined}> + + Ordered list + + + ); + } +} diff --git a/studio/src/app/utils/editor/list.utils.tsx b/studio/src/app/utils/editor/list.utils.tsx new file mode 100644 index 000000000..47f7317f7 --- /dev/null +++ b/studio/src/app/utils/editor/list.utils.tsx @@ -0,0 +1,16 @@ +import {SlotType} from './slot-type'; +import {SlotUtils} from './slot.utils'; + +export class ListUtils { + static async isElementList(element: HTMLElement): Promise { + if (!SlotUtils.isNodeList(element)) { + return undefined; + } + + if (SlotUtils.isNodeRevealList(element)) { + return element && element.getAttribute('list-tag') === SlotType.UL ? SlotType.UL : SlotType.OL; + } else { + return element && element.nodeName && element.nodeName.toLowerCase() === SlotType.OL ? SlotType.OL : SlotType.UL; + } + } +} diff --git a/studio/src/components.d.ts b/studio/src/components.d.ts index 79065c424..f6b77cd97 100644 --- a/studio/src/components.d.ts +++ b/studio/src/components.d.ts @@ -222,6 +222,9 @@ export namespace Components { interface AppLandingContent {} interface AppLandingDeck {} interface AppLandingFooter {} + interface AppList { + 'selectedElement': HTMLElement; + } interface AppLogo {} interface AppMenu {} interface AppMoreDeckActions { @@ -665,6 +668,12 @@ declare global { new (): HTMLAppLandingFooterElement; }; + interface HTMLAppListElement extends Components.AppList, HTMLStencilElement {} + var HTMLAppListElement: { + prototype: HTMLAppListElement; + new (): HTMLAppListElement; + }; + interface HTMLAppLogoElement extends Components.AppLogo, HTMLStencilElement {} var HTMLAppLogoElement: { prototype: HTMLAppLogoElement; @@ -967,6 +976,7 @@ declare global { 'app-landing-content': HTMLAppLandingContentElement; 'app-landing-deck': HTMLAppLandingDeckElement; 'app-landing-footer': HTMLAppLandingFooterElement; + 'app-list': HTMLAppListElement; 'app-logo': HTMLAppLogoElement; 'app-menu': HTMLAppMenuElement; 'app-more-deck-actions': HTMLAppMoreDeckActionsElement; @@ -1233,6 +1243,9 @@ declare namespace LocalJSX { interface AppLandingContent {} interface AppLandingDeck {} interface AppLandingFooter {} + interface AppList { + 'selectedElement'?: HTMLElement; + } interface AppLogo {} interface AppMenu {} interface AppMoreDeckActions { @@ -1400,6 +1413,7 @@ declare namespace LocalJSX { 'app-landing-content': AppLandingContent; 'app-landing-deck': AppLandingDeck; 'app-landing-footer': AppLandingFooter; + 'app-list': AppList; 'app-logo': AppLogo; 'app-menu': AppMenu; 'app-more-deck-actions': AppMoreDeckActions; @@ -1506,6 +1520,7 @@ declare module "@stencil/core" { 'app-landing-content': LocalJSX.AppLandingContent & JSXBase.HTMLAttributes; 'app-landing-deck': LocalJSX.AppLandingDeck & JSXBase.HTMLAttributes; 'app-landing-footer': LocalJSX.AppLandingFooter & JSXBase.HTMLAttributes; + 'app-list': LocalJSX.AppList & JSXBase.HTMLAttributes; 'app-logo': LocalJSX.AppLogo & JSXBase.HTMLAttributes; 'app-menu': LocalJSX.AppMenu & JSXBase.HTMLAttributes; 'app-more-deck-actions': LocalJSX.AppMoreDeckActions & JSXBase.HTMLAttributes;