add keybaord shortcuts on array for choices

This commit is contained in:
tykayn 2020-11-06 14:33:20 +01:00
parent d3042b3723
commit 4769ecefc3
5 changed files with 187 additions and 10 deletions

View File

@ -65,6 +65,34 @@
<fieldset class="date-kind"> <fieldset class="date-kind">
<!-- choix spécialement pour les dates--> <!-- choix spécialement pour les dates-->
</fieldset> </fieldset>
<button
(click)="showDateInterval = !showDateInterval"
[ngClass]="{ active: showDateInterval }"
class="btn btn--primary"
id="toggle_interval_button"
>
<i class="fa fa-clock-o" aria-hidden="true"></i>
{{ 'dates.add_interval' | translate }}
</button>
<section *ngIf="showDateInterval" class="date-interval form-row">
<h2>{{ 'dates.add_interval' | translate }}</h2>
<p>
{{ 'dates.interval_propose' | translate }}
<input (change)="countDays()" formControlName="startDateInterval" type="date" />
{{ 'dates.interval_span' | translate }}
<input (change)="countDays()" formControlName="endDateInterval" type="date" />
<br />
</p>
<button (click)="addIntervalOfDates()" class="btn btn-block btn--primary">
<i class="fa fa-plus" aria-hidden="true"></i>
{{ 'dates.interval_button' | translate }}
{{ intervalDays }}
{{ 'dates.interval_button_dates' | translate }}
</button>
<hr />
</section>
<div class="form-field"> <div class="form-field">
<h2> <h2>
{{ 'choices.title' | translate }} {{ 'choices.title' | translate }}
@ -94,6 +122,9 @@
</span> </span>
</span> </span>
<p class="hint">
{{ 'creation.choices_hint' | translate }}
</p>
<span *ngFor="let choice of choices.controls; let i = index"> <span *ngFor="let choice of choices.controls; let i = index">
<div class="form-row" [formGroup]="choice"> <div class="form-row" [formGroup]="choice">
<div class="columns"> <div class="columns">
@ -104,11 +135,23 @@
{{ i * 1 + 1 }}) {{ i * 1 + 1 }})
</div> </div>
<div class="column"> <div class="column">
<label for="label" class="hidden">label</label> <label [for]="'choice_label_' + i" class="hidden">label</label>
<input formControlName="label" id="label" placeholder="Enter a choice description" /> <input
formControlName="label"
[id]="'choice_label_' + i"
placeholder="Enter a choice description"
(keyup)="keyOnChoice($event, i)"
(keyup.backspace)="deleteChoiceField(i)"
/>
<br /> <br />
<label for="imageUrl" class="hidden">image Url</label> <label [for]="'image_url_' + i" class="hidden">image Url</label>
<input formControlName="imageUrl" id="imageUrl" placeholder="URL de l' image" /> <input
formControlName="imageUrl"
[id]="'image_url_' + i"
placeholder="URL de l' image"
(keyup)="keyOnChoice($event, i)"
(keyup.backspace)="deleteChoiceField(i)"
/>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,10 +1,12 @@
import { Component, Input, OnInit } from '@angular/core'; import { ChangeDetectorRef, Component, Inject, Input, OnInit } from '@angular/core';
import { Poll } from '../../../core/models/poll.model'; import { Poll } from '../../../core/models/poll.model';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { UuidService } from '../../../core/services/uuid.service'; import { UuidService } from '../../../core/services/uuid.service';
import { ApiService } from '../../../core/services/api.service'; import { ApiService } from '../../../core/services/api.service';
import { ToastService } from '../../../core/services/toast.service'; import { ToastService } from '../../../core/services/toast.service';
import { PollService } from '../../../core/services/poll.service'; import { PollService } from '../../../core/services/poll.service';
import { DateUtilities } from '../../old-stuff/config/DateUtilities';
import { DOCUMENT } from '@angular/common';
@Component({ @Component({
selector: 'app-admin-form', selector: 'app-admin-form',
@ -18,13 +20,22 @@ export class FormComponent implements OnInit {
public urlPrefix: string = window.location.origin + '/participation/'; public urlPrefix: string = window.location.origin + '/participation/';
public advancedDisplayEnabled = false; public advancedDisplayEnabled = false;
public showDateInterval = true;
startDateInterval: any;
intervalDays: any;
intervalDaysDefault = 7;
endDateInterval: any;
dateList: any[];
constructor( constructor(
private fb: FormBuilder, private fb: FormBuilder,
private cd: ChangeDetectorRef,
private uuidService: UuidService, private uuidService: UuidService,
private toastService: ToastService, private toastService: ToastService,
private pollService: PollService, private pollService: PollService,
private apiService: ApiService public dateUtilities: DateUtilities,
private apiService: ApiService,
@Inject(DOCUMENT) private document: any
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
@ -61,6 +72,13 @@ export class FormComponent implements OnInit {
}); });
} }
this.choices.push(newControlGroup); this.choices.push(newControlGroup);
this.cd.detectChanges();
console.log('this.choices.length', this.choices.length);
const selector = '#choice_label_' + (this.choices.length - 1);
const elem = this.document.querySelector(selector);
if (elem) {
elem.focus();
}
} }
deleteChoiceField(index: number): void { deleteChoiceField(index: number): void {
@ -73,7 +91,7 @@ export class FormComponent implements OnInit {
this.choices.setValue([]); this.choices.setValue([]);
} }
initFormDefault(): void { initFormDefault(showDemoValues = true): void {
this.form = this.fb.group({ this.form = this.fb.group({
title: ['', [Validators.required, Validators.minLength(12)]], title: ['', [Validators.required, Validators.minLength(12)]],
creatorPseudo: ['', [Validators.required]], creatorPseudo: ['', [Validators.required]],
@ -82,6 +100,8 @@ export class FormComponent implements OnInit {
description: ['', [Validators.required]], description: ['', [Validators.required]],
choices: new FormArray([]), choices: new FormArray([]),
isAboutDate: [true, [Validators.required]], isAboutDate: [true, [Validators.required]],
startDateInterval: ['', [Validators.required]],
endDateInterval: ['', [Validators.required]],
isProtectedByPassword: [false, [Validators.required]], isProtectedByPassword: [false, [Validators.required]],
isOwnerNotifiedByEmailOnNewVote: [false, [Validators.required]], isOwnerNotifiedByEmailOnNewVote: [false, [Validators.required]],
isOwnerNotifiedByEmailOnNewComment: [false, [Validators.required]], isOwnerNotifiedByEmailOnNewComment: [false, [Validators.required]],
@ -90,9 +110,33 @@ export class FormComponent implements OnInit {
expiracyNumberOfDays: [60, [Validators.required, Validators.min(0)]], expiracyNumberOfDays: [60, [Validators.required, Validators.min(0)]],
}); });
console.log('this.form ', this.form); console.log('this.form ', this.form);
this.setDemoValues(); this.setDefaultDatesForInterval();
if (showDemoValues) {
this.setDemoValues();
}
} }
/**
* default interval of dates proposed is from today to 7 days more
*/
setDefaultDatesForInterval(): void {
const dateCurrent = new Date();
const dateJson = dateCurrent.toISOString();
this.startDateInterval = dateJson.substring(0, 10);
this.endDateInterval = this.dateUtilities
.addDaysToDate(this.intervalDaysDefault, dateCurrent)
.toISOString()
.substring(0, 10);
this.form.patchValue({
startDateInterval: this.startDateInterval,
endDateInterval: this.endDateInterval,
});
}
/**
* add example values to the form
*/
setDemoValues(): void { setDemoValues(): void {
this.addChoice('orange'); this.addChoice('orange');
this.addChoice('raisin'); this.addChoice('raisin');
@ -116,7 +160,88 @@ export class FormComponent implements OnInit {
} }
askInitFormDefault(): void { askInitFormDefault(): void {
this.initFormDefault(); this.initFormDefault(false);
this.toastService.display('formulaire réinitialisé'); this.toastService.display('formulaire réinitialisé');
} }
countDays(): void {
this.intervalDays = this.dateUtilities.countDays(this.startDateInterval, this.endDateInterval);
}
/**
* add all the dates between the start and end dates in the interval section
*/
addIntervalOfDates(): void {
const newIntervalArray = this.dateUtilities.getDatesInRange(this.startDateInterval, this.endDateInterval, 1);
const converted = [];
newIntervalArray.forEach((element) => {
converted.push({
literal: element.literal,
date_object: element.date_object,
timeList: [],
});
});
this.dateList = [...new Set(converted)];
// add only dates that are not already present with a Set of unique items
console.log('this.dateList', this.dateList);
this.showDateInterval = false;
this.toastService.display(`les dates ont été ajoutées aux réponses possibles.`);
}
/**
* handle keyboard shortcuts
* @param $event
*/
keyOnChoice($event: KeyboardEvent, choice_number: number): void {
$event.preventDefault();
console.log('this.choices.length', this.choices.length);
console.log('choice_number', choice_number);
const lastChoice = this.choices.length - 1 === choice_number;
// reset field with Ctrl + D
// add a field with Ctrl + N
// go to previous choice with arrow up
// go to next choice with arrow down
console.log('$event', $event);
if ($event.key == 'ArrowUp' && choice_number > 0) {
const selector = '#choice_label_' + (choice_number - 1);
const elem = this.document.querySelector(selector);
if (elem) {
elem.focus();
}
}
if ($event.key == 'ArrowDown') {
// add a field if we are on the last choice
if (lastChoice) {
this.addChoice();
this.toastService.display('choix ajouté par raccourci "flèche bas"');
} else {
const selector = '#choice_label_' + (choice_number + 1);
const elem = this.document.querySelector(selector);
if (elem) {
elem.focus();
}
}
}
if ($event.ctrlKey && $event.key == 'Backspace') {
this.deleteChoiceField(choice_number);
this.toastService.display('choix supprimé par raccourci "Ctrl + retour"');
this.cd.detectChanges();
const selector = '#choice_label_' + Math.min(choice_number - 1, 0);
const elem = this.document.querySelector(selector);
if (elem) {
elem.focus();
}
}
if ($event.ctrlKey && $event.key == 'Enter') {
// go to other fields
const elem = this.document.querySelector('#creatorEmail');
if (elem) {
elem.focus();
}
}
}
} }

View File

@ -60,4 +60,12 @@ export class DateUtilities {
getDoubleDigits(str) { getDoubleDigits(str) {
return ('00' + str).slice(-2); return ('00' + str).slice(-2);
} }
countDays(startDateInterval: Date, endDateInterval: Date): number {
// compute the number of days in the date interval
if (endDateInterval && startDateInterval) {
return this.dayDiff(endDateInterval, startDateInterval);
}
return 0;
}
} }

View File

@ -55,7 +55,7 @@ export class DatesComponent extends BaseComponent implements OnInit {
date_object: new Date(), date_object: new Date(),
timeList: [], timeList: [],
}); });
const selector = '[ng-reflect-choice_label="dateChoices_' + (this.config.dateList.length - 1) + '"]'; const selector = '["choice_label_' + (this.config.dateList.length - 1) + '"]';
this.cd.detectChanges(); this.cd.detectChanges();
const elem = this.document.querySelector(selector); const elem = this.document.querySelector(selector);
if (elem) { if (elem) {

View File

@ -35,6 +35,7 @@
}, },
"choose_title": "Dont le titre sera", "choose_title": "Dont le titre sera",
"choose_title_placeholder": "titre", "choose_title_placeholder": "titre",
"choices_hint": "Utilisez les flèches haut ⬆️ et bas ⬇️ pour passer d'un choix à un autre",
"name": "Je peux aussi préciser mon nom si je le souhaite", "name": "Je peux aussi préciser mon nom si je le souhaite",
"name_placeholder": "mon nom", "name_placeholder": "mon nom",
"description": "et la description serait", "description": "et la description serait",