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

Commit d62be56

Browse files
feat: add slide from aside menu and other improvements (#1296)
* feat: add slide button at the bottom of the thumbnails * feat: handles delete slide * style: ripple effect * fix: semantic * feat: effectively add slide * feat: if thumbnails slides are displayed hide duplicated wide screen actions * feat: popover position if triggered from aside * style: split thumbnail * feat: do not hide slide navigation on mobile * fix: drag and drop quirks * feat: highlight current slide
1 parent d8e41e0 commit d62be56

File tree

16 files changed

+278
-171
lines changed

16 files changed

+278
-171
lines changed
Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,91 @@
1+
@use "../../../../../global/theme/mixins/button";
2+
13
app-slides-aside {
2-
display: flex;
3-
flex-direction: column;
44

5-
min-height: 100%;
6-
height: 100%;
5+
position: relative;
6+
7+
aside {
8+
display: flex;
9+
flex-direction: column;
10+
11+
min-height: 100%;
12+
height: 100%;
713

8-
width: var(--slides-aside-width);
14+
width: var(--slides-aside-width);
915

10-
padding: 16px;
11-
overflow: scroll;
12-
border-right: 1px solid #dedede;
16+
padding: 16px 16px 48px;
17+
overflow: scroll;
18+
border-right: 1px solid #dedede;
1319

14-
--preview-width: calc(var(--slides-aside-width) - 32px);
20+
--preview-width: calc(var(--slides-aside-width) - 32px);
21+
22+
&.drag {
23+
app-slide-thumbnail {
24+
transition: margin 0.25s ease-out, min-height 0.25s ease-in;
25+
}
26+
}
1527

16-
&.drag {
1728
app-slide-thumbnail {
18-
transition: margin 0.25s ease-out, min-height 0.25s ease-in;
29+
margin-bottom: 16px;
30+
31+
transition: margin 0.15s ease-in;
32+
33+
&.highlight {
34+
border: 1px solid var(--ion-color-dark);
35+
box-shadow: rgba(var(--ion-color-dark-rgb), 0.4) 0 1px 4px;
36+
}
37+
38+
&.hover {
39+
margin-bottom: calc(var(--slides-aside-width) * 9 / 16);
40+
}
41+
42+
&.hover-top {
43+
margin-top: calc(var(--slides-aside-width) * 9 / 16);
44+
}
45+
46+
&.drag-start, &.drag-hover {
47+
visibility: hidden;
48+
opacity: 0;
49+
}
50+
51+
&.drag-hover {
52+
min-height: 0;
53+
height: 0;
54+
border: none;
55+
}
1956
}
2057
}
2158

22-
app-slide-thumbnail {
23-
margin-bottom: 16px;
59+
div.actions {
60+
position: absolute;
61+
bottom: 0;
62+
left: 0;
2463

25-
transition: margin 0.15s ease-in;
64+
display: flex;
65+
justify-content: center;
66+
align-items: center;
67+
flex-direction: column;
2668

27-
&.hover {
28-
margin-bottom: calc((var(--slides-aside-width) - 32px) * 9 / 16);
29-
}
69+
width: 100%;
3070

31-
&.hover-top {
32-
margin-top: calc((var(--slides-aside-width) - 32px) * 9 / 16);
33-
}
71+
background: var(--ion-color-light);
72+
border: 1px solid #dedede;
3473

35-
&.drag-start, &.drag-hover {
36-
visibility: hidden;
37-
opacity: 0;
38-
}
74+
padding: 8px 16px;
75+
}
76+
77+
app-action-add-slide {
78+
button {
79+
--button-action-flex-direction: row;
80+
81+
@include button.action;
82+
83+
ion-icon {
84+
@include button.icon;
3985

40-
&.drag-hover {
41-
min-height: 0;
42-
border: none;
86+
font-size: 14px;
87+
margin: 0 4px 0 0;
88+
}
4389
}
4490
}
4591
}

studio/src/app/components/editor/actions/app-slides-aside/app-slides-aside.tsx

Lines changed: 91 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ export class AppSlidesAside {
1515
@State()
1616
private slides: HTMLElement[] = [];
1717

18+
@Prop()
19+
activeIndex: number;
20+
1821
@Prop()
1922
deckRef!: HTMLDeckgoDeckElement;
2023

@@ -28,6 +31,9 @@ export class AppSlidesAside {
2831

2932
private readonly debounceUpdateSlide: (updateSlide: HTMLElement) => void;
3033

34+
private canDragLeave: boolean = true;
35+
private canDragHover: boolean = true;
36+
3137
constructor() {
3238
this.debounceUpdateAllSlides = debounce(async () => {
3339
await this.updateAllSlides();
@@ -42,6 +48,13 @@ export class AppSlidesAside {
4248
this.debounceUpdateAllSlides();
4349
}
4450

51+
componentDidUpdate() {
52+
setTimeout(() => {
53+
this.canDragLeave = true;
54+
this.canDragHover = true;
55+
}, 250);
56+
}
57+
4558
@Listen('deckDidLoad', {target: 'document'})
4659
onDeckDidLoad() {
4760
this.debounceUpdateAllSlides();
@@ -57,12 +70,27 @@ export class AppSlidesAside {
5770
this.debounceUpdateSlide(updatedSlide);
5871
}
5972

73+
@Listen('slideDelete', {target: 'document'})
74+
async onSlideDelete({detail: deletedSlide}: CustomEvent<HTMLElement>) {
75+
await this.deleteSlide(deletedSlide);
76+
}
77+
6078
private async updateSlide(updatedSlide: HTMLElement) {
61-
const slideIndex: number = Array.from(updatedSlide.parentNode.children).indexOf(updatedSlide);
79+
const slideIndex: number = this.slideIndex(updatedSlide);
6280

6381
this.slides = [...this.slides.map((slide: HTMLElement, index: number) => (slideIndex === index ? (updatedSlide.cloneNode(true) as HTMLElement) : slide))];
6482
}
6583

84+
private async deleteSlide(deletedSlide: HTMLElement) {
85+
const slideIndex: number = this.slideIndex(deletedSlide);
86+
87+
this.slides = [...this.slides.filter((_slide: HTMLElement, index: number) => slideIndex !== index)];
88+
}
89+
90+
private slideIndex(slide: HTMLElement): number {
91+
return Array.from(slide.parentNode.children).indexOf(slide);
92+
}
93+
6694
private async updateAllSlides() {
6795
const slides: NodeListOf<HTMLElement> = document.querySelectorAll(`${deckSelector} > *`);
6896

@@ -84,17 +112,29 @@ export class AppSlidesAside {
84112
}
85113

86114
private onDragHover(to: number) {
87-
if (!this.reorderDetail) {
115+
if (!this.canDragHover) {
88116
return;
89117
}
90118

119+
if (!this.reorderDetail || this.reorderDetail.to === to) {
120+
return;
121+
}
122+
123+
if (this.reorderDetail.to === -1 && to === 0) {
124+
this.canDragLeave = false;
125+
}
126+
91127
this.reorderDetail = {
92128
...this.reorderDetail,
93129
to
94130
};
95131
}
96132

97133
private onDragLeave() {
134+
if (!this.canDragLeave) {
135+
return;
136+
}
137+
98138
if (!this.reorderDetail) {
99139
return;
100140
}
@@ -103,6 +143,8 @@ export class AppSlidesAside {
103143
return;
104144
}
105145

146+
this.canDragHover = false;
147+
106148
this.reorderDetail = {
107149
...this.reorderDetail,
108150
to: -1
@@ -131,34 +173,57 @@ export class AppSlidesAside {
131173

132174
render() {
133175
return (
134-
<Host
176+
<Host>
177+
{this.renderSlides()}
178+
179+
{this.renderActions()}
180+
</Host>
181+
);
182+
}
183+
184+
private renderSlides() {
185+
return (
186+
<aside
135187
onDrop={() => this.onDrop()}
136188
onDragOver={($event: DragEvent) => $event.preventDefault()}
137189
onDragLeave={() => this.onDragLeave()}
138190
class={this.reorderDetail !== undefined ? 'drag' : ''}>
139-
{this.slides.map((slide: HTMLElement, index: number) => (
140-
<app-slide-thumbnail
141-
custom-tappable
142-
onClick={async () => await slideTo(index)}
143-
key={slide.getAttribute('slide_id')}
144-
slide={slide}
145-
deck={this.deckRef}
146-
class={
147-
index === this.reorderDetail?.to && this.reorderDetail?.from !== this.reorderDetail?.to
148-
? 'hover'
149-
: index === 0 && this.reorderDetail?.to === -1
150-
? 'hover-top'
151-
: index === this.reorderDetail?.from
152-
? index === this.reorderDetail?.to
153-
? 'drag-start'
154-
: 'drag-hover'
155-
: ''
156-
}
157-
draggable={true}
158-
onDragStart={() => this.onDragStart(index)}
159-
onDragOver={() => this.onDragHover(index)}></app-slide-thumbnail>
160-
))}
161-
</Host>
191+
{this.slides.map((slide: HTMLElement, index: number) => this.renderThumbnail(slide, index))}
192+
</aside>
193+
);
194+
}
195+
196+
private renderThumbnail(slide: HTMLElement, index: number) {
197+
const dragClass: string =
198+
index === this.reorderDetail?.to && this.reorderDetail?.from !== this.reorderDetail?.to
199+
? 'hover'
200+
: index === 0 && this.reorderDetail?.to === -1
201+
? 'hover-top'
202+
: index === this.reorderDetail?.from
203+
? index === this.reorderDetail?.to
204+
? 'drag-start'
205+
: 'drag-hover'
206+
: '';
207+
208+
return (
209+
<app-slide-thumbnail
210+
custom-tappable
211+
onClick={async () => await slideTo(index)}
212+
key={slide.getAttribute('slide_id')}
213+
slide={slide}
214+
deck={this.deckRef}
215+
class={`${dragClass} ${this.activeIndex === index ? 'highlight' : ''}`}
216+
draggable={true}
217+
onDragStart={() => this.onDragStart(index)}
218+
onDragOver={() => this.onDragHover(index)}></app-slide-thumbnail>
219+
);
220+
}
221+
222+
private renderActions() {
223+
return (
224+
<div class="actions">
225+
<app-action-add-slide slidesLength={this.slides.length} popoverCssClass="popover-menu-wide-start"></app-action-add-slide>
226+
</div>
162227
);
163228
}
164229
}

studio/src/app/components/editor/actions/deck/app-action-add-slide/app-action-add-slide.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, Element, EventEmitter, h, JSX, Prop} from '@stencil/core';
1+
import {Component, Element, Event, EventEmitter, h, JSX, Prop} from '@stencil/core';
22

33
import {modalController, OverlayEventDetail, popoverController} from '@ionic/core';
44

@@ -20,16 +20,19 @@ export class AppActionAddSlide {
2020
@Element() el: HTMLElement;
2121

2222
@Prop()
23-
slides: JSX.IntrinsicElements[] = [];
23+
slidesLength: number | undefined;
2424

2525
@Prop()
26-
blockSlide: EventEmitter;
26+
popoverCssClass: string;
2727

28-
@Prop()
29-
signIn: EventEmitter;
28+
@Event({bubbles: true})
29+
private signIn: EventEmitter<void>;
3030

31-
@Prop()
32-
addSlide: EventEmitter;
31+
@Event({bubbles: true})
32+
private addSlide: EventEmitter<JSX.IntrinsicElements>;
33+
34+
@Event({bubbles: true})
35+
private blockSlide: EventEmitter<boolean>;
3336

3437
private anonymousService: AnonymousService;
3538

@@ -42,7 +45,7 @@ export class AppActionAddSlide {
4245
return;
4346
}
4447

45-
const couldAddSlide: boolean = await this.anonymousService.couldAddSlide(this.slides);
48+
const couldAddSlide: boolean = await this.anonymousService.couldAddSlide(this.slidesLength);
4649

4750
if (!couldAddSlide) {
4851
this.signIn.emit();
@@ -53,7 +56,7 @@ export class AppActionAddSlide {
5356
component: 'app-create-slide',
5457
mode: 'ios',
5558
showBackdrop: false,
56-
cssClass: 'popover-menu popover-menu-wide'
59+
cssClass: `popover-menu popover-menu-wide ${this.popoverCssClass}`
5760
});
5861

5962
popover.onDidDismiss().then(async (detail: OverlayEventDetail) => {
@@ -276,10 +279,7 @@ export class AppActionAddSlide {
276279

277280
render() {
278281
return (
279-
<app-action-busy
280-
aria-label={i18n.state.editor.add_slide}
281-
iconName="add"
282-
onActionReady={($event: CustomEvent) => this.onActionOpenSlideAdd($event)}>
282+
<app-action-busy aria-label={i18n.state.editor.add_slide} iconName="add" onActionReady={($event: CustomEvent) => this.onActionOpenSlideAdd($event)}>
283283
<ion-label aria-hidden="true">{i18n.state.editor.add_slide}</ion-label>
284284
</app-action-busy>
285285
);

0 commit comments

Comments
 (0)