MOBILE-3565 searchbox: Searchbox component implementation
parent
9daf5a3db7
commit
f3ae7e5e4a
|
@ -54,6 +54,7 @@ import { CoreUtilsProvider } from '@services/utils/utils';
|
||||||
import { initCoreFilepoolDB } from '@services/filepool.db';
|
import { initCoreFilepoolDB } from '@services/filepool.db';
|
||||||
import { initCoreSitesDB } from '@services/sites.db';
|
import { initCoreSitesDB } from '@services/sites.db';
|
||||||
import { initCoreSyncDB } from '@services/sync.db';
|
import { initCoreSyncDB } from '@services/sync.db';
|
||||||
|
import { initCoreSearchHistoryDB } from '@core/search/services/search.history.db';
|
||||||
|
|
||||||
// Import core modules.
|
// Import core modules.
|
||||||
import { CoreEmulatorModule } from '@core/emulator/emulator.module';
|
import { CoreEmulatorModule } from '@core/emulator/emulator.module';
|
||||||
|
@ -170,6 +171,7 @@ export class AppModule {
|
||||||
initCoreFilepoolDB();
|
initCoreFilepoolDB();
|
||||||
initCoreSitesDB();
|
initCoreSitesDB();
|
||||||
initCoreSyncDB();
|
initCoreSyncDB();
|
||||||
|
initCoreSearchHistoryDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.core-loading-loaded {
|
&.core-loading-loaded {
|
||||||
position: relative;
|
position: unset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { IonicModule } from '@ionic/angular';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
|
||||||
|
import { CoreSearchBoxComponent } from './search-box/search-box';
|
||||||
|
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CoreSearchBoxComponent,
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
FormsModule,
|
||||||
|
TranslateModule.forChild(),
|
||||||
|
CoreDirectivesModule,
|
||||||
|
CoreComponentsModule,
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CoreSearchBoxComponent,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CoreSearchComponentsModule {}
|
|
@ -0,0 +1,28 @@
|
||||||
|
<ion-card>
|
||||||
|
<form (ngSubmit)="submitForm($event)" role="search" #searchForm>
|
||||||
|
<ion-item>
|
||||||
|
<ion-label>
|
||||||
|
<ion-input type="search" name="search" [(ngModel)]="searchText" [placeholder]="placeholder"
|
||||||
|
[autocorrect]="autocorrect" [spellcheck]="spellcheck" [core-auto-focus]="autoFocus"
|
||||||
|
[disabled]="disabled" role="searchbox" (ionFocus)="focus($event)"></ion-input>
|
||||||
|
</ion-label>
|
||||||
|
<ion-button slot="end" fill="clear" type="submit" size="small" [attr.aria-label]="searchLabel"
|
||||||
|
[disabled]="disabled || !searchText || (searchText.length < lengthCheck)">
|
||||||
|
<ion-icon name="fas-search" slot="icon-only"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
<ion-button *ngIf="showClear" slot="end" fill="clear" size="small"
|
||||||
|
[attr.aria-label]="'core.clearsearch' | translate" [disabled]="searched == '' || disabled"
|
||||||
|
(click)="clearForm()">
|
||||||
|
<ion-icon name="fas-backspace" slot="icon-only"></ion-icon>
|
||||||
|
</ion-button>
|
||||||
|
</ion-item>
|
||||||
|
<ion-list class="core-search-history" [hidden]="!historyShown">
|
||||||
|
<ion-item class="ion-text-wrap" *ngFor="let item of history"
|
||||||
|
(click)="historyClicked($event, item.searchedtext)" class="core-clickable" tabindex="1">
|
||||||
|
<ion-icon name="fa-history" slot="start">
|
||||||
|
</ion-icon>
|
||||||
|
{{item.searchedtext}}
|
||||||
|
</ion-item>
|
||||||
|
</ion-list>
|
||||||
|
</form>
|
||||||
|
</ion-card>
|
|
@ -0,0 +1,27 @@
|
||||||
|
:host {
|
||||||
|
height: 80px;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
ion-card {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
ion-button.button:last-child {
|
||||||
|
margin-left: unset;
|
||||||
|
margin-inline-start: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-search-history {
|
||||||
|
max-height: calc(-120px + 80vh);
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.item:hover {
|
||||||
|
--background: var(--gray-lighter);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,173 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
import { CoreSearchHistory, CoreSearchHistoryItem } from '../../services/search-history';
|
||||||
|
import { Translate } from '@singletons/core.singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to display a "search box".
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* This component will display a standalone search box with its search button in order to have a better UX.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* <core-search-box (onSubmit)="search($event)" [placeholder]="'core.courses.search' | translate"
|
||||||
|
* [searchLabel]="'core.courses.search' | translate" autoFocus="true"></core-search-box>
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-search-box',
|
||||||
|
templateUrl: 'core-search-box.html',
|
||||||
|
styleUrls: ['search-box.scss'],
|
||||||
|
})
|
||||||
|
export class CoreSearchBoxComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() searchLabel?: string; // Label to be used on action button.
|
||||||
|
@Input() placeholder?: string; // Placeholder text for search text input.
|
||||||
|
@Input() autocorrect = 'on'; // Enables/disable Autocorrection on search text input.
|
||||||
|
@Input() spellcheck?: string | boolean = true; // Enables/disable Spellchecker on search text input.
|
||||||
|
@Input() autoFocus?: string | boolean; // Enables/disable Autofocus when entering view.
|
||||||
|
@Input() lengthCheck = 3; // Check value length before submit. If 0, any string will be submitted.
|
||||||
|
@Input() showClear = true; // Show/hide clear button.
|
||||||
|
@Input() disabled = false; // Disables the input text.
|
||||||
|
@Input() protected initialSearch = ''; // Initial search text.
|
||||||
|
|
||||||
|
/* If provided. It will save and display a history of searches for this particular Id.
|
||||||
|
* To use different history lists, place different Id.
|
||||||
|
* I.e. AddonMessagesContacts or CoreUserParticipants-6 (using the course Id).*/
|
||||||
|
@Input() protected searchArea = '';
|
||||||
|
|
||||||
|
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form.
|
||||||
|
@Output() onClear: EventEmitter<void>; // Send event when clearing the search form.
|
||||||
|
|
||||||
|
formElement?: HTMLFormElement;
|
||||||
|
|
||||||
|
searched = ''; // Last search emitted.
|
||||||
|
searchText = '';
|
||||||
|
history: CoreSearchHistoryItem[] = [];
|
||||||
|
historyShown = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.onSubmit = new EventEmitter<string>();
|
||||||
|
this.onClear = new EventEmitter<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.searchLabel = this.searchLabel || Translate.instance.instant('core.search');
|
||||||
|
this.placeholder = this.placeholder || Translate.instance.instant('core.search');
|
||||||
|
this.spellcheck = CoreUtils.instance.isTrueOrOne(this.spellcheck);
|
||||||
|
this.showClear = CoreUtils.instance.isTrueOrOne(this.showClear);
|
||||||
|
this.searchText = this.initialSearch;
|
||||||
|
|
||||||
|
if (this.searchArea) {
|
||||||
|
this.loadHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form submitted.
|
||||||
|
*
|
||||||
|
* @param e Event.
|
||||||
|
*/
|
||||||
|
submitForm(e?: Event): void {
|
||||||
|
e && e.preventDefault();
|
||||||
|
e && e.stopPropagation();
|
||||||
|
|
||||||
|
if (this.searchText.length < this.lengthCheck) {
|
||||||
|
// The view should handle this case, but we check it here too just in case.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.searchArea) {
|
||||||
|
this.saveSearchToHistory(this.searchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreDomUtils.instance.triggerFormSubmittedEvent(this.formElement, false, CoreSites.instance.getCurrentSiteId());
|
||||||
|
|
||||||
|
this.historyShown = false;
|
||||||
|
this.searched = this.searchText;
|
||||||
|
this.onSubmit.emit(this.searchText);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the search term onto the history.
|
||||||
|
*
|
||||||
|
* @param text Text to save.
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async saveSearchToHistory(text: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
await CoreSearchHistory.instance.insertOrUpdateSearchText(this.searchArea, text.toLowerCase());
|
||||||
|
} finally {
|
||||||
|
this.loadHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads search history.
|
||||||
|
*
|
||||||
|
* @return Promise resolved when done.
|
||||||
|
*/
|
||||||
|
protected async loadHistory(): Promise<void> {
|
||||||
|
this.history = await CoreSearchHistory.instance.getSearchHistory(this.searchArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select an item and use it for search text.
|
||||||
|
*
|
||||||
|
* @param e Event.
|
||||||
|
* @param text Selected text.
|
||||||
|
*/
|
||||||
|
historyClicked(e: Event, text: string): void {
|
||||||
|
if (this.searched != text) {
|
||||||
|
this.searchText = text;
|
||||||
|
this.submitForm(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form submitted.
|
||||||
|
*/
|
||||||
|
clearForm(): void {
|
||||||
|
this.searched = '';
|
||||||
|
this.searchText = '';
|
||||||
|
this.onClear.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param event Focus event on input element.
|
||||||
|
*/
|
||||||
|
focus(event: CustomEvent): void {
|
||||||
|
this.historyShown = true;
|
||||||
|
|
||||||
|
if (!this.formElement) {
|
||||||
|
this.formElement = event.detail.target.closest('form');
|
||||||
|
|
||||||
|
|
||||||
|
this.formElement?.addEventListener('blur', () => {
|
||||||
|
// Wait the new element to be focused.
|
||||||
|
setTimeout(() => {
|
||||||
|
if (document.activeElement?.closest('form') != this.formElement) {
|
||||||
|
this.historyShown = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CoreSearchComponentsModule } from './components/components.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreSearchComponentsModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CoreSearchComponentsModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CoreSearchModule {}
|
|
@ -0,0 +1,133 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
|
import { CoreSites } from '@services/sites';
|
||||||
|
import { SQLiteDB } from '@classes/sqlitedb';
|
||||||
|
import { CoreSearchHistoryDBRecord, SEARCH_HISTORY_TABLE_NAME } from './search.history.db';
|
||||||
|
import { makeSingleton } from '@/app/singletons/core.singletons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service that enables adding a history to a search box.
|
||||||
|
*/
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class CoreSearchHistoryProvider {
|
||||||
|
|
||||||
|
protected static readonly HISTORY_LIMIT = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a search area history sorted by use.
|
||||||
|
*
|
||||||
|
* @param searchArea Search Area Name.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with the list of items when done.
|
||||||
|
*/
|
||||||
|
async getSearchHistory(searchArea: string, siteId?: string): Promise<CoreSearchHistoryDBRecord[]> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
const conditions = {
|
||||||
|
searcharea: searchArea,
|
||||||
|
};
|
||||||
|
|
||||||
|
const history: CoreSearchHistoryDBRecord[] = await site.getDb().getRecords(SEARCH_HISTORY_TABLE_NAME, conditions);
|
||||||
|
|
||||||
|
// Sorting by last used DESC.
|
||||||
|
return history.sort((a, b) => (b.lastused || 0) - (a.lastused || 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls search limit and removes the last item if overflows.
|
||||||
|
*
|
||||||
|
* @param searchArea Search area to control
|
||||||
|
* @param db SQLite DB where to perform the search.
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
protected async controlSearchLimit(searchArea: string, db: SQLiteDB): Promise<void> {
|
||||||
|
const items = await this.getSearchHistory(searchArea);
|
||||||
|
if (items.length > CoreSearchHistoryProvider.HISTORY_LIMIT) {
|
||||||
|
// Over the limit. Remove the last.
|
||||||
|
const lastItem = items.pop();
|
||||||
|
|
||||||
|
const searchItem = {
|
||||||
|
searcharea: lastItem!.searcharea,
|
||||||
|
searchedtext: lastItem!.searchedtext,
|
||||||
|
};
|
||||||
|
|
||||||
|
await db.deleteRecords(SEARCH_HISTORY_TABLE_NAME, searchItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the search history item if exists.
|
||||||
|
*
|
||||||
|
* @param searchArea Area where the search has been performed.
|
||||||
|
* @param text Text of the performed text.
|
||||||
|
* @param db SQLite DB where to perform the search.
|
||||||
|
* @return True if exists, false otherwise.
|
||||||
|
*/
|
||||||
|
protected async updateExistingItem(searchArea: string, text: string, db: SQLiteDB): Promise<boolean> {
|
||||||
|
const searchItem = {
|
||||||
|
searcharea: searchArea,
|
||||||
|
searchedtext: text,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const existingItem: CoreSearchHistoryDBRecord = await db.getRecord(SEARCH_HISTORY_TABLE_NAME, searchItem);
|
||||||
|
|
||||||
|
// If item exist, update time and number of times searched.
|
||||||
|
existingItem.lastused = Date.now();
|
||||||
|
existingItem.times++;
|
||||||
|
|
||||||
|
await db.updateRecords(SEARCH_HISTORY_TABLE_NAME, existingItem, searchItem);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a searched term on the history.
|
||||||
|
*
|
||||||
|
* @param searchArea Area where the search has been performed.
|
||||||
|
* @param text Text of the performed text.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Resolved when done.
|
||||||
|
*/
|
||||||
|
async insertOrUpdateSearchText(searchArea: string, text: string, siteId?: string): Promise<void> {
|
||||||
|
const site = await CoreSites.instance.getSite(siteId);
|
||||||
|
const db = site.getDb();
|
||||||
|
|
||||||
|
const exists = await this.updateExistingItem(searchArea, text, db);
|
||||||
|
|
||||||
|
if (!exists) {
|
||||||
|
// If item is new, control the history does not goes over the limit.
|
||||||
|
const searchItem: CoreSearchHistoryDBRecord = {
|
||||||
|
searcharea: searchArea,
|
||||||
|
searchedtext: text,
|
||||||
|
lastused: Date.now(),
|
||||||
|
times: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
await db.insertRecord(SEARCH_HISTORY_TABLE_NAME, searchItem);
|
||||||
|
|
||||||
|
await this.controlSearchLimit(searchArea, db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CoreSearchHistory extends makeSingleton(CoreSearchHistoryProvider) {}
|
|
@ -0,0 +1,67 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { CoreSiteSchema, registerSiteSchema } from '@services/sites';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database variables for CoreSearchHistory service.
|
||||||
|
*/
|
||||||
|
export const SEARCH_HISTORY_TABLE_NAME = 'seach_history';
|
||||||
|
const SITE_SCHEMA: CoreSiteSchema = {
|
||||||
|
name: 'CoreSearchHistoryProvider',
|
||||||
|
version: 1,
|
||||||
|
tables: [
|
||||||
|
{
|
||||||
|
name: SEARCH_HISTORY_TABLE_NAME,
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
name: 'searcharea',
|
||||||
|
type: 'TEXT',
|
||||||
|
notNull: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lastused',
|
||||||
|
type: 'INTEGER',
|
||||||
|
notNull: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'times',
|
||||||
|
type: 'INTEGER',
|
||||||
|
notNull: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'searchedtext',
|
||||||
|
type: 'TEXT',
|
||||||
|
notNull: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
primaryKeys: ['searcharea', 'searchedtext'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search history item definition.
|
||||||
|
*/
|
||||||
|
export type CoreSearchHistoryDBRecord = {
|
||||||
|
searcharea: string; // Search area where the search has been performed.
|
||||||
|
lastused: number; // Timestamp of the last search.
|
||||||
|
searchedtext: string; // Text of the performed search.
|
||||||
|
times: number; // Times search has been performed (if previously in history).
|
||||||
|
};
|
||||||
|
|
||||||
|
export const initCoreSearchHistoryDB = (): void => {
|
||||||
|
registerSiteSchema(SITE_SCHEMA);
|
||||||
|
};
|
||||||
|
|
|
@ -20,6 +20,5 @@ import { CoreSettingsRoutingModule } from './settings-routing.module';
|
||||||
imports: [
|
imports: [
|
||||||
CoreSettingsRoutingModule,
|
CoreSettingsRoutingModule,
|
||||||
],
|
],
|
||||||
declarations: [],
|
|
||||||
})
|
})
|
||||||
export class CoreSettingsModule {}
|
export class CoreSettingsModule {}
|
||||||
|
|
|
@ -1725,14 +1725,14 @@ export class CoreDomUtilsProvider {
|
||||||
* @param online Whether the action was done in offline or not.
|
* @param online Whether the action was done in offline or not.
|
||||||
* @param siteId The site affected. If not provided, no site affected.
|
* @param siteId The site affected. If not provided, no site affected.
|
||||||
*/
|
*/
|
||||||
triggerFormSubmittedEvent(formRef: ElementRef | undefined, online?: boolean, siteId?: string): void {
|
triggerFormSubmittedEvent(formRef: ElementRef | HTMLFormElement | undefined, online?: boolean, siteId?: string): void {
|
||||||
if (!formRef) {
|
if (!formRef) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreEvents.trigger(CoreEvents.FORM_ACTION, {
|
CoreEvents.trigger(CoreEvents.FORM_ACTION, {
|
||||||
action: 'submit',
|
action: 'submit',
|
||||||
form: formRef.nativeElement,
|
form: formRef.nativeElement || formRef,
|
||||||
online: !!online,
|
online: !!online,
|
||||||
}, siteId);
|
}, siteId);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue