mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
feat(tour): add basic steps
This commit is contained in:
parent
64ba20a227
commit
74d1c40964
14 changed files with 882 additions and 206 deletions
10
angular.json
10
angular.json
|
|
@ -35,7 +35,10 @@
|
|||
"src/manifest.json",
|
||||
"src/static"
|
||||
],
|
||||
"styles": ["src/styles.scss"],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
"node_modules/shepherd.js/dist/css/shepherd.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"webWorkerTsConfig": "src/tsconfig.worker.json"
|
||||
},
|
||||
|
|
@ -210,7 +213,10 @@
|
|||
"tsConfig": "src/tsconfig.spec.json",
|
||||
"preserveSymlinks": true,
|
||||
"karmaConfig": "src/karma.conf.js",
|
||||
"styles": ["src/styles.scss"],
|
||||
"styles": [
|
||||
"src/styles.scss",
|
||||
"node_modules/shepherd.js/dist/css/shepherd.css"
|
||||
],
|
||||
"scripts": [],
|
||||
"assets": ["src/favicon.ico", "src/assets", "src/manifest.json"]
|
||||
}
|
||||
|
|
|
|||
710
package-lock.json
generated
710
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -107,12 +107,14 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.12",
|
||||
"angular-shepherd": "^14.0.0",
|
||||
"electron-dl": "^3.5.1",
|
||||
"electron-localshortcut": "^3.2.1",
|
||||
"electron-log": "^5.0.1",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"fs-extra": "^11.1.1",
|
||||
"node-fetch": "^2.7.0"
|
||||
"node-fetch": "^2.7.0",
|
||||
"shepherd.js": "^11.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^14.1.1",
|
||||
|
|
|
|||
|
|
@ -110,3 +110,5 @@
|
|||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<shepherd></shepherd>
|
||||
|
|
|
|||
|
|
@ -51,13 +51,14 @@ import { WelcomeModule } from './features/welcome/welcome.module';
|
|||
import { DominaModeModule } from './features/domina-mode/domina-mode.module';
|
||||
import { FocusModeModule } from './features/focus-mode/focus-mode.module';
|
||||
import { CalendarIntegrationModule } from './features/calendar-integration/calendar-integration.module';
|
||||
import { ShepherdComponent } from './features/shepherd/shepherd.component';
|
||||
|
||||
// NOTE: export required for aot to work
|
||||
export const createTranslateLoader = (http: HttpClient): TranslateHttpLoader =>
|
||||
new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent],
|
||||
declarations: [AppComponent, ShepherdComponent],
|
||||
imports: [
|
||||
// Those features need to be included first for store not to mess up, probably because we use it initially at many places
|
||||
ConfigModule,
|
||||
|
|
|
|||
52
src/app/features/shepherd/shepherd-helper.ts
Normal file
52
src/app/features/shepherd/shepherd-helper.ts
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import { Observable, Subject } from 'rxjs';
|
||||
import Shepherd from 'shepherd.js';
|
||||
import Step = Shepherd.Step;
|
||||
import { first, takeUntil, tap } from 'rxjs/operators';
|
||||
|
||||
export const waitForEl = (selector: string, cb: () => void): void => {
|
||||
const int = window.setInterval(() => {
|
||||
if (document.querySelector(selector)) {
|
||||
window.clearInterval(int);
|
||||
cb();
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
export const waitForElRemove = (
|
||||
el: HTMLElement | Element | null,
|
||||
cb: () => void,
|
||||
): void => {
|
||||
if (!el) {
|
||||
throw new Error('No el provided');
|
||||
}
|
||||
const int = window.setInterval(() => {
|
||||
if (!document.contains(el)) {
|
||||
window.clearInterval(int);
|
||||
cb();
|
||||
}
|
||||
}, 50);
|
||||
};
|
||||
|
||||
export const waitForObs = (
|
||||
obs: Observable<any>,
|
||||
cb: () => void,
|
||||
): Partial<Step.StepOptions> => {
|
||||
let _onDestroy$;
|
||||
return {
|
||||
when: {
|
||||
show: () => {
|
||||
_onDestroy$ = new Subject<void>();
|
||||
obs
|
||||
.pipe(
|
||||
takeUntil(_onDestroy$),
|
||||
tap((v) => console.log('waitForObs', v)),
|
||||
first(),
|
||||
)
|
||||
.subscribe(() => cb());
|
||||
},
|
||||
hide: () => {
|
||||
_onDestroy$.next();
|
||||
_onDestroy$.complete();
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
207
src/app/features/shepherd/shepherd-steps.const.ts
Normal file
207
src/app/features/shepherd/shepherd-steps.const.ts
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
import Step from 'shepherd.js/src/types/step';
|
||||
import { ShepherdService } from 'angular-shepherd';
|
||||
import { waitForEl, waitForObs } from './shepherd-helper';
|
||||
import { LayoutService } from '../../core-ui/layout/layout.service';
|
||||
import { TaskService } from '../tasks/task.service';
|
||||
import { distinctUntilChanged, filter, skip, tap } from 'rxjs/operators';
|
||||
import { promiseTimeout } from '../../util/promise-timeout';
|
||||
|
||||
const NEXT_BTN = {
|
||||
classes: 'shepherd-button-primary',
|
||||
text: 'Next',
|
||||
type: 'next',
|
||||
};
|
||||
|
||||
export const SHEPHERD_STANDARD_BTNS = [
|
||||
{
|
||||
classes: 'shepherd-button-secondary',
|
||||
text: 'Exit',
|
||||
type: 'cancel',
|
||||
},
|
||||
{
|
||||
classes: 'shepherd-button-primary',
|
||||
text: 'Back',
|
||||
type: 'back',
|
||||
},
|
||||
NEXT_BTN,
|
||||
];
|
||||
|
||||
export const SHEPHERD_STEPS = (
|
||||
shepherdService: ShepherdService,
|
||||
layoutService: LayoutService,
|
||||
taskService: TaskService,
|
||||
): Array<Step.StepOptions> => [
|
||||
// TODO remove
|
||||
{
|
||||
title: 'YXXXXO',
|
||||
// beforeShowPromise: () => promiseTimeout(200),
|
||||
when: {
|
||||
show: () => {
|
||||
setTimeout(() => {
|
||||
shepherdService.next();
|
||||
}, 500);
|
||||
},
|
||||
},
|
||||
buttons: [],
|
||||
},
|
||||
// {
|
||||
// title: 'Welcome to Super Productivity!!',
|
||||
// text: 'Super Productivity is a ToDo app that helps you to improve your personal workflows.',
|
||||
// buttons: SHEPHERD_STANDARD_BTNS,
|
||||
// },
|
||||
// {
|
||||
// attachTo: {
|
||||
// element: '.action-nav button',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// when: {
|
||||
// show: () => {
|
||||
// waitForEl('app-root > add-task-bar input', () => shepherdService.next());
|
||||
// },
|
||||
// },
|
||||
// scrollTo: false,
|
||||
// title: "Let's add your first task!",
|
||||
// text: 'Click on this button or press <kbd>Shift</kbd> + <kbd>a</kbd>',
|
||||
// advanceOn: {
|
||||
// selector: '.action-nav button',
|
||||
// event: 'click',
|
||||
// },
|
||||
// },
|
||||
//
|
||||
// {
|
||||
// attachTo: {
|
||||
// element: 'add-task-bar',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// beforeShowPromise: () => promiseTimeout(1000),
|
||||
// when: {
|
||||
// show: () => {
|
||||
// waitForEl('task', () => {
|
||||
// layoutService.hideAddTaskBar();
|
||||
// shepherdService.next();
|
||||
// });
|
||||
// },
|
||||
// },
|
||||
// scrollTo: false,
|
||||
// title: 'Enter a title!',
|
||||
// text: 'Enter the title you want to give your task and hit the <kbd>Enter</kbd> key. After that you can press the <kbd>Escape</kbd> key or click anywhere on the grayed out backdrop to leave the add task bar.',
|
||||
// },
|
||||
// {
|
||||
// title: 'Congrats! This is your first task!',
|
||||
// text: 'Hover over it with your mouse',
|
||||
// attachTo: {
|
||||
// element: 'task',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// beforeShowPromise: () => promiseTimeout(1000),
|
||||
// when: {
|
||||
// show: () => {
|
||||
// setTimeout(() => {
|
||||
// waitForEl('task .hover-controls', () => shepherdService.next());
|
||||
// }, 1000);
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: 'Start Tracking',
|
||||
// attachTo: {
|
||||
// element: '.start-task-btn',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// text: 'Pressing the play button will start your first time tracking session. Time tracking is useful since it allows you to get a better idea on how you spend your time.',
|
||||
// // attachTo: {
|
||||
// // element: 'add-task-bar',
|
||||
// // on: 'bottom',
|
||||
// // },
|
||||
//
|
||||
// ...waitForObs(taskService.currentTaskId$.pipe(filter((id) => !!id)), () =>
|
||||
// shepherdService.next(),
|
||||
// ),
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// title: 'Stop Tracking',
|
||||
// text: 'To stop tracking click on the pause button.',
|
||||
// attachTo: {
|
||||
// element: '.start-task-btn',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// ...waitForObs(taskService.currentTaskId$.pipe(filter((id) => !id)), () =>
|
||||
// shepherdService.next(),
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// title: 'Edit Task Title',
|
||||
// text: 'You can edit the task title by clicking on it. Do this now and change the title to something else!',
|
||||
// attachTo: {
|
||||
// element: '.task-title',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// advanceOn: {
|
||||
// selector: '.task-title',
|
||||
// event: 'blur',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: 'Task Side Panel',
|
||||
// text: 'There is more you you can do with task. Hover over the task you created with your mouse again.',
|
||||
// buttons: [],
|
||||
// attachTo: {
|
||||
// element: 'task',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// beforeShowPromise: () => promiseTimeout(500),
|
||||
// when: {
|
||||
// show: () => {
|
||||
// setTimeout(() => {
|
||||
// waitForEl('task .hover-controls', () => shepherdService.next());
|
||||
// }, 200);
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// title: 'Opening Task Side Panel',
|
||||
// attachTo: {
|
||||
// element: '.show-additional-info-btn',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// text: 'You can open a panel with additional controls by clicking on the button. Alternatively you can press the <kbd>➔</kbd> key when a task is focused.',
|
||||
// buttons: [],
|
||||
// ...waitForObs(
|
||||
// taskService.selectedTask$.pipe(filter((selectedTask) => !!selectedTask)),
|
||||
// () => shepherdService.next(),
|
||||
// ),
|
||||
// },
|
||||
// {
|
||||
// title: 'The Task Side Panel',
|
||||
// text: 'In the task side panel you can adjust estimates, schedule your task, add a description or attachments or configure your task to be repeated.',
|
||||
// buttons: [NEXT_BTN],
|
||||
// },
|
||||
// {
|
||||
// title: 'Closing the Task Side Panel',
|
||||
// text: 'You can close the panel by clicking the X or by pressing <kbd>←</kbd>. Do this now!',
|
||||
// attachTo: {
|
||||
// element: '.show-additional-info-btn',
|
||||
// on: 'bottom',
|
||||
// },
|
||||
// ...waitForObs(
|
||||
// taskService.selectedTask$.pipe(filter((selectedTask) => !selectedTask)),
|
||||
// () => shepherdService.next(),
|
||||
// ),
|
||||
// },
|
||||
{
|
||||
title: 'Deleting a Task',
|
||||
text: 'To delete a task you need to open the task context menu.',
|
||||
when: {
|
||||
show: () => {
|
||||
waitForEl('mat-menu-panel', () => shepherdService.next());
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'These are the basics',
|
||||
text: 'Best',
|
||||
|
||||
buttons: [],
|
||||
},
|
||||
];
|
||||
0
src/app/features/shepherd/shepherd.component.html
Normal file
0
src/app/features/shepherd/shepherd.component.html
Normal file
0
src/app/features/shepherd/shepherd.component.scss
Normal file
0
src/app/features/shepherd/shepherd.component.scss
Normal file
23
src/app/features/shepherd/shepherd.component.spec.ts
Normal file
23
src/app/features/shepherd/shepherd.component.spec.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
//
|
||||
// import { ShepherdComponent } from './shepherd.component';
|
||||
//
|
||||
// describe('ShepherdComponent', () => {
|
||||
// let component: ShepherdComponent;
|
||||
// let fixture: ComponentFixture<ShepherdComponent>;
|
||||
//
|
||||
// beforeEach(async () => {
|
||||
// await TestBed.configureTestingModule({
|
||||
// declarations: [ ShepherdComponent ]
|
||||
// })
|
||||
// .compileComponents();
|
||||
//
|
||||
// fixture = TestBed.createComponent(ShepherdComponent);
|
||||
// component = fixture.componentInstance;
|
||||
// fixture.detectChanges();
|
||||
// });
|
||||
//
|
||||
// it('should create', () => {
|
||||
// expect(component).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
38
src/app/features/shepherd/shepherd.component.ts
Normal file
38
src/app/features/shepherd/shepherd.component.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { AfterViewInit, ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { ShepherdService } from 'angular-shepherd';
|
||||
import { SHEPHERD_STANDARD_BTNS, SHEPHERD_STEPS } from './shepherd-steps.const';
|
||||
import { LayoutService } from '../../core-ui/layout/layout.service';
|
||||
import { TaskService } from '../tasks/task.service';
|
||||
|
||||
@Component({
|
||||
selector: 'shepherd',
|
||||
template: '',
|
||||
// templateUrl: './shepherd.component.html',
|
||||
// styleUrls: ['./shepherd.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ShepherdComponent implements AfterViewInit {
|
||||
constructor(
|
||||
private shepherdService: ShepherdService,
|
||||
private layoutService: LayoutService,
|
||||
private taskService: TaskService,
|
||||
) {}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.shepherdService.defaultStepOptions = {
|
||||
scrollTo: false,
|
||||
highlightClass: 'shepherd-highlight',
|
||||
arrow: true,
|
||||
cancelIcon: {
|
||||
enabled: true,
|
||||
},
|
||||
buttons: [],
|
||||
};
|
||||
// this.shepherdService.modal = true;
|
||||
// this.shepherdService.confirmCancel = false;
|
||||
this.shepherdService.addSteps(
|
||||
SHEPHERD_STEPS(this.shepherdService, this.layoutService, this.taskService) as any,
|
||||
);
|
||||
this.shepherdService.start();
|
||||
}
|
||||
}
|
||||
|
|
@ -245,7 +245,8 @@ export class AddTaskBarComponent implements AfterViewInit, OnDestroy {
|
|||
const { className } = relatedTarget;
|
||||
isUIelement =
|
||||
className.includes('switch-add-to-btn') ||
|
||||
className.includes('switch-add-to-bot-btn');
|
||||
className.includes('switch-add-to-bot-btn') ||
|
||||
className.includes('shepherd-enabled');
|
||||
}
|
||||
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -13,3 +13,4 @@
|
|||
@import 'promise-btn';
|
||||
@import 'global-error-alert';
|
||||
@import 'wrap-buttons';
|
||||
@import 'shepherd';
|
||||
|
|
|
|||
35
src/styles/components/shepherd.scss
Normal file
35
src/styles/components/shepherd.scss
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
.shepherd-modal-overlay-container {
|
||||
width: 100%;
|
||||
height: 100% !important;
|
||||
//opacity: .2 !important;
|
||||
}
|
||||
|
||||
.shepherd-highlight {
|
||||
outline: 3px solid $c-accent !important;
|
||||
}
|
||||
|
||||
.shepherd-text kbd {
|
||||
background-color: #eee;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #b4b4b4;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 0 2px 0 0 rgba(255, 255, 255, 0.7) inset;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
font-size: 0.85em;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
padding: 2px 4px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.shepherd-header {
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.shepherd-element[data-popper-reference-hidden]:not(.shepherd-centered) {
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue