From 387b27be9a7eaa0ad8607106640860a2e4916821 Mon Sep 17 00:00:00 2001 From: Adrien Crivelli Date: Thu, 29 Feb 2024 15:38:07 +0800 Subject: [PATCH] Update eslint rules #10134 It's been a while since we updated our rules. This time we opt in into all rules and explicitly disable the ones that don't fit our code. This way we will automatically receive new rules in the future, and we can decide whether we accept or reject them, instead of needing to re-check which one is new at every update. --- .eslintrc.json | 68 +++++++++++++--- e2e/src/app.spec.ts | 4 +- projects/natural-editor/.eslintrc.json | 51 ++++++++++++ .../class-dialog/class-dialog.component.ts | 4 +- .../color-dialog/color-dialog.component.html | 2 +- .../color-dialog/color-dialog.component.ts | 4 +- .../custom-css/custom-css.directive.spec.ts | 2 +- .../src/lib/editor/editor.component.html | 52 ++++++------- .../src/lib/editor/editor.component.spec.ts | 2 +- .../lib/id-dialog/id-dialog.component.html | 2 +- .../src/lib/id-dialog/id-dialog.component.ts | 4 +- .../lib/link-dialog/link-dialog.component.ts | 4 +- .../natural-editor/src/lib/utils/image.ts | 6 +- .../src/lib/utils/items/table-item.ts | 6 +- .../natural-editor/src/lib/utils/keymap.ts | 8 +- .../src/lib/utils/schema/heading.ts | 2 +- .../utils/schema/paragraph-with-alignment.ts | 2 +- .../src/lib/utils/schema/schema.ts | 6 +- .../src/lib/utils/schema/table.ts | 2 +- projects/natural/.eslintrc.json | 62 +++++++++++++++ .../src/lib/classes/abstract-list.spec.ts | 3 +- .../natural/src/lib/classes/abstract-list.ts | 4 +- .../lib/classes/abstract-navigable-list.ts | 17 ++-- .../natural/src/lib/classes/apollo-utils.ts | 4 +- .../src/lib/classes/data-source.spec.ts | 4 +- .../natural/src/lib/classes/data-source.ts | 4 +- .../src/lib/classes/query-variable-manager.ts | 19 ++--- projects/natural/src/lib/classes/utility.ts | 4 +- .../natural/src/lib/classes/validators.ts | 2 +- .../lib/directives/http-prefix.directive.ts | 2 +- .../lib/modules/alert/confirm.component.ts | 4 +- .../avatar/component/avatar.component.spec.ts | 2 +- .../columns-picker.component.html | 16 ++-- .../columns-picker.component.ts | 10 +-- .../directives/linkable-tab.directive.spec.ts | 8 +- .../directives/linkable-tab.directive.ts | 2 +- .../common/services/seo.service.spec.ts | 3 +- .../modules/common/services/seo.service.ts | 14 ++-- .../detail-header.component.html | 4 +- .../dialog-trigger.component.ts | 5 +- ...-association-select-component.directive.ts | 2 +- .../type-boolean/type-boolean.component.ts | 4 +- .../type-date-range.component.html | 10 ++- .../type-date-range.component.ts | 4 +- .../type-date/type-date.component.html | 4 +- .../type-date/type-date.component.spec.ts | 11 +-- .../type-date/type-date.component.ts | 4 +- .../type-hierarchic-selector.component.html | 2 +- .../type-hierarchic-selector.component.ts | 13 ++-- .../type-natural-select.component.html | 2 +- .../type-natural-select.component.ts | 4 +- .../type-number/type-number.component.ts | 4 +- .../type-options/type-options.component.ts | 8 +- .../type-select/type-select.component.html | 2 +- .../type-select/type-select.component.ts | 6 +- .../type-text/type-text.component.ts | 2 +- .../lib/modules/dropdown-components/types.ts | 8 +- .../src/lib/modules/file/abstract-file.ts | 8 +- .../file/component/file.component.html | 6 +- .../modules/file/component/file.component.ts | 8 +- .../modules/file/file-drop.directive.spec.ts | 2 +- .../fixed-button-detail.component.html | 4 +- .../fixed-button/fixed-button.component.html | 2 +- .../hierarchic-selector/classes/flat-node.ts | 8 +- .../classes/hierarchic-configuration.ts | 9 +-- .../hierarchic-filters-configuration.ts | 6 +- .../hierarchic-selector-dialog.component.html | 2 +- .../hierarchic-selector-dialog.component.ts | 8 +- .../hierarchic-selector.component.html | 24 ++---- .../hierarchic-selector.component.ts | 26 +++---- .../hierarchic-selector.service.ts | 12 ++- .../lib/modules/icon/icon.directive.spec.ts | 2 +- .../src/lib/modules/icon/icon.directive.ts | 12 ++- .../src/lib/modules/logger/error-handler.ts | 8 +- .../lib/modules/matomo/matomo.service.spec.ts | 3 +- .../src/lib/modules/matomo/matomo.service.ts | 4 +- .../src/lib/modules/panels/abstract-panel.ts | 2 +- .../src/lib/modules/panels/panels.service.ts | 6 +- .../src/lib/modules/panels/panels.spec.ts | 14 ++-- .../lib/modules/panels/panels.urlmatcher.ts | 2 +- .../natural/src/lib/modules/panels/types.ts | 28 +++---- .../relations/relations.component.html | 10 +-- .../modules/relations/relations.component.ts | 26 +++---- .../search/classes/graphql-doctrine.ts | 2 +- .../search/classes/graphql-doctrine.types.ts | 78 +++++++++---------- .../src/lib/modules/search/classes/url.ts | 16 ++-- .../dropdown-container.component.html | 2 +- .../dropdown-container.component.ts | 5 +- .../dropdown-container/dropdown.service.ts | 4 +- .../facet-selector.component.ts | 4 +- .../modules/search/group/group.component.html | 4 +- .../modules/search/input/input.component.html | 6 +- .../modules/search/input/input.component.ts | 62 +++++++-------- .../search/search/search.component.html | 10 +-- .../modules/search/search/search.component.ts | 2 +- .../search/types/dropdown-component.ts | 4 +- .../src/lib/modules/search/types/facet.ts | 14 ++-- .../src/lib/modules/search/types/values.ts | 12 +-- .../select-enum/select-enum.component.spec.ts | 6 +- .../select-hierarchic.component.html | 6 +- .../select-hierarchic.component.spec.ts | 6 +- .../select-hierarchic.component.ts | 2 +- .../select/select/select.component.html | 12 +-- .../select/select/select.component.spec.ts | 6 +- .../modules/select/select/select.component.ts | 14 ++-- .../sidenav-container.component.html | 4 +- .../sidenav-content.component.ts | 2 +- .../sidenav/sidenav/sidenav.component.html | 2 +- .../sidenav/sidenav/sidenav.component.ts | 2 +- .../table-button/table-button.component.html | 26 +++---- .../table-button/table-button.component.ts | 1 + .../lib/services/abstract-model.service.ts | 22 ++---- .../natural/src/lib/services/enum.service.ts | 20 ++--- .../src/lib/services/link-mutation.service.ts | 28 +++---- .../src/lib/services/persistence.service.ts | 4 + .../natural/src/lib/testing/item.service.ts | 4 +- .../src/lib/testing/mock-apollo.provider.ts | 12 +-- projects/natural/src/lib/types/types.ts | 4 +- src/app/app.component.html | 2 +- src/app/avatar/avatar.component.html | 60 +++++++------- src/app/avatar/user.model.ts | 4 +- src/app/demo.error-handler.ts | 2 +- .../detail-header.component.html | 11 +-- src/app/detail/detail.component.html | 4 +- .../editable-list.component.html | 8 +- src/app/editor/editor.component.html | 4 +- src/app/file/file.component.html | 2 +- src/app/file/file.component.ts | 8 +- src/app/hierarchic/hierarchic.component.html | 2 +- src/app/home/home.component.html | 46 +++++------ src/app/homepage/homepage.component.spec.ts | 2 +- src/app/list/list.component.html | 14 ++-- src/app/list/list.component.spec.ts | 4 +- src/app/list/list.component.ts | 2 +- .../navigable-list.component.html | 15 ++-- .../navigable-list.component.ts | 2 +- src/app/other/other.component.html | 38 ++++----- src/app/panels/panels.component.html | 2 +- src/app/relations/relations.component.html | 8 +- .../select-enum/select-enum.component.html | 28 +++---- .../select-hierarchic.component.html | 30 +++---- src/app/select/select.component.html | 50 ++++-------- src/app/shared/services/theme.service.ts | 6 +- 143 files changed, 811 insertions(+), 707 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index ee0416f0..1661c75f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,16 +4,37 @@ { "files": ["*.ts"], "parserOptions": { - "project": ["tsconfig.json"], + "project": true, "createDefaultProgram": true }, "extends": [ "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@angular-eslint/recommended", + "plugin:@typescript-eslint/strict-type-checked", + "plugin:@typescript-eslint/stylistic-type-checked", + "plugin:@angular-eslint/all", "plugin:@angular-eslint/template/process-inline-templates" ], "rules": { + "@angular-eslint/component-max-inline-declarations": "off", // We use that mostly for testing, so it's fine + "@angular-eslint/no-forward-ref": "off", // We sometimes need it + "@angular-eslint/prefer-on-push-component-change-detection": "off", + "@angular-eslint/use-component-selector": "off", // Some components are not template-able and thus do not need selector + "@typescript-eslint/consistent-type-definitions": ["error", "type"], + "@typescript-eslint/explicit-member-accessibility": "error", + "@typescript-eslint/no-confusing-void-expression": "off", // Don't create unncessary closure and we prefer code tersity anyway + "@typescript-eslint/no-dynamic-delete": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-extraneous-class": "off", // We have component without any logic in TS + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unnecessary-condition": "off", // This is very unfortunate, but there are too many dangerous false-positive, see https://github.com/typescript-eslint/typescript-eslint/issues/1798 + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/prefer-nullish-coalescing": "off", // Usually a good idea, but sometimes dangerous false-positive + "@typescript-eslint/unbound-method": "off", "@angular-eslint/directive-selector": [ "error", { @@ -30,23 +51,28 @@ "style": "kebab-case" } ], - "@typescript-eslint/ban-types": "error", "@typescript-eslint/explicit-function-return-type": [ "error", { "allowExpressions": true } ], - "@typescript-eslint/explicit-member-accessibility": "error", "@typescript-eslint/explicit-module-boundary-types": [ "error", { "allowArgumentsExplicitlyTypedAsAny": true } ], - "@typescript-eslint/prefer-for-of": "error", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-misused-promises": [ + "error", + { + "checksVoidReturn": { + // We want to use promise in Rxjs subscribes without caring about the promise result + "arguments": false, + "properties": false + } + } + ], "no-restricted-globals": [ "error", "atob", @@ -61,14 +87,32 @@ "navigator", "sessionStorage", "window" - ], - "prefer-const": ["error"] + ] } }, { "files": ["*.html"], - "extends": ["plugin:@angular-eslint/template/recommended"], - "rules": {} + "extends": ["plugin:@angular-eslint/template/all"], + "rules": { + "@angular-eslint/template/alt-text": "off", // We don't care as much as we should about a11y + "@angular-eslint/template/attributes-order": "off", // TODO: We want to enable this, but autofix mess up our code, and it's too much manual changes + "@angular-eslint/template/button-has-type": "off", + "@angular-eslint/template/click-events-have-key-events": "off", // We don't care as much as we should about a11y + "@angular-eslint/template/i18n": "off", + "@angular-eslint/template/interactive-supports-focus": "off", // We don't care as much as we should about a11y + "@angular-eslint/template/label-has-associated-control": "off", // We don't care as much as we should about a11y + "@angular-eslint/template/no-any": "off", // Unfortunately, some libs force us to use this + "@angular-eslint/template/no-autofocus": "off", + "@angular-eslint/template/no-call-expression": "off", + "@angular-eslint/template/no-inline-styles": "off", // We sometimes use short inlie styles + "@angular-eslint/template/prefer-ngsrc": "off", // TODO: experiment with ngsrc and see if we need to use it or not + "@angular-eslint/template/eqeqeq": [ + "error", + { + "allowNullOrUndefined": true + } + ] + } } ] } diff --git a/e2e/src/app.spec.ts b/e2e/src/app.spec.ts index 34f8eb96..165e7955 100644 --- a/e2e/src/app.spec.ts +++ b/e2e/src/app.spec.ts @@ -5,7 +5,7 @@ import {AppPage} from './app.po'; test.describe('Demo', () => { let app: AppPage; - test.beforeEach(async ({page}) => { + test.beforeEach(({page}) => { app = new AppPage(page); }); @@ -38,7 +38,7 @@ test.describe('Demo', () => { // Search something else, and expect to read the searched value when bluring the field await page.fill(inputSelector, 'any search 3'); - await (await page.locator(inputSelector)).blur(); + await page.locator(inputSelector).blur(); expect(await page.inputValue(inputSelector)).toEqual('name-16'); // Unselect model by entering empty field with ENTER key diff --git a/projects/natural-editor/.eslintrc.json b/projects/natural-editor/.eslintrc.json index 4311a18c..67c536ee 100644 --- a/projects/natural-editor/.eslintrc.json +++ b/projects/natural-editor/.eslintrc.json @@ -20,6 +20,57 @@ } ] } + }, + { + "files": ["*.html"], + "rules": { + "@angular-eslint/template/i18n": [ + "error", + { + "checkId": false, + "ignoreAttributes": [ + "autocomplete", + "charset", + "class", + "color", + "colspan", + "dir", + "fill", + "for", + "formArrayName", + "formControlName", + "formGroupName", + "height", + "href", + "id", + "lang", + "list", + "name", + "ngClass", + "ngProjectAs", + "role", + "routerLink", + "routerLinkActive", + "src", + "stroke", + "stroke-width", + "style", + "svgIcon", + "tabindex", + "target", + "type", + "value", + "viewBox", + "width", + "xmlns", + "align", + "naturalIcon", + "naturalCustomCss", + "fontIcon" + ] + } + ] + } } ] } diff --git a/projects/natural-editor/src/lib/class-dialog/class-dialog.component.ts b/projects/natural-editor/src/lib/class-dialog/class-dialog.component.ts index 00fa63e3..98843a16 100644 --- a/projects/natural-editor/src/lib/class-dialog/class-dialog.component.ts +++ b/projects/natural-editor/src/lib/class-dialog/class-dialog.component.ts @@ -7,7 +7,7 @@ import {MatButtonModule} from '@angular/material/button'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; -export interface ClassDialogData { +export type ClassDialogData = { /** * CSS class names * @@ -17,7 +17,7 @@ export interface ClassDialogData { * - `"my-class my-other-class"` */ class: string; -} +}; @Component({ templateUrl: './class-dialog.component.html', diff --git a/projects/natural-editor/src/lib/color-dialog/color-dialog.component.html b/projects/natural-editor/src/lib/color-dialog/color-dialog.component.html index 28a21a20..6602f82b 100644 --- a/projects/natural-editor/src/lib/color-dialog/color-dialog.component.html +++ b/projects/natural-editor/src/lib/color-dialog/color-dialog.component.html @@ -13,7 +13,7 @@

Sélectionner une couleur

diff --git a/projects/natural-editor/src/lib/color-dialog/color-dialog.component.ts b/projects/natural-editor/src/lib/color-dialog/color-dialog.component.ts index 91390c79..fee666be 100644 --- a/projects/natural-editor/src/lib/color-dialog/color-dialog.component.ts +++ b/projects/natural-editor/src/lib/color-dialog/color-dialog.component.ts @@ -8,7 +8,7 @@ import {NaturalIconDirective} from '@ecodev/natural'; import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; -export interface ColorDialogData { +export type ColorDialogData = { /** * Empty string means no color set at all. Anything else must be a valid CSS color in hexa format. * @@ -18,7 +18,7 @@ export interface ColorDialogData { * - `"#ff4000"` */ color: string; -} +}; @Component({ templateUrl: './color-dialog.component.html', diff --git a/projects/natural-editor/src/lib/custom-css/custom-css.directive.spec.ts b/projects/natural-editor/src/lib/custom-css/custom-css.directive.spec.ts index 6999a6da..9c8cfca9 100644 --- a/projects/natural-editor/src/lib/custom-css/custom-css.directive.spec.ts +++ b/projects/natural-editor/src/lib/custom-css/custom-css.directive.spec.ts @@ -31,7 +31,7 @@ describe('NaturalLinkableTabDirective', () => { document = TestBed.inject(DOCUMENT); }); - it('should inject CSS and remove it', async () => { + it('should inject CSS and remove it', () => { fixture.detectChanges(); const expected1 = `[data-natural-id=n1] p {background: pink;}`; diff --git a/projects/natural-editor/src/lib/editor/editor.component.html b/projects/natural-editor/src/lib/editor/editor.component.html index 6493c1a9..d8b5cf7f 100644 --- a/projects/natural-editor/src/lib/editor/editor.component.html +++ b/projects/natural-editor/src/lib/editor/editor.component.html @@ -3,7 +3,7 @@ diff --git a/projects/natural-editor/src/lib/editor/editor.component.spec.ts b/projects/natural-editor/src/lib/editor/editor.component.spec.ts index 539abdb6..609d1e82 100644 --- a/projects/natural-editor/src/lib/editor/editor.component.spec.ts +++ b/projects/natural-editor/src/lib/editor/editor.component.spec.ts @@ -10,7 +10,7 @@ import {naturalProviders} from '@ecodev/natural'; const IMAGE_UPLOADER = new InjectionToken('Image uploader for tests'); @Component({ - template: ` `, + template: ` `, standalone: true, imports: [FormsModule, NaturalEditorComponent], }) diff --git a/projects/natural-editor/src/lib/id-dialog/id-dialog.component.html b/projects/natural-editor/src/lib/id-dialog/id-dialog.component.html index b6a43e73..8893c1af 100644 --- a/projects/natural-editor/src/lib/id-dialog/id-dialog.component.html +++ b/projects/natural-editor/src/lib/id-dialog/id-dialog.component.html @@ -2,7 +2,7 @@

Saisir le ID

- ID + ID @if (idControl.hasError('pattern')) { Doit être un ID valide diff --git a/projects/natural-editor/src/lib/id-dialog/id-dialog.component.ts b/projects/natural-editor/src/lib/id-dialog/id-dialog.component.ts index f1b0674f..e1c70816 100644 --- a/projects/natural-editor/src/lib/id-dialog/id-dialog.component.ts +++ b/projects/natural-editor/src/lib/id-dialog/id-dialog.component.ts @@ -7,7 +7,7 @@ import {MatButtonModule} from '@angular/material/button'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; -export interface IdDialogData { +export type IdDialogData = { /** * ID name * @@ -17,7 +17,7 @@ export interface IdDialogData { * - `"my-id"` */ id: string; -} +}; @Component({ templateUrl: './id-dialog.component.html', diff --git a/projects/natural-editor/src/lib/link-dialog/link-dialog.component.ts b/projects/natural-editor/src/lib/link-dialog/link-dialog.component.ts index 071794d5..167b6e2b 100644 --- a/projects/natural-editor/src/lib/link-dialog/link-dialog.component.ts +++ b/projects/natural-editor/src/lib/link-dialog/link-dialog.component.ts @@ -7,10 +7,10 @@ import {MatButtonModule} from '@angular/material/button'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; -export interface LinkDialogData { +export type LinkDialogData = { href: string; title?: string; -} +}; @Component({ templateUrl: './link-dialog.component.html', diff --git a/projects/natural-editor/src/lib/utils/image.ts b/projects/natural-editor/src/lib/utils/image.ts index 980b5936..da52289b 100644 --- a/projects/natural-editor/src/lib/utils/image.ts +++ b/projects/natural-editor/src/lib/utils/image.ts @@ -7,7 +7,7 @@ import {DOCUMENT} from '@angular/common'; export type ImageUploader = (file: File) => Observable; -@Injectable() +@Injectable({providedIn: 'root'}) export class ImagePlugin { public readonly plugin: Plugin; @@ -25,11 +25,11 @@ export class ImagePlugin { // See if the transaction adds or removes any placeholders const action = tr.getMeta(self.plugin); - if (action && action.add) { + if (action?.add) { const widget = document.createElement('placeholder'); const deco = Decoration.widget(action.add.pos, widget, {id: action.add.id}); set = set.add(tr.doc, [deco]); - } else if (action && action.remove) { + } else if (action?.remove) { set = set.remove(set.find(undefined, undefined, spec => spec.id === action.remove.id)); } diff --git a/projects/natural-editor/src/lib/utils/items/table-item.ts b/projects/natural-editor/src/lib/utils/items/table-item.ts index e3665f35..2a4407fb 100644 --- a/projects/natural-editor/src/lib/utils/items/table-item.ts +++ b/projects/natural-editor/src/lib/utils/items/table-item.ts @@ -5,7 +5,7 @@ import {Item} from './item'; function createCell( cellType: NodeType, - cellContent?: Fragment | ProsemirrorNode | Array, + cellContent?: Fragment | ProsemirrorNode | ProsemirrorNode[], ): ProsemirrorNode | null | undefined { return cellContent ? cellType.createChecked(null, cellContent) : cellType.createAndFill(); } @@ -15,7 +15,7 @@ function createTable( rowsCount: number, colsCount: number, withHeaderRow: boolean, - cellContent?: Fragment | ProsemirrorNode | Array, + cellContent?: Fragment | ProsemirrorNode | ProsemirrorNode[], ): ProsemirrorNode { const types = tableNodeTypes(state.schema); const headerCells = []; @@ -58,7 +58,7 @@ function addTable( rowsCount?: number; colsCount?: number; withHeaderRow?: boolean; - cellContent?: Fragment | ProsemirrorNode | Array; + cellContent?: Fragment | ProsemirrorNode | ProsemirrorNode[]; } = {}, ): void { const offset = state.tr.selection.anchor + 1; diff --git a/projects/natural-editor/src/lib/utils/keymap.ts b/projects/natural-editor/src/lib/utils/keymap.ts index 2c2bb894..15d07f02 100644 --- a/projects/natural-editor/src/lib/utils/keymap.ts +++ b/projects/natural-editor/src/lib/utils/keymap.ts @@ -15,7 +15,7 @@ import {undoInputRule} from 'prosemirror-inputrules'; import {MarkType, NodeType, Schema} from 'prosemirror-model'; import {Command} from 'prosemirror-state'; -type Keymap = {[key: string]: Command}; +type Keymap = Record; /** * Inspect the given schema looking for marks and nodes from the @@ -51,7 +51,7 @@ export function buildKeymap(schema: Schema, isMac: boolean): Keymap { keys['Mod-z'] = undo; keys['Shift-Mod-z'] = redo; - keys['Backspace'] = undoInputRule; + keys.Backspace = undoInputRule; if (!isMac) { keys['Mod-y'] = redo; } @@ -59,7 +59,7 @@ export function buildKeymap(schema: Schema, isMac: boolean): Keymap { keys['Alt-ArrowUp'] = joinUp; keys['Alt-ArrowDown'] = joinDown; keys['Mod-BracketLeft'] = lift; - keys['Escape'] = selectParentNode; + keys.Escape = selectParentNode; let type: MarkType | NodeType = schema.marks.strong; if (type) { @@ -109,7 +109,7 @@ export function buildKeymap(schema: Schema, isMac: boolean): Keymap { type = schema.nodes.list_item; if (type) { - keys['Enter'] = splitListItem(type); + keys.Enter = splitListItem(type); keys['Mod-['] = liftListItem(type); keys['Mod-]'] = sinkListItem(type); } diff --git a/projects/natural-editor/src/lib/utils/schema/heading.ts b/projects/natural-editor/src/lib/utils/schema/heading.ts index a975a5ae..09a2cad8 100644 --- a/projects/natural-editor/src/lib/utils/schema/heading.ts +++ b/projects/natural-editor/src/lib/utils/schema/heading.ts @@ -61,7 +61,7 @@ export const heading: NodeSpec = { ], toDOM: node => { const {id} = node.attrs; - const attrs: {[key: string]: string} = {}; + const attrs: Record = {}; if (id) { attrs.id = id; diff --git a/projects/natural-editor/src/lib/utils/schema/paragraph-with-alignment.ts b/projects/natural-editor/src/lib/utils/schema/paragraph-with-alignment.ts index deeb4411..ebcd1c97 100644 --- a/projects/natural-editor/src/lib/utils/schema/paragraph-with-alignment.ts +++ b/projects/natural-editor/src/lib/utils/schema/paragraph-with-alignment.ts @@ -42,7 +42,7 @@ export const paragraphWithAlignment: NodeSpec = { ], toDOM: node => { const {align, id} = node.attrs; - const attrs: {[key: string]: string} = {}; + const attrs: Record = {}; let style = ''; if (align && align !== 'left') { diff --git a/projects/natural-editor/src/lib/utils/schema/schema.ts b/projects/natural-editor/src/lib/utils/schema/schema.ts index 62d45cb2..73fdf502 100644 --- a/projects/natural-editor/src/lib/utils/schema/schema.ts +++ b/projects/natural-editor/src/lib/utils/schema/schema.ts @@ -40,11 +40,11 @@ const tmpSchema2 = new Schema({ background: { default: null, getFromDOM(dom) { - return (dom as HTMLElement).style.backgroundColor || null; + return dom.style.backgroundColor || null; }, setDOMAttr(value, attrs) { - if (value) { - attrs.style = (attrs.style || '') + `background-color: ${value};`; + if (typeof value === 'string' && value) { + attrs.style = ((attrs.style as string) || '') + `background-color: ${value};`; } }, }, diff --git a/projects/natural-editor/src/lib/utils/schema/table.ts b/projects/natural-editor/src/lib/utils/schema/table.ts index 4d659e99..8c734d6f 100644 --- a/projects/natural-editor/src/lib/utils/schema/table.ts +++ b/projects/natural-editor/src/lib/utils/schema/table.ts @@ -19,7 +19,7 @@ function getCellAttrs(dom: Node | string, extraAttrs: CellAttributes): null | At for (const prop in extraAttrs) { const getter = extraAttrs[prop].getFromDOM; - const value = getter && getter(dom); + const value = getter?.(dom); if (value != null) result[prop] = value; } diff --git a/projects/natural/.eslintrc.json b/projects/natural/.eslintrc.json index 4311a18c..18daac1a 100644 --- a/projects/natural/.eslintrc.json +++ b/projects/natural/.eslintrc.json @@ -20,6 +20,68 @@ } ] } + }, + { + "files": ["*.html"], + "rules": { + "@angular-eslint/template/i18n": [ + "error", + { + "checkId": false, + "ignoreAttributes": [ + "autocomplete", + "charset", + "class", + "color", + "colspan", + "dir", + "fill", + "for", + "formArrayName", + "formControlName", + "formGroupName", + "height", + "href", + "id", + "lang", + "list", + "name", + "ngClass", + "ngProjectAs", + "role", + "routerLink", + "routerLinkActive", + "src", + "stroke", + "stroke-width", + "style", + "svgIcon", + "tabindex", + "target", + "type", + "value", + "viewBox", + "width", + "xmlns", + "align", + "aria-orientation", + "enumName", + "fontIcon", + "icon", + "loading", + "matColumnDef", + "matTooltipPosition", + "mode", + "naturalCustomCss", + "naturalIcon", + "naturalSrcDensity", + "panelWidth", + "srcset", + "togglePosition" + ] + } + ] + } } ] } diff --git a/projects/natural/src/lib/classes/abstract-list.spec.ts b/projects/natural/src/lib/classes/abstract-list.spec.ts index 580f6557..eec4af44 100644 --- a/projects/natural/src/lib/classes/abstract-list.spec.ts +++ b/projects/natural/src/lib/classes/abstract-list.spec.ts @@ -18,8 +18,7 @@ import {MockApolloProvider} from '../testing/mock-apollo.provider'; [availableColumns]="availableColumns" [selections]="selectedColumns" (selectionChange)="selectColumns($event)" - > - `, + />`, standalone: true, imports: [NaturalColumnsPickerComponent], }) diff --git a/projects/natural/src/lib/classes/abstract-list.ts b/projects/natural/src/lib/classes/abstract-list.ts index 743cb66b..2c89bcb0 100644 --- a/projects/natural/src/lib/classes/abstract-list.ts +++ b/projects/natural/src/lib/classes/abstract-list.ts @@ -151,7 +151,7 @@ export class NaturalAbstractList< /** * Initial sorting */ - protected defaultSorting?: Array; + protected defaultSorting?: Sorting[]; protected readonly router = inject(Router); protected readonly route = inject(ActivatedRoute); @@ -311,7 +311,7 @@ export class NaturalAbstractList< if (paginationChannel && paginationChannel.pagination) { // The cast should not be necessary because Typescript correctly narrow down the type to `PaginationInput` // but somehow still get confused when returning it - return paginationChannel.pagination as PaginationInput; + return paginationChannel.pagination; } return this.defaultPagination; diff --git a/projects/natural/src/lib/classes/abstract-navigable-list.ts b/projects/natural/src/lib/classes/abstract-navigable-list.ts index dfdd7127..f9e3e6e9 100644 --- a/projects/natural/src/lib/classes/abstract-navigable-list.ts +++ b/projects/natural/src/lib/classes/abstract-navigable-list.ts @@ -8,6 +8,7 @@ import {PaginatedData} from './data-source'; import {NaturalQueryVariablesManager, QueryVariables} from './query-variable-manager'; import {ExtractTall, ExtractTallOne, ExtractVall, Literal} from '../types/types'; import {first, Observable} from 'rxjs'; +import {FilterGroupCondition} from '../modules/search/classes/graphql-doctrine.types'; type BreadcrumbItem = { id: string; @@ -62,23 +63,23 @@ export class NaturalAbstractNavigableList< // "na" is a trailing param, and should be considered only when there is no search this.route.params.subscribe(params => { // "ns" stands for natural-search to be shorter in url - if (params['ns']) { + if (params.ns) { return; } - let navigationConditionValue: any | null = null; + let navigationConditionValue: FilterGroupCondition | null = null; // "na" stands for "navigation" (relation) in url - if (params['na']) { - navigationConditionValue = {have: {values: [params['na']]}}; - this.service.getOne(params['na']).subscribe( + if (params.na) { + navigationConditionValue = {have: {values: [params.na]}}; + this.service.getOne(params.na).subscribe( // TODO casting should disappear and instead this class should enforce // the service to support Tone with a new generic (ancestor: BreadcrumbItem) => (this.breadcrumbs = this.getBreadcrumb(ancestor)), ); - const hasAncestorChanged = params['na'] !== this.oldAncertorId; - this.oldAncertorId = params['na']; + const hasAncestorChanged = params.na !== this.oldAncertorId; + this.oldAncertorId = params.na; this.clearSearch(hasAncestorChanged); } else { navigationConditionValue = {empty: {}}; @@ -162,7 +163,7 @@ export class NaturalAbstractNavigableList< * Return an array for router link usage */ public getChildLink(ancestor: {id: string} | null): RouterLink['routerLink'] { - if (ancestor && ancestor.id) { + if (ancestor?.id) { return ['.', {na: ancestor.id}]; } else { return ['.', {}]; diff --git a/projects/natural/src/lib/classes/apollo-utils.ts b/projects/natural/src/lib/classes/apollo-utils.ts index cc9b6c36..734c37ec 100644 --- a/projects/natural/src/lib/classes/apollo-utils.ts +++ b/projects/natural/src/lib/classes/apollo-utils.ts @@ -9,6 +9,7 @@ import extractFiles from 'extract-files/extractFiles.mjs'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error import isExtractableFile from 'extract-files/isExtractableFile.mjs'; +import {Kind, OperationTypeNode} from 'graphql/language'; function isFile(value: unknown): boolean { return ( @@ -51,7 +52,8 @@ export function hasFilesAndProcessDate(variables: unknown): boolean { */ export function isMutation(query: DocumentNode): boolean { return query.definitions.some( - definition => definition.kind === 'OperationDefinition' && definition.operation === 'mutation', + definition => + definition.kind === Kind.OPERATION_DEFINITION && definition.operation === OperationTypeNode.MUTATION, ); } diff --git a/projects/natural/src/lib/classes/data-source.spec.ts b/projects/natural/src/lib/classes/data-source.spec.ts index ea3d1a6b..c47250d9 100644 --- a/projects/natural/src/lib/classes/data-source.spec.ts +++ b/projects/natural/src/lib/classes/data-source.spec.ts @@ -2,9 +2,9 @@ import {NaturalDataSource, PaginatedData} from './data-source'; import {Subject} from 'rxjs'; import {deepFreeze} from '@ecodev/natural'; -interface Model { +type Model = { a: string; -} +}; describe('DataSource', () => { let dataSource: NaturalDataSource>; diff --git a/projects/natural/src/lib/classes/data-source.ts b/projects/natural/src/lib/classes/data-source.ts index 237b994a..d8b2431a 100644 --- a/projects/natural/src/lib/classes/data-source.ts +++ b/projects/natural/src/lib/classes/data-source.ts @@ -3,13 +3,13 @@ import {BehaviorSubject, Observable, Subject} from 'rxjs'; import {map, takeUntil} from 'rxjs/operators'; import {Literal} from '../types/types'; -export interface PaginatedData { +export type PaginatedData = { readonly items: readonly T[]; readonly offset?: number | null; readonly pageSize: number; readonly pageIndex: number; readonly length: number; -} +}; /** * A NaturalDataSource will connect immediately, in order to know as soon as possible if diff --git a/projects/natural/src/lib/classes/query-variable-manager.ts b/projects/natural/src/lib/classes/query-variable-manager.ts index 2c933783..017d5e11 100644 --- a/projects/natural/src/lib/classes/query-variable-manager.ts +++ b/projects/natural/src/lib/classes/query-variable-manager.ts @@ -4,24 +4,25 @@ import {Literal} from '../types/types'; import {mergeOverrideArray} from './utility'; import {hasMixedGroupLogic} from './query-variable-manager-utils'; -export interface QueryVariables { +export type QueryVariables = { + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents filter?: any | null; pagination?: PaginationInput | null; - sorting?: Array | null; -} + sorting?: Sorting[] | null; +}; -export interface PaginationInput { +export type PaginationInput = { offset?: number | null; pageIndex?: number | null; pageSize?: number | null; -} +}; -export interface Sorting { +export type Sorting = { field: any; order?: SortingOrder | null; nullAsHighest?: boolean | null; emptyStringAsHighest?: boolean | null; -} +}; export enum SortingOrder { ASC = 'ASC', @@ -150,12 +151,12 @@ export class NaturalQueryVariablesManager | null { +export function validateSorting(data: unknown): Sorting[] | null { if (!Array.isArray(data)) { return null; } - const result: Array = []; + const result: Sorting[] = []; data.forEach(s => { const r = validateOneSorting(s); if (r) { diff --git a/projects/natural/src/lib/classes/validators.ts b/projects/natural/src/lib/classes/validators.ts index 8fd9145f..58936fa1 100644 --- a/projects/natural/src/lib/classes/validators.ts +++ b/projects/natural/src/lib/classes/validators.ts @@ -32,7 +32,7 @@ export function unique( condition[fieldName] = {equal: {value: control.value}}; if (excludedId) { - condition['id'] = {equal: {value: excludedId, not: true}}; + condition.id = {equal: {value: excludedId, not: true}}; } const variables: QueryVariables = { diff --git a/projects/natural/src/lib/directives/http-prefix.directive.ts b/projects/natural/src/lib/directives/http-prefix.directive.ts index 02236242..29e5c2cc 100644 --- a/projects/natural/src/lib/directives/http-prefix.directive.ts +++ b/projects/natural/src/lib/directives/http-prefix.directive.ts @@ -10,7 +10,7 @@ export function ensureHttpPrefix(value: string | null): string | null { } const completePrefix = /^(https?):\/\//i.test(value); - const startingPrefix = 'https://'.indexOf(value) === 0 || 'http://'.indexOf(value) === 0; + const startingPrefix = 'https://'.startsWith(value) || 'http://'.startsWith(value); if (!completePrefix && !startingPrefix) { return 'http://' + value; diff --git a/projects/natural/src/lib/modules/alert/confirm.component.ts b/projects/natural/src/lib/modules/alert/confirm.component.ts index 7dd6e1cf..bad55184 100644 --- a/projects/natural/src/lib/modules/alert/confirm.component.ts +++ b/projects/natural/src/lib/modules/alert/confirm.component.ts @@ -2,12 +2,12 @@ import {Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogModule} from '@angular/material/dialog'; import {MatButtonModule} from '@angular/material/button'; -export interface NaturalConfirmData { +export type NaturalConfirmData = { title: string; message: string; confirmText: string; cancelText: string; -} +}; @Component({ templateUrl: './confirm.component.html', diff --git a/projects/natural/src/lib/modules/avatar/component/avatar.component.spec.ts b/projects/natural/src/lib/modules/avatar/component/avatar.component.spec.ts index c40d5bc3..08e79f06 100644 --- a/projects/natural/src/lib/modules/avatar/component/avatar.component.spec.ts +++ b/projects/natural/src/lib/modules/avatar/component/avatar.component.spec.ts @@ -11,7 +11,7 @@ describe('NaturalAvatarComponent', () => { let fixture: ComponentFixture; let avatarService: AvatarService; - beforeEach(async () => { + beforeEach(() => { fixture = TestBed.createComponent(NaturalAvatarComponent); component = fixture.componentInstance; avatarService = TestBed.inject(AvatarService); diff --git a/projects/natural/src/lib/modules/columns-picker/columns-picker.component.html b/projects/natural/src/lib/modules/columns-picker/columns-picker.component.html index 2a3fe3f0..b21ebad2 100644 --- a/projects/natural/src/lib/modules/columns-picker/columns-picker.component.html +++ b/projects/natural/src/lib/modules/columns-picker/columns-picker.component.html @@ -1,7 +1,7 @@
@if ((isMobile | async) && someVisibleButtons()) { @@ -15,7 +15,7 @@ [ngClass]="needMargin(button)" > @if (useCheckbox(button)) { - + } {{ button.label }} @@ -28,7 +28,7 @@ [ngClass]="needMargin(button)" > @if (useCheckbox(button)) { - + } {{ button.label }} @@ -41,7 +41,7 @@ [ngClass]="needMargin(button)" > @if (useCheckbox(button)) { - + } {{ button.label }} @@ -78,7 +78,7 @@ [color]="color(button)" [matTooltip]="button.label" > - + } @if (defaultTrue(button.show) && !button.href && !button.buttons) { @@ -89,7 +89,7 @@ [color]="color(button)" [matTooltip]="button.label" > - + } @if (defaultTrue(button.show) && button.buttons) { @@ -101,7 +101,7 @@ [color]="color(button)" [matTooltip]="button.label" > - + @@ -125,7 +125,7 @@ i18n-matTooltip matTooltip="Sélectionner les colonnes" > - + } } diff --git a/projects/natural/src/lib/modules/columns-picker/columns-picker.component.ts b/projects/natural/src/lib/modules/columns-picker/columns-picker.component.ts index dae346b3..6312f61b 100644 --- a/projects/natural/src/lib/modules/columns-picker/columns-picker.component.ts +++ b/projects/natural/src/lib/modules/columns-picker/columns-picker.component.ts @@ -102,11 +102,6 @@ export class NaturalColumnsPickerComponent implements OnChanges, OnDestroy { this.selectionChange.emit(selectedColumns); } - public ngOnDestroy(): void { - this.ngUnsubscribe.next(); // unsubscribe everybody - this.ngUnsubscribe.complete(); // complete the stream, because we will never emit again - } - public ngOnChanges(changes: SimpleChanges): void { // Unfortunately need a timeout to avoid an ExpressionChangedAfterItHasBeenCheckedError on /state/4989/process cancellableTimeout(this.ngUnsubscribe).subscribe(() => { @@ -119,6 +114,11 @@ export class NaturalColumnsPickerComponent implements OnChanges, OnDestroy { }); } + public ngOnDestroy(): void { + this.ngUnsubscribe.next(); // unsubscribe everybody + this.ngUnsubscribe.complete(); // complete the stream, because we will never emit again + } + public defaultTrue(value: boolean | undefined): boolean { return value ?? true; } diff --git a/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.spec.ts b/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.spec.ts index 3fffef0e..538028c4 100644 --- a/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.spec.ts +++ b/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.spec.ts @@ -11,7 +11,7 @@ import {HarnessLoader} from '@angular/cdk/testing'; import {NaturalLinkableTabDirective} from '@ecodev/natural'; @Component({ - template: '', + template: '', standalone: true, imports: [RouterOutlet], }) @@ -22,9 +22,9 @@ class TestRootComponent { @Component({ template: ` - Tab content 1 - Tab content 2 - Tab content 3 + Tab content 1 + Tab content 2 + Tab content 3 `, standalone: true, diff --git a/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts b/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts index d372da1d..6c4ac1e6 100644 --- a/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts +++ b/projects/natural/src/lib/modules/common/directives/linkable-tab.directive.ts @@ -71,7 +71,7 @@ export class NaturalLinkableTabDirective extends NaturalAbstractController imple this.router.navigate(['.', params], { relativeTo: this.route, queryParamsHandling: 'preserve', - fragment: activatedTabName && activatedTabName.length ? activatedTabName : undefined, + fragment: activatedTabName?.length ? activatedTabName : undefined, }); }); } diff --git a/projects/natural/src/lib/modules/common/services/seo.service.spec.ts b/projects/natural/src/lib/modules/common/services/seo.service.spec.ts index d8adc732..3530d894 100644 --- a/projects/natural/src/lib/modules/common/services/seo.service.spec.ts +++ b/projects/natural/src/lib/modules/common/services/seo.service.spec.ts @@ -15,7 +15,8 @@ import {Meta, Title} from '@angular/platform-browser'; import {of} from 'rxjs'; @Component({ - template: `
Test simple component
`, + standalone: true, + template: `
Test simple component
`, }) class TestSimpleComponent {} diff --git a/projects/natural/src/lib/modules/common/services/seo.service.ts b/projects/natural/src/lib/modules/common/services/seo.service.ts index 20dfe3b3..b5214861 100644 --- a/projects/natural/src/lib/modules/common/services/seo.service.ts +++ b/projects/natural/src/lib/modules/common/services/seo.service.ts @@ -42,9 +42,7 @@ type NaturalLinkDefinition = { sizes?: string; target?: string; type?: string; -} & { - [prop: string]: string; -}; +} & Record; /** * Typically used for a "dynamic" page where a single object is resolved. So a detail page, such * as the detail of a risk and so on. @@ -75,14 +73,14 @@ export type NaturalSeoResolveData = { seo: NaturalSeoBasic; }; -interface Robots { +type Robots = { /** * If given will be used as robots meta tag, otherwise fallback on default value */ robots?: string; -} +}; -interface NaturalSeoConfigPlain { +type NaturalSeoConfigPlain = { /** * The name of the application that will always appear in the page title */ @@ -112,7 +110,7 @@ interface NaturalSeoConfigPlain { * */ readonly languages?: Readonly; -} +}; export type NaturalSeoConfig = NaturalSeoConfigPlain | Observable; export const NATURAL_SEO_CONFIG = new InjectionToken('Configuration for SEO service'); @@ -233,7 +231,7 @@ export class NaturalSeoService { // need better like something recursive ? if (urlTree.root.hasChildren()) { - const segments = urlTree.root.children['primary'].segments; + const segments = urlTree.root.children.primary.segments; if (segments && segments.length > 0) { url += '/' + segments.map(segment => segment.path).join('/'); } diff --git a/projects/natural/src/lib/modules/detail-header/detail-header.component.html b/projects/natural/src/lib/modules/detail-header/detail-header.component.html index 59dcc4f1..b8ca4b51 100644 --- a/projects/natural/src/lib/modules/detail-header/detail-header.component.html +++ b/projects/natural/src/lib/modules/detail-header/detail-header.component.html @@ -15,7 +15,7 @@
@if (icon) {
- +
} @if (!model.id) { @@ -25,6 +25,6 @@
{{ model.name || model.fullName || label }}
}
- +
diff --git a/projects/natural/src/lib/modules/dialog-trigger/dialog-trigger.component.ts b/projects/natural/src/lib/modules/dialog-trigger/dialog-trigger.component.ts index a13b0905..3e50ee74 100644 --- a/projects/natural/src/lib/modules/dialog-trigger/dialog-trigger.component.ts +++ b/projects/natural/src/lib/modules/dialog-trigger/dialog-trigger.component.ts @@ -3,17 +3,18 @@ import {Component, OnDestroy} from '@angular/core'; import {MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog'; import {ActivatedRoute, Router, RouterLink} from '@angular/router'; -export interface NaturalDialogTriggerRoutingData { +export type NaturalDialogTriggerRoutingData = { component: ComponentType; afterClosedRoute?: RouterLink['routerLink']; dialogConfig: MatDialogConfig; -} +}; export type NaturalDialogTriggerProvidedData = { data?: Readonly | null; activatedRoute: ActivatedRoute; }; +// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents export type NaturalDialogTriggerRedirectionValues = RouterLink['routerLink'] | null | undefined | '' | -1; @Component({ diff --git a/projects/natural/src/lib/modules/dropdown-components/abstract-association-select-component.directive.ts b/projects/natural/src/lib/modules/dropdown-components/abstract-association-select-component.directive.ts index 24492cd7..f3a33972 100644 --- a/projects/natural/src/lib/modules/dropdown-components/abstract-association-select-component.directive.ts +++ b/projects/natural/src/lib/modules/dropdown-components/abstract-association-select-component.directive.ts @@ -111,7 +111,7 @@ export abstract class AbstractAssociationSelectComponent implements DropdownC case 'none': return {empty: {not: false}}; default: - throw new Error('Unsupported operator key: ' + key); + throw new Error('Unsupported operator key: ' + (key as string)); } } } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-boolean/type-boolean.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-boolean/type-boolean.component.ts index e03772e6..e6f5ae6d 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-boolean/type-boolean.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-boolean/type-boolean.component.ts @@ -7,10 +7,10 @@ import {TypeOptionsComponent, TypeOptionsConfiguration} from '../type-options/ty import {NaturalDropdownRef} from '../../search/dropdown-container/dropdown-ref'; import {MatButtonToggleModule} from '@angular/material/button-toggle'; -export interface TypeBooleanConfiguration { +export type TypeBooleanConfiguration = { displayWhenActive: string; displayWhenInactive: string; -} +}; @Component({ templateUrl: '../type-options/type-options.component.html', diff --git a/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.html b/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.html index eb9fee43..7f736b5b 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.html +++ b/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.html @@ -4,13 +4,14 @@ matInput [matDatepicker]="from" placeholder="De" + i18n-placeholder [formControl]="fromCtrl" [errorStateMatcher]="matcher" [min]="configuration.min" [max]="configuration.max" /> - - + + @if (form.hasError('toGreaterThanFrom')) { {{ render(fromCtrl.value) }} > {{ render(toCtrl.value) }} } @@ -30,13 +31,14 @@ matInput [matDatepicker]="to" placeholder="à" + i18n-placeholder [formControl]="toCtrl" [errorStateMatcher]="matcher" [min]="configuration.min" [max]="configuration.max" /> - - + + @if (toCtrl.hasError('min') && !form.hasError('toGreaterThanFrom')) { < {{ configuration.min }} } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.ts index 1c96bca7..59ec1a2f 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-date-range/type-date-range.component.ts @@ -22,10 +22,10 @@ import {MatDatepickerModule} from '@angular/material/datepicker'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; -export interface TypeDateRangeConfiguration { +export type TypeDateRangeConfiguration = { min?: D | null; max?: D | null; -} +}; class InvalidWithValueStateMatcher implements ErrorStateMatcher { public isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { diff --git a/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.html b/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.html index edc9dab9..236c3bda 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.html +++ b/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.html @@ -20,8 +20,8 @@ [required]="true" matInput /> - - + + @if (valueCtrl.hasError('min')) { < {{ configuration.min }} } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.spec.ts b/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.spec.ts index 8799d5d5..2149bae0 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.spec.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.spec.ts @@ -10,7 +10,7 @@ import { } from '@ecodev/natural'; import {Injectable} from '@angular/core'; -@Injectable() +@Injectable({providedIn: 'root'}) class ImpossibleParsingDateAdapter extends NativeDateAdapter { public override parse(): Date | null { throw new Error('`parse` method should never be called at all'); @@ -77,9 +77,9 @@ describe('TypeDateComponent', () => { less: {value: '2019-09-09'}, // the date will be transparently fixed into "tomorrow" }; - const config: TypeDateConfiguration = {}; + const config: TypeDateConfiguration = {}; - const configWithRules: TypeDateConfiguration = { + const configWithRules: TypeDateConfiguration = { min: new Date('2001-01-01'), max: new Date('2010-01-01'), }; @@ -104,10 +104,7 @@ describe('TypeDateComponent', () => { }).compileComponents(); }); - function createComponent( - c: FilterGroupConditionField | null, - configuration: TypeDateConfiguration | null, - ): void { + function createComponent(c: FilterGroupConditionField | null, configuration: TypeDateConfiguration | null): void { data.condition = c; data.configuration = configuration; TestBed.overrideProvider(NATURAL_DROPDOWN_DATA, {useValue: data}); diff --git a/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.ts index 10e99eb5..c2596251 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-date/type-date.component.ts @@ -15,10 +15,10 @@ import {MatInputModule} from '@angular/material/input'; import {MatSelectModule} from '@angular/material/select'; import {MatFormFieldModule} from '@angular/material/form-field'; -export interface TypeDateConfiguration { +export type TypeDateConfiguration = { min?: D | null; max?: D | null; -} +}; @Component({ templateUrl: './type-date.component.html', diff --git a/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.html b/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.html index 7dd1d59b..bb0fca76 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.html +++ b/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.html @@ -18,6 +18,6 @@ [multiple]="true" [selected]="valueCtrl.value || {}" style="margin-right: 20px" - > + /> } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.ts index b92da654..52b7951b 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-hierarchic-selector/type-hierarchic-selector.component.ts @@ -14,19 +14,19 @@ import {MatSelectModule} from '@angular/material/select'; import {MatFormFieldModule} from '@angular/material/form-field'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -export interface HierarchicFilterConfiguration { +export type HierarchicFilterConfiguration = { service: NaturalHierarchicConfiguration['service']; filter: T; -} +}; -export type HierarchicFiltersConfiguration = Array>; +export type HierarchicFiltersConfiguration = HierarchicFilterConfiguration[]; -export interface TypeHierarchicSelectorConfiguration { +export type TypeHierarchicSelectorConfiguration = { key: string; service: UntypedModelService; config: NaturalHierarchicConfiguration[]; filters?: HierarchicFiltersConfiguration; -} +}; @Component({ templateUrl: './type-hierarchic-selector.component.html', @@ -54,7 +54,8 @@ export class TypeHierarchicSelectorComponent extends AbstractAssociationSelectCo return this.operatorKeyToCondition(this.operatorCtrl.value, ids); } - protected reloadValue(condition: FilterGroupConditionField): Observable { + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + protected reloadValue(condition: FilterGroupConditionField): Observable { if (!condition.have) { return EMPTY; } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.html b/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.html index ee8c3d4c..b61a31f5 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.html +++ b/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.html @@ -17,6 +17,6 @@ [placeholder]="configuration.placeholder" [service]="configuration.service" [filter]="configuration.filter" - > + /> } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.ts index 33155152..d34f18e9 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-natural-select/type-natural-select.component.ts @@ -10,11 +10,11 @@ import {MatSelectModule} from '@angular/material/select'; import {MatFormFieldModule} from '@angular/material/form-field'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; -export interface TypeSelectNaturalConfiguration { +export type TypeSelectNaturalConfiguration = { service: TService; placeholder: string; filter?: ExtractVall['filter']; -} +}; @Component({ templateUrl: './type-natural-select.component.html', diff --git a/projects/natural/src/lib/modules/dropdown-components/type-number/type-number.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-number/type-number.component.ts index 4bd75260..1d38babf 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-number/type-number.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-number/type-number.component.ts @@ -14,11 +14,11 @@ import {MatOptionModule} from '@angular/material/core'; import {MatSelectModule} from '@angular/material/select'; import {MatFormFieldModule} from '@angular/material/form-field'; -export interface TypeNumberConfiguration { +export type TypeNumberConfiguration = { min?: number | null; max?: number | null; step?: number | null; -} +}; @Component({ templateUrl: './type-number.component.html', diff --git a/projects/natural/src/lib/modules/dropdown-components/type-options/type-options.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-options/type-options.component.ts index f184fbfc..c69b0fa4 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-options/type-options.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-options/type-options.component.ts @@ -9,14 +9,14 @@ import {NATURAL_DROPDOWN_DATA, NaturalDropdownData} from '../../search/dropdown- import {DropdownComponent} from '../../search/types/dropdown-component'; import {isEqual} from 'lodash-es'; -export interface TypeOption { +export type TypeOption = { display: string; condition: Literal; -} +}; -export interface TypeOptionsConfiguration { +export type TypeOptionsConfiguration = { options: TypeOption[]; -} +}; @Component({ templateUrl: './type-options.component.html', diff --git a/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.html b/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.html index 261ee1e1..2ad2fcae 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.html +++ b/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.html @@ -15,7 +15,7 @@ @if (requireValueCtrl) { @for (item of items; track item) { - + {{ getDisplay(item) }} } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts index 8cb62cac..7d0e8582 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-select/type-select.component.ts @@ -23,14 +23,14 @@ export type TypeSelectItem = name: Scalar; }; -export interface TypeSelectConfiguration { +export type TypeSelectConfiguration = { items: TypeSelectItem[] | Observable; multiple?: boolean; /** * If true (default) a selectbox allows to choose an operator. Otherwise, the selectbox is hidden and the operator will always be `is`. */ operators?: boolean; -} +}; @Component({ templateUrl: './type-select.component.html', @@ -217,7 +217,7 @@ export class TypeSelectComponent case 'none': return {null: {not: false}}; default: - throw new Error('Unsupported operator key: ' + key); + throw new Error('Unsupported operator key: ' + (key as string)); } } } diff --git a/projects/natural/src/lib/modules/dropdown-components/type-text/type-text.component.ts b/projects/natural/src/lib/modules/dropdown-components/type-text/type-text.component.ts index 57202b90..63d33aed 100644 --- a/projects/natural/src/lib/modules/dropdown-components/type-text/type-text.component.ts +++ b/projects/natural/src/lib/modules/dropdown-components/type-text/type-text.component.ts @@ -40,7 +40,7 @@ export class TypeTextComponent implements DropdownComponent { this.formCtrl.setValidators([Validators.required]); - if (data.condition && data.condition.like) { + if (data.condition?.like) { this.formCtrl.setValue('' + data.condition.like.value); } } diff --git a/projects/natural/src/lib/modules/dropdown-components/types.ts b/projects/natural/src/lib/modules/dropdown-components/types.ts index afd52f72..b288fd06 100644 --- a/projects/natural/src/lib/modules/dropdown-components/types.ts +++ b/projects/natural/src/lib/modules/dropdown-components/types.ts @@ -5,10 +5,10 @@ export type PossibleComparableOpertorKeys = keyof Pick< 'less' | 'lessOrEqual' | 'equal' | 'greaterOrEqual' | 'greater' >; -export interface PossibleComparableOperator { +export type PossibleComparableOperator = { key: PossibleComparableOpertorKeys; label: string; -} +}; export const possibleComparableOperators: Readonly = [ { @@ -35,10 +35,10 @@ export const possibleComparableOperators: Readonly export type PossibleDiscreteOperatorKeys = 'is' | 'isnot' | 'any' | 'none'; -export interface PossibleDiscreteOperator { +export type PossibleDiscreteOperator = { key: PossibleDiscreteOperatorKeys; label: string; -} +}; export const possibleDiscreteOperators: Readonly = [ { diff --git a/projects/natural/src/lib/modules/file/abstract-file.ts b/projects/natural/src/lib/modules/file/abstract-file.ts index a99d3830..da888c35 100644 --- a/projects/natural/src/lib/modules/file/abstract-file.ts +++ b/projects/natural/src/lib/modules/file/abstract-file.ts @@ -26,12 +26,12 @@ import {NaturalAbstractController} from '../../classes/abstract-controller'; import {DOCUMENT} from '@angular/common'; import {forkJoin, map, Observable, ObservableInput, of, tap} from 'rxjs'; -export interface InvalidFile { +export type InvalidFile = { file: File; error: string; -} +}; -export interface FileSelection { +export type FileSelection = { /** * The list of files that have been selected. */ @@ -41,7 +41,7 @@ export interface FileSelection { * The list of files that have been selected but are invalid according to validators. */ invalid: InvalidFile[]; -} +}; /** * A master base set of logic intended to support file select/drag/drop operations diff --git a/projects/natural/src/lib/modules/file/component/file.component.html b/projects/natural/src/lib/modules/file/component/file.component.html index 0768bd44..1efde6aa 100644 --- a/projects/natural/src/lib/modules/file/component/file.component.html +++ b/projects/natural/src/lib/modules/file/component/file.component.html @@ -15,17 +15,17 @@ > @if (filePreview) {
- + {{ filePreview | uppercase }}
}
@if (action === 'upload') { - + } @if (action === 'download') { - + } {{ action | capitalize }}
diff --git a/projects/natural/src/lib/modules/file/component/file.component.ts b/projects/natural/src/lib/modules/file/component/file.component.ts index 0e8673d0..ae83d08e 100644 --- a/projects/natural/src/lib/modules/file/component/file.component.ts +++ b/projects/natural/src/lib/modules/file/component/file.component.ts @@ -92,16 +92,16 @@ export class NaturalFileComponent implements OnInit, OnChanges { @Inject(DOCUMENT) private readonly document: Document, ) {} - public ngOnInit(): void { - this.updateImage(); - } - public ngOnChanges(changes: SimpleChanges): void { if (changes.model && changes.model.previousValue !== changes.model.currentValue) { this.updateImage(); } } + public ngOnInit(): void { + this.updateImage(); + } + public upload(file: File): void { this.model = {file: file}; this.updateImage(); diff --git a/projects/natural/src/lib/modules/file/file-drop.directive.spec.ts b/projects/natural/src/lib/modules/file/file-drop.directive.spec.ts index 14e8ed37..8d37c774 100644 --- a/projects/natural/src/lib/modules/file/file-drop.directive.spec.ts +++ b/projects/natural/src/lib/modules/file/file-drop.directive.spec.ts @@ -4,7 +4,7 @@ import {NaturalFileService} from '@ecodev/natural'; import {NaturalFileDropDirective} from './file-drop.directive'; @Component({ - template: '
my drag and drop area
', + template: '
my drag and drop area
', standalone: true, imports: [NaturalFileDropDirective], }) diff --git a/projects/natural/src/lib/modules/fixed-button-detail/fixed-button-detail.component.html b/projects/natural/src/lib/modules/fixed-button-detail/fixed-button-detail.component.html index 22b16817..757d2ed0 100644 --- a/projects/natural/src/lib/modules/fixed-button-detail/fixed-button-detail.component.html +++ b/projects/natural/src/lib/modules/fixed-button-detail/fixed-button-detail.component.html @@ -5,7 +5,7 @@ [color]="form.valid ? 'accent' : 'warn'" class="detail-speed-dial" icon="save" - > + /> } @if (!isCreation && (!model.permissions || model.permissions.delete)) { @@ -18,5 +18,5 @@ i18n-matTooltip matTooltip="Supprimer définitivement" matTooltipPosition="left" - > + /> } diff --git a/projects/natural/src/lib/modules/fixed-button/fixed-button.component.html b/projects/natural/src/lib/modules/fixed-button/fixed-button.component.html index 0f67ab58..86931c2f 100644 --- a/projects/natural/src/lib/modules/fixed-button/fixed-button.component.html +++ b/projects/natural/src/lib/modules/fixed-button/fixed-button.component.html @@ -1,3 +1,3 @@ diff --git a/projects/natural/src/lib/modules/hierarchic-selector/classes/flat-node.ts b/projects/natural/src/lib/modules/hierarchic-selector/classes/flat-node.ts index ecdf8bfc..6974122a 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/classes/flat-node.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/classes/flat-node.ts @@ -6,9 +6,9 @@ export class HierarchicFlatNode { public constructor( public readonly node: HierarchicModelNode, public readonly name: string, - public readonly level: number = 0, - public expandable: boolean = false, - public readonly selectable: boolean = true, - public deselectable: boolean = true, + public readonly level = 0, + public expandable = false, + public readonly selectable = true, + public deselectable = true, ) {} } diff --git a/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-configuration.ts b/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-configuration.ts index 8646d202..544b1aa2 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-configuration.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-configuration.ts @@ -2,7 +2,7 @@ import {Type} from '@angular/core'; import {QueryVariables} from '../../../classes/query-variable-manager'; import {UntypedModelService} from '../../../types/types'; -export interface NaturalHierarchicConfiguration { +export type NaturalHierarchicConfiguration = { /** * An AbstractModelService to be used to fetch items */ @@ -62,9 +62,8 @@ export interface NaturalHierarchicConfiguration string; -} +}; -export interface NaturalHierarchicServiceConfiguration - extends NaturalHierarchicConfiguration { +export type NaturalHierarchicServiceConfiguration = { injectedService: T; -} +} & NaturalHierarchicConfiguration; diff --git a/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-filters-configuration.ts b/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-filters-configuration.ts index 7c8a1fcf..fa996119 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-filters-configuration.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/classes/hierarchic-filters-configuration.ts @@ -1,9 +1,9 @@ import {Literal} from '../../../types/types'; import {NaturalHierarchicConfiguration} from './hierarchic-configuration'; -export interface HierarchicFilterConfiguration { +export type HierarchicFilterConfiguration = { service: NaturalHierarchicConfiguration['service']; filter: T; -} +}; -export type HierarchicFiltersConfiguration = Array>; +export type HierarchicFiltersConfiguration = HierarchicFilterConfiguration[]; diff --git a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.html b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.html index 2a9c187a..4df48cee 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.html +++ b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.html @@ -11,7 +11,7 @@

Sélection

[searchFacets]="config.searchFacets ?? []" [searchSelections]="config.searchSelections ?? []" (searchSelectionChange)="searchSelectionsOutput = $event" - > + /> diff --git a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.ts b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.ts index 98568bf2..52f59e51 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector-dialog/hierarchic-selector-dialog.component.ts @@ -9,12 +9,12 @@ import {OrganizedModelSelection} from '../hierarchic-selector/hierarchic-selecto import {MatButtonModule} from '@angular/material/button'; import {NaturalHierarchicSelectorComponent} from '../hierarchic-selector/hierarchic-selector.component'; -export interface HierarchicDialogResult { +export type HierarchicDialogResult = { hierarchicSelection?: OrganizedModelSelection; searchSelections?: NaturalSearchSelections | null; -} +}; -export interface HierarchicDialogConfig { +export type HierarchicDialogConfig = { /** * Configuration to setup rules of hierarchy */ @@ -49,7 +49,7 @@ export interface HierarchicDialogConfig { * Selections of natural search to initialize on HierarchicComponent initialisation */ searchSelections?: NaturalSearchSelections | null; -} +}; @Component({ templateUrl: './hierarchic-selector-dialog.component.html', diff --git a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.html b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.html index a8db86e5..d3a12217 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.html +++ b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.html @@ -1,14 +1,10 @@
- +
@if (loading) { - + } @@ -21,16 +17,10 @@ matTreeNodeToggle > @if (node.loading) { - + } @if (!node.loading) { - + } } @@ -42,7 +32,7 @@ style="margin-right: 10px" > @if (node.node.config.icon) { - + } {{ node.name }} @@ -53,11 +43,11 @@ @for (node of selectedNodes; track node) { @if (node.config.icon) { - + }
{{ node.model.name || node.model.fullName }}
} diff --git a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts index ff5d3e61..5746c30f 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.component.ts @@ -121,6 +121,19 @@ export class NaturalHierarchicSelectorComponent extends NaturalAbstractControlle super(); } + /** + * Angular OnChange implementation + */ + public ngOnChanges(changes: SimpleChanges): void { + if (changes.selected && !changes.selected.firstChange) { + this.updateInnerSelection(this.selected); + } + + if (changes.filters && !changes.filters.firstChange) { + this.loadRoots(); + } + } + /** * Angular OnInit implementation */ @@ -158,19 +171,6 @@ export class NaturalHierarchicSelectorComponent extends NaturalAbstractControlle this.updateInnerSelection(this.selected); } - /** - * Angular OnChange implementation - */ - public ngOnChanges(changes: SimpleChanges): void { - if (changes.selected && !changes.selected.firstChange) { - this.updateInnerSelection(this.selected); - } - - if (changes.filters && !changes.filters.firstChange) { - this.loadRoots(); - } - } - /** * Toggle selection of a FlatNode, considering if multiple selection is activated or not */ diff --git a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.ts b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.ts index 2586e54c..e2eeb98d 100644 --- a/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.ts +++ b/projects/natural/src/lib/modules/hierarchic-selector/hierarchic-selector/hierarchic-selector.service.ts @@ -17,14 +17,12 @@ import {HierarchicModel, HierarchicModelNode} from '../classes/model-node'; import {Literal} from '../../../types/types'; import {FilterGroupCondition} from '../../search/classes/graphql-doctrine.types'; -export interface OrganizedModelSelection { - [key: string]: any[]; -} +export type OrganizedModelSelection = Record; -interface ContextualizedConfig { +type ContextualizedConfig = { configuration: NaturalHierarchicServiceConfiguration; variablesManager: NaturalQueryVariablesManager; -} +}; @Injectable({providedIn: 'root'}) export class NaturalHierarchicSelectorService { @@ -180,12 +178,12 @@ export class NaturalHierarchicSelectorService { * Returns a Literal of models grouped by their configuration attribute "selectableAtKey" */ public toOrganizedSelection(nodes: HierarchicModelNode[]): OrganizedModelSelection { - const selection = this.configuration.reduce((group, config) => { + const selection = this.configuration.reduce((group, config) => { if (config.selectableAtKey) { group[config.selectableAtKey] = []; } return group; - }, {} as Literal); + }, {}); for (const node of nodes) { if (node.config.selectableAtKey) { diff --git a/projects/natural/src/lib/modules/icon/icon.directive.spec.ts b/projects/natural/src/lib/modules/icon/icon.directive.spec.ts index 5c5e0fbf..ff9e90c1 100644 --- a/projects/natural/src/lib/modules/icon/icon.directive.spec.ts +++ b/projects/natural/src/lib/modules/icon/icon.directive.spec.ts @@ -35,7 +35,7 @@ describe('NaturalIconComponent', () => { let elements: DebugElement[]; // the elements with the directive let httpTestingController: HttpTestingController; - beforeEach(async () => { + beforeEach(() => { fixture = TestBed.configureTestingModule({ imports: [HttpClientTestingModule], providers: [ diff --git a/projects/natural/src/lib/modules/icon/icon.directive.ts b/projects/natural/src/lib/modules/icon/icon.directive.ts index 0b5705af..73da95bc 100644 --- a/projects/natural/src/lib/modules/icon/icon.directive.ts +++ b/projects/natural/src/lib/modules/icon/icon.directive.ts @@ -2,22 +2,20 @@ import {Directive, Host, HostBinding, Inject, InjectionToken, Input, Optional, S import {MatIcon, MatIconRegistry} from '@angular/material/icon'; import {DomSanitizer} from '@angular/platform-browser'; -interface NaturalIconType { +type NaturalIconType = { name: string; svg?: string; font?: string; class?: 'negative' | 'neutral' | 'positive'; -} +}; -export interface NaturalIconConfig { +export type NaturalIconConfig = { svg?: string; font?: string; class?: 'negative' | 'neutral' | 'positive'; -} +}; -export interface NaturalIconsConfig { - [key: string]: NaturalIconConfig; -} +export type NaturalIconsConfig = Record; export const NATURAL_ICONS_CONFIG = new InjectionToken('Configuration for Natural Icons'); diff --git a/projects/natural/src/lib/modules/logger/error-handler.ts b/projects/natural/src/lib/modules/logger/error-handler.ts index e9dafde4..22b5ac2d 100644 --- a/projects/natural/src/lib/modules/logger/error-handler.ts +++ b/projects/natural/src/lib/modules/logger/error-handler.ts @@ -3,7 +3,7 @@ import {HttpClient, HttpHeaders} from '@angular/common/http'; import {ErrorHandler, Inject, Injectable, InjectionToken, Optional} from '@angular/core'; import {catchError, EMPTY, first, Observable, of} from 'rxjs'; -export interface NaturalLoggerType { +export type NaturalLoggerType = { message: string; stacktrace?: string; href?: string; @@ -17,9 +17,9 @@ export interface NaturalLoggerType { user?: string; [key: string]: any; -} +}; -export interface NaturalLoggerExtra { +export type NaturalLoggerExtra = { /** * Return an observable of extra data that will be logged. Those data will be merged into * the original data, and so it can override things. @@ -27,7 +27,7 @@ export interface NaturalLoggerExtra { * Only the first emitted value will be used. */ getExtras(error: unknown): Observable>; -} +}; export const NaturalLoggerConfigUrl = new InjectionToken('Absolute URL of the log server'); export const NaturalLoggerConfigExtra = new InjectionToken( diff --git a/projects/natural/src/lib/modules/matomo/matomo.service.spec.ts b/projects/natural/src/lib/modules/matomo/matomo.service.spec.ts index 249fa4f3..df0abaa2 100644 --- a/projects/natural/src/lib/modules/matomo/matomo.service.spec.ts +++ b/projects/natural/src/lib/modules/matomo/matomo.service.spec.ts @@ -13,7 +13,8 @@ import {DOCUMENT} from '@angular/common'; import {PaqItem} from './matomo.service'; @Component({ - template: `
Test component
`, + standalone: true, + template: `
Test component
`, }) class TestSimpleComponent {} diff --git a/projects/natural/src/lib/modules/matomo/matomo.service.ts b/projects/natural/src/lib/modules/matomo/matomo.service.ts index 1dbe0339..4e3af62a 100644 --- a/projects/natural/src/lib/modules/matomo/matomo.service.ts +++ b/projects/natural/src/lib/modules/matomo/matomo.service.ts @@ -21,9 +21,9 @@ type MatomoFunction = export type PaqItem = [MatomoFunction, ...(number | string | null)[]]; -interface Paq { +type Paq = { push: (item: PaqItem) => void; -} +}; /** * Service to track visitors via Matomo. diff --git a/projects/natural/src/lib/modules/panels/abstract-panel.ts b/projects/natural/src/lib/modules/panels/abstract-panel.ts index 44134f36..c1662404 100644 --- a/projects/natural/src/lib/modules/panels/abstract-panel.ts +++ b/projects/natural/src/lib/modules/panels/abstract-panel.ts @@ -47,7 +47,7 @@ export class NaturalAbstractPanel extends NaturalAbstractController { this.panelData = panelData; this.isPanel = true; - if (this.panelData && this.panelData.data) { + if (this.panelData?.data) { merge(this.data, this.panelData.data); } } diff --git a/projects/natural/src/lib/modules/panels/panels.service.ts b/projects/natural/src/lib/modules/panels/panels.service.ts index 7ab4f9b6..8e233368 100644 --- a/projects/natural/src/lib/modules/panels/panels.service.ts +++ b/projects/natural/src/lib/modules/panels/panels.service.ts @@ -297,7 +297,7 @@ export class NaturalPanelsService { linkableObjects: [], }; - if (this.hooksConfig && this.hooksConfig.beforeOpenPanel) { + if (this.hooksConfig?.beforeOpenPanel) { const event: NaturalPanelsBeforeOpenPanel = { itemData: itemData, panelConfig: config, @@ -319,13 +319,13 @@ export class NaturalPanelsService { return subject; } - private getResolvedData(config: NaturalPanelConfig): Observable<{[key: string]: unknown}> { + private getResolvedData(config: NaturalPanelConfig): Observable> { if (!config.resolve || (config.resolve && Object.keys(config.resolve).length === 0)) { return of({}); } const resolveKeys = Object.keys(config.resolve); - const resolvedData: {[key: string]: Observable} = {}; + const resolvedData: Record> = {}; const injector = config.injector; if (injector) { diff --git a/projects/natural/src/lib/modules/panels/panels.spec.ts b/projects/natural/src/lib/modules/panels/panels.spec.ts index 2c8d86e5..f2ad1436 100644 --- a/projects/natural/src/lib/modules/panels/panels.spec.ts +++ b/projects/natural/src/lib/modules/panels/panels.spec.ts @@ -18,7 +18,7 @@ import {fallbackIfNoOpenedPanels} from './fallback-if-no-opened-panels.urlmatche @Component({ selector: 'natural-test-root', - template: '', + template: '', standalone: true, imports: [RouterOutlet], }) @@ -28,7 +28,7 @@ class TestRootComponent { @Component({ selector: 'natural-test-no-panel', - template: `

Page without panels at all

`, + template: `

Page without panels at all

`, standalone: true, }) class TestNoPanelComponent {} @@ -36,8 +36,8 @@ class TestNoPanelComponent {} @Component({ selector: 'natural-test-with-panel', template: ` -

Page with panels

- +

Page with panels

+ `, standalone: true, imports: [RouterOutlet], @@ -46,21 +46,21 @@ class TestWithPanelComponent {} @Component({ selector: 'natural-test-panel-a', - template: `

Panel A content

`, + template: `

Panel A content

`, standalone: true, }) class TestPanelAComponent extends NaturalAbstractPanel {} @Component({ selector: 'natural-test-panel-b', - template: `

Panel B content

`, + template: `

Panel B content

`, standalone: true, }) class TestPanelBComponent extends NaturalAbstractPanel {} @Component({ selector: 'natural-test-fallback', - template: `

404 fallback page

`, + template: `

404 fallback page

`, standalone: true, }) class TestFallbackComponent {} diff --git a/projects/natural/src/lib/modules/panels/panels.urlmatcher.ts b/projects/natural/src/lib/modules/panels/panels.urlmatcher.ts index b26dd475..33600aa6 100644 --- a/projects/natural/src/lib/modules/panels/panels.urlmatcher.ts +++ b/projects/natural/src/lib/modules/panels/panels.urlmatcher.ts @@ -55,7 +55,7 @@ function getComponentConfig( } // If find variable, store it - if (configSegments[i].indexOf(':') > -1 && +segments[i].path > 0) { + if (configSegments[i].includes(':') && +segments[i].path > 0) { params[configSegments[i].replace(':', '')] = segments[i].path; } else if (configSegments[i] !== segments[i].path) { // If segments are different, url does not match diff --git a/projects/natural/src/lib/modules/panels/types.ts b/projects/natural/src/lib/modules/panels/types.ts index 9ed0e177..f5ae1415 100644 --- a/projects/natural/src/lib/modules/panels/types.ts +++ b/projects/natural/src/lib/modules/panels/types.ts @@ -9,27 +9,27 @@ import {LinkableObject} from '../../services/link-mutation.service'; /** * Kind of snapshot of the instance of a panel activated route */ -export interface NaturalPanelsRouteConfig { +export type NaturalPanelsRouteConfig = { segments: UrlSegment[]; path: string; -} +}; /** * Config required to manage url and instantiate component correctly */ -export interface NaturalPanelConfig { +export type NaturalPanelConfig = { component: ComponentType; injector: Injector | null; resolve: NaturalPanelResolves; params: Literal; rule: NaturalPanelsRouterRule; route: NaturalPanelsRouteConfig; -} +}; /** * Data provided to instantiated components in context of a panel/dialog */ -export interface NaturalPanelData { +export type NaturalPanelData = { config: NaturalPanelConfig; data: Literal; @@ -37,37 +37,37 @@ export interface NaturalPanelData { * Related objects that should be linked to the object shown in the panel after its creation */ linkableObjects: LinkableObject[]; -} +}; /** * Similar to Angular functional resolver interface, but simpler for our panels' needs */ type NaturalPanelResolve = (route: NaturalPanelConfig) => Observable; -export type NaturalPanelResolves = {[key: string]: NaturalPanelResolve}; +export type NaturalPanelResolves = Record>; /** * Configuration for a route */ -export interface NaturalPanelsRouterRule { +export type NaturalPanelsRouterRule = { path: string; component: ComponentType; resolve?: NaturalPanelResolves; -} +}; -export interface NaturalPanelsBeforeOpenPanel { +export type NaturalPanelsBeforeOpenPanel = { itemData: NaturalPanelData; panelConfig: NaturalPanelConfig; fullPanelsConfig: NaturalPanelConfig[]; resolvedResult: any; // todo : Generic or NaturalAbstractModelService -} +}; -export interface NaturalPanelsHooksConfig { +export type NaturalPanelsHooksConfig = { beforeOpenPanel?: ( injector: Injector, naturalPanelsBeforeOpenPanel: NaturalPanelsBeforeOpenPanel, ) => NaturalPanelData; -} +}; // Array of NaturalPanelsRouterRule -export type NaturalPanelsRoutesConfig = Array; +export type NaturalPanelsRoutesConfig = NaturalPanelsRouterRule[]; export const PanelsHooksConfig = new InjectionToken('NaturalPanelsHooksConfig'); diff --git a/projects/natural/src/lib/modules/relations/relations.component.html b/projects/natural/src/lib/modules/relations/relations.component.html index d1872958..45202484 100644 --- a/projects/natural/src/lib/modules/relations/relations.component.html +++ b/projects/natural/src/lib/modules/relations/relations.component.html @@ -13,7 +13,7 @@ + /> @@ -28,7 +28,7 @@ i18n-matTooltip matTooltip="Dissocier" > - + } @@ -43,7 +43,7 @@ [pageIndex]="dataSource?.data?.pageIndex || 0" [pageSizeOptions]="pageSizeOptions" [pageSize]="dataSource?.data?.pageSize || 0" - > + /> } @if (!loading && dataSource?.data?.length === 0) { @@ -53,7 +53,7 @@ } @if (loading) { - + }
@@ -65,7 +65,7 @@ [placeholder]="placeholder" [service]="service" [showIcon]="false" - > + /> } @if (hierarchicSelectorConfig && !disabled) { diff --git a/projects/natural/src/lib/modules/relations/relations.component.ts b/projects/natural/src/lib/modules/relations/relations.component.ts index d2a208a2..4a3633a1 100644 --- a/projects/natural/src/lib/modules/relations/relations.component.ts +++ b/projects/natural/src/lib/modules/relations/relations.component.ts @@ -145,7 +145,7 @@ export class NaturalRelationsComponent< /** * Observable variables/options for listing service usage and apollo watchQuery */ - private variablesManager: NaturalQueryVariablesManager = new NaturalQueryVariablesManager(); + private variablesManager = new NaturalQueryVariablesManager(); public readonly removing = new Set(); @@ -167,27 +167,27 @@ export class NaturalRelationsComponent< this.variablesManager.set('relations-filter', {filter: filter}); } - public ngOnInit(): void { - this.pagination(); - - // Force disabled if cannot update object - if (this.main && this.main.permissions) { - this.disabled = this.disabled || !this.main.permissions.update; - } - } - public ngOnChanges(): void { if (this.service) { this.queryItems(); } - if (this.disabled && this.displayedColumns.indexOf('unlink') > -1) { + if (this.disabled && this.displayedColumns.includes('unlink')) { this.displayedColumns.pop(); - } else if (!this.disabled && this.displayedColumns.indexOf('unlink') === -1) { + } else if (!this.disabled && !this.displayedColumns.includes('unlink')) { this.displayedColumns.push('unlink'); } } + public ngOnInit(): void { + this.pagination(); + + // Force disabled if cannot update object + if (this.main && this.main.permissions) { + this.disabled = this.disabled || !this.main.permissions.update; + } + } + /** * Unlink action * Refetch result to display it in table @@ -260,7 +260,7 @@ export class NaturalRelationsComponent< .open(hierarchicConfig) .afterClosed() .subscribe(result => { - if (result && result.hierarchicSelection !== undefined) { + if (result?.hierarchicSelection !== undefined) { const selection = result.hierarchicSelection[selectAtKey]; if (selection.length) { this.addRelations(selection); diff --git a/projects/natural/src/lib/modules/search/classes/graphql-doctrine.ts b/projects/natural/src/lib/modules/search/classes/graphql-doctrine.ts index 2053a843..1be5dc75 100644 --- a/projects/natural/src/lib/modules/search/classes/graphql-doctrine.ts +++ b/projects/natural/src/lib/modules/search/classes/graphql-doctrine.ts @@ -131,7 +131,7 @@ function wrapWithFieldName(field: string, condition: FilterGroupConditionField): } function transformSelection(facet: Facet | null, selection: NaturalSearchSelection): NaturalSearchSelection { - const newSelection = facet && facet.transform ? facet.transform(selection) : selection; + const newSelection = facet?.transform ? facet.transform(selection) : selection; return isDateFacet(facet) ? replaceToday(newSelection) : selection; } diff --git a/projects/natural/src/lib/modules/search/classes/graphql-doctrine.types.ts b/projects/natural/src/lib/modules/search/classes/graphql-doctrine.types.ts index f0b5efac..2c8be627 100644 --- a/projects/natural/src/lib/modules/search/classes/graphql-doctrine.types.ts +++ b/projects/natural/src/lib/modules/search/classes/graphql-doctrine.types.ts @@ -2,11 +2,11 @@ import {Literal} from '../../../types/types'; -export interface Filter { - groups?: Array | null; -} +export type Filter = { + groups?: FilterGroup[] | null; +}; -export interface FilterGroup { +export type FilterGroup = { // The logic operator to be used to append this group groupLogic?: LogicalOperator | null; // The logic operator to be used within all conditions in this group @@ -14,20 +14,18 @@ export interface FilterGroup { // Optional joins to either filter the query or fetch related objects from DB in a single query joins?: FilterGroupJoin | null; // Conditions to be applied on fields - conditions?: Array | null; -} + conditions?: FilterGroupCondition[] | null; +}; -export interface FilterGroupJoin { - [key: string]: JoinOn; -} +export type FilterGroupJoin = Record; -export interface JoinOn { +export type JoinOn = { type?: JoinType | null; // Optional joins to either filter the query or fetch related objects from DB in a single query joins?: FilterGroupJoin | null; // Conditions to be applied on fields - conditions?: Array | null; -} + conditions?: FilterGroupCondition[] | null; +}; // Logical operator to be used in conditions export enum LogicalOperator { @@ -41,11 +39,9 @@ export enum JoinType { leftJoin = 'leftJoin', } -export interface FilterGroupCondition { - [key: string]: FilterGroupConditionField; -} +export type FilterGroupCondition = Record; -export interface FilterGroupConditionField { +export type FilterGroupConditionField = { between?: BetweenOperator | null; equal?: EqualOperator | null; greater?: GreaterOperator | null; @@ -62,60 +58,60 @@ export interface FilterGroupConditionField { // Allow anything else for custom operators [key: string]: Literal | undefined | null; -} +}; export type Scalar = number | string | boolean; -export interface HaveOperator { - values: Array; +export type HaveOperator = { + values: string[]; not?: boolean | null; -} +}; -export interface EmptyOperator { +export type EmptyOperator = { not?: boolean | null; -} +}; -export interface BetweenOperator { +export type BetweenOperator = { from: Scalar; to: Scalar; not?: boolean | null; -} +}; -export interface EqualOperator { +export type EqualOperator = { value: Scalar; not?: boolean | null; -} +}; -export interface GreaterOperator { +export type GreaterOperator = { value: Scalar; not?: boolean | null; -} +}; -export interface GreaterOrEqualOperator { +export type GreaterOrEqualOperator = { value: Scalar; not?: boolean | null; -} +}; -export interface InOperator { - values: Array; +export type InOperator = { + values: Scalar[]; not?: boolean | null; -} +}; -export interface LessOperator { +export type LessOperator = { value: Scalar; not?: boolean | null; -} +}; -export interface LessOrEqualOperator { +export type LessOrEqualOperator = { value: Scalar; not?: boolean | null; -} +}; -export interface LikeOperator { +export type LikeOperator = { value: Scalar; not?: boolean | null; -} +}; -export interface NullOperator { +export type NullOperator = { not?: boolean | null; -} +}; diff --git a/projects/natural/src/lib/modules/search/classes/url.ts b/projects/natural/src/lib/modules/search/classes/url.ts index 8889d87c..5de5fb15 100644 --- a/projects/natural/src/lib/modules/search/classes/url.ts +++ b/projects/natural/src/lib/modules/search/classes/url.ts @@ -9,15 +9,15 @@ import {Params} from '@angular/router'; * The string can be parsed back with `fromUrl()` */ export function toUrl(selections: NaturalSearchSelections | null): string | null { - if (!selections || !selections.length) { + if (!selections?.length) { return null; } const s = deepClone(selections); for (const a of s) { for (const b of a) { - (b as Literal)['f'] = b.field; - (b as Literal)['c'] = b.condition; + (b as Literal).f = b.field; + (b as Literal).c = b.condition; delete (b as Literal).field; delete (b as Literal).condition; @@ -33,7 +33,7 @@ export function toUrl(selections: NaturalSearchSelections | null): string | null * Parse a string, probably coming from URL, into a selection */ export function fromUrl(selections: string | null): NaturalSearchSelections { - if (!selections || !selections.length) { + if (!selections?.length) { return [[]]; } @@ -41,11 +41,11 @@ export function fromUrl(selections: string | null): NaturalSearchSelections { for (const a of result) { for (const b of a) { - b.field = (b as Literal)['f']; - b.condition = (b as Literal)['c']; + b.field = (b as Literal).f; + b.condition = (b as Literal).c; - delete (b as Literal)['f']; - delete (b as Literal)['c']; + delete (b as Literal).f; + delete (b as Literal).c; } } diff --git a/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.html b/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.html index 8eb0ba64..9c8d0a80 100644 --- a/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.html +++ b/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.html @@ -6,7 +6,7 @@ tabindex="-1" >
- +
@if (data.showValidateButton) { diff --git a/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.ts b/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.ts index d5c9491f..9f7f6bf3 100644 --- a/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.ts +++ b/projects/natural/src/lib/modules/search/dropdown-container/dropdown-container.component.ts @@ -21,9 +21,9 @@ export function throwMatDialogContentAlreadyAttachedError(): void { throw Error('Attempting to attach dialog content after content is already attached'); } -export interface NaturalDropdownContainerData { +export type NaturalDropdownContainerData = { showValidateButton: boolean; -} +}; export const NATURAL_DROPDOWN_CONTAINER_DATA = new InjectionToken( 'NaturalDropdownContainerData', @@ -32,6 +32,7 @@ export const NATURAL_DROPDOWN_CONTAINER_DATA = new InjectionToken { +export type NaturalDropdownData = { condition: FilterGroupConditionField | null; configuration: C; title?: string; -} +}; export const NATURAL_DROPDOWN_DATA = new InjectionToken('NaturalDropdownData'); diff --git a/projects/natural/src/lib/modules/search/facet-selector/facet-selector.component.ts b/projects/natural/src/lib/modules/search/facet-selector/facet-selector.component.ts index 8cfb06ce..e94a66df 100644 --- a/projects/natural/src/lib/modules/search/facet-selector/facet-selector.component.ts +++ b/projects/natural/src/lib/modules/search/facet-selector/facet-selector.component.ts @@ -10,9 +10,9 @@ import {MatListModule} from '@angular/material/list'; /** * Configuration for facet selection */ -export interface FacetSelectorConfiguration { +export type FacetSelectorConfiguration = { facets: NaturalSearchFacets; -} +}; @Component({ templateUrl: './facet-selector.component.html', diff --git a/projects/natural/src/lib/modules/search/group/group.component.html b/projects/natural/src/lib/modules/search/group/group.component.html index ae3c6145..98aa1018 100644 --- a/projects/natural/src/lib/modules/search/group/group.component.html +++ b/projects/natural/src/lib/modules/search/group/group.component.html @@ -4,7 +4,7 @@ (selectionChange)="updateInput($event, $index)" [facets]="facets" [selection]="selection" - > + /> } +/> diff --git a/projects/natural/src/lib/modules/search/input/input.component.html b/projects/natural/src/lib/modules/search/input/input.component.html index 632af602..687ba389 100644 --- a/projects/natural/src/lib/modules/search/input/input.component.html +++ b/projects/natural/src/lib/modules/search/input/input.component.html @@ -21,18 +21,18 @@ /> @if (!facet && !selection) { - + } @if (selection) { } @if (facet && !selection) { } diff --git a/projects/natural/src/lib/modules/search/input/input.component.ts b/projects/natural/src/lib/modules/search/input/input.component.ts index 1d096872..92aed66c 100644 --- a/projects/natural/src/lib/modules/search/input/input.component.ts +++ b/projects/natural/src/lib/modules/search/input/input.component.ts @@ -15,7 +15,7 @@ import { StaticProvider, ViewChild, } from '@angular/core'; -import {FormControl, ValidationErrors, ValidatorFn, FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {FormControl, FormsModule, ReactiveFormsModule, ValidationErrors, ValidatorFn} from '@angular/forms'; import {ErrorStateMatcher, MatRipple, MatRippleModule} from '@angular/material/core'; import {FilterGroupConditionField} from '../classes/graphql-doctrine.types'; import {getFacetFromSelection} from '../classes/utils'; @@ -176,6 +176,34 @@ export class NaturalInputComponent implements OnInit, OnChanges, OnDestroy { private readonly injector: EnvironmentInjector, ) {} + public ngOnChanges(): void { + if (!this.facets && this.selection) { + setTimeout(() => this.clear()); + } else if (this.facets && this.selection) { + this.facet = getFacetFromSelection(this.facets, this.selection); + + if (this.isDropdown()) { + const dropdownComponent = this.createComponent(this.facet as DropdownFacet); + this.formCtrl.setValidators([isComponentValid(dropdownComponent)]); + dropdownComponent.renderedValue.subscribe(value => { + this.formCtrl.setValue(value); + }); + } else if (this.isFlag()) { + this.formCtrl.setValue(''); + } else if ( + this.selection && + this.selection.field === this.searchFieldName && + this.selection.condition.like + ) { + // global search mode + this.formCtrl.setValue('' + this.selection.condition.like.value); + } else { + // If component is invalid (no facet and not a global search), clear from result and destroy component + setTimeout(() => this.clear()); + } + } + } + public ngOnInit(): void { this.input.nativeElement.addEventListener('focus', () => { this.openDropdown(); @@ -205,34 +233,6 @@ export class NaturalInputComponent implements OnInit, OnChanges, OnDestroy { } } - public ngOnChanges(): void { - if (!this.facets && this.selection) { - setTimeout(() => this.clear()); - } else if (this.facets && this.selection) { - this.facet = getFacetFromSelection(this.facets, this.selection); - - if (this.isDropdown()) { - const dropdownComponent = this.createComponent(this.facet as DropdownFacet); - this.formCtrl.setValidators([isComponentValid(dropdownComponent)]); - dropdownComponent.renderedValue.subscribe(value => { - this.formCtrl.setValue(value); - }); - } else if (this.isFlag()) { - this.formCtrl.setValue(''); - } else if ( - this.selection && - this.selection.field === this.searchFieldName && - this.selection.condition.like - ) { - // global search mode - this.formCtrl.setValue('' + this.selection.condition.like.value); - } else { - // If component is invalid (no facet and not a global search), clear from result and destroy component - setTimeout(() => this.clear()); - } - } - } - public search(event: Event): void { event.stopPropagation(); event.preventDefault(); @@ -291,7 +291,7 @@ export class NaturalInputComponent implements OnInit, OnChanges, OnDestroy { this.dropdownComponentRef.destroy(); } - const condition = this.selection ? (this.selection.condition as FilterGroupConditionField) : null; + const condition = this.selection ? this.selection.condition : null; const data: NaturalDropdownData = { condition: condition, configuration: facet.configuration, @@ -410,7 +410,7 @@ export class NaturalInputComponent implements OnInit, OnChanges, OnDestroy { condition: condition, }; - if (this.facet && this.facet.name) { + if (this.facet?.name) { selection.name = this.facet.name; } diff --git a/projects/natural/src/lib/modules/search/search/search.component.html b/projects/natural/src/lib/modules/search/search/search.component.html index 1dcca24e..2133cbbf 100644 --- a/projects/natural/src/lib/modules/search/search/search.component.html +++ b/projects/natural/src/lib/modules/search/search/search.component.html @@ -8,7 +8,7 @@ [placeholder]="placeholder" [selections]="groupSelections" [dropdownTitle]="dropdownTitle" - > + />
@if (innerSelections.length > 1) { }
@if (!$last) { - + } } @@ -31,7 +31,7 @@
@if (multipleGroups) { } @@ -42,7 +42,7 @@ i18n-matTooltip matTooltip="Annuler la recherche" > - +
diff --git a/projects/natural/src/lib/modules/search/search/search.component.ts b/projects/natural/src/lib/modules/search/search/search.component.ts index da119871..a4f13cab 100644 --- a/projects/natural/src/lib/modules/search/search/search.component.ts +++ b/projects/natural/src/lib/modules/search/search/search.component.ts @@ -63,7 +63,7 @@ export class NaturalSearchComponent implements OnChanges { */ @Input() public set selections(selections: NaturalSearchSelections) { - this.innerSelections = selections && selections[0] ? deepClone(selections) : [[]]; + this.innerSelections = selections?.[0] ? deepClone(selections) : [[]]; } public readonly isMobile = this.breakpointObserver.observe(Breakpoints.XSmall).pipe(map(result => result.matches)); diff --git a/projects/natural/src/lib/modules/search/types/dropdown-component.ts b/projects/natural/src/lib/modules/search/types/dropdown-component.ts index f6d7cab6..d1d14b89 100644 --- a/projects/natural/src/lib/modules/search/types/dropdown-component.ts +++ b/projects/natural/src/lib/modules/search/types/dropdown-component.ts @@ -1,7 +1,7 @@ import {BehaviorSubject} from 'rxjs'; import {FilterGroupConditionField} from '../classes/graphql-doctrine.types'; -export interface DropdownComponent { +export type DropdownComponent = { /** * Observable of current value as string */ @@ -21,4 +21,4 @@ export interface DropdownComponent { * Returns true if the dropdown value has change */ isDirty(): boolean; -} +}; diff --git a/projects/natural/src/lib/modules/search/types/facet.ts b/projects/natural/src/lib/modules/search/types/facet.ts index 253600ad..9e062179 100644 --- a/projects/natural/src/lib/modules/search/types/facet.ts +++ b/projects/natural/src/lib/modules/search/types/facet.ts @@ -2,7 +2,7 @@ import {Type} from '@angular/core'; import {DropdownComponent} from './dropdown-component'; import {NaturalSearchSelection} from './values'; -interface BasicFacet { +type BasicFacet = { /** * The label to be used in the GUI */ @@ -37,12 +37,12 @@ interface BasicFacet { * friendly values, but the API works with a "low-level" unit. */ transform?: (s: NaturalSearchSelection) => NaturalSearchSelection; -} +}; /** * Facet that is only a flag (set or unset) */ -export interface FlagFacet extends BasicFacet { +export type FlagFacet = { /** * The value to be returned when the flag is set */ @@ -55,12 +55,12 @@ export interface FlagFacet extends BasicFacet { * Defaults to `false`. */ inversed?: boolean; -} +} & BasicFacet; /** * Facet that uses a component in a dropdown */ -export interface DropdownFacet extends BasicFacet { +export type DropdownFacet = { component: Type; /** @@ -72,7 +72,7 @@ export interface DropdownFacet extends BasicFacet { * Anything that could be useful for the dropdown component */ configuration?: C; -} +} & BasicFacet; /** * A facet @@ -82,4 +82,4 @@ export type Facet = DropdownFacet | FlagFacet; /** * Exhaustive list of facets */ -export type NaturalSearchFacets = Array; +export type NaturalSearchFacets = Facet[]; diff --git a/projects/natural/src/lib/modules/search/types/values.ts b/projects/natural/src/lib/modules/search/types/values.ts index b46b698d..cabf0274 100644 --- a/projects/natural/src/lib/modules/search/types/values.ts +++ b/projects/natural/src/lib/modules/search/types/values.ts @@ -4,7 +4,7 @@ import {Facet} from './facet'; /** * Type for a search selection */ -export interface NaturalSearchSelection { +export type NaturalSearchSelection = { field: string; /** @@ -14,24 +14,24 @@ export interface NaturalSearchSelection { */ name?: string; condition: FilterGroupConditionField; -} +}; /** * Groups are a list of values, that should be interpreted with AND condition */ -export type GroupSelections = Array; +export type GroupSelections = NaturalSearchSelection[]; /** * List of groups, that should be interpreted with OR condition * Final input / output format */ -export type NaturalSearchSelections = Array; +export type NaturalSearchSelections = GroupSelections[]; /** * Consolidated type for a selection and it's matching facet * Used internally for dropdown */ -export interface DropdownResult { +export type DropdownResult = { condition: FilterGroupConditionField; facet?: Facet; -} +}; diff --git a/projects/natural/src/lib/modules/select/select-enum/select-enum.component.spec.ts b/projects/natural/src/lib/modules/select/select-enum/select-enum.component.spec.ts index 2b4e5ab8..10315e4a 100644 --- a/projects/natural/src/lib/modules/select/select-enum/select-enum.component.spec.ts +++ b/projects/natural/src/lib/modules/select/select-enum/select-enum.component.spec.ts @@ -23,7 +23,8 @@ import { (blur)="onBlur()" [(ngModel)]="myValue" placeholder="ngModel" - > + i18n-placeholder + /> `, standalone: true, imports: [FormsModule, NaturalSelectEnumComponent], @@ -38,7 +39,8 @@ class TestHostWithNgModelComponent extends AbstractTestHostWithNgModelComponent (blur)="onBlur()" [formControl]="formControl" placeholder="formControl" - > + i18n-placeholder + /> `, standalone: true, imports: [ReactiveFormsModule, NaturalSelectEnumComponent], diff --git a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.html b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.html index 8fdb63be..a36b1814 100644 --- a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.html +++ b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.html @@ -14,7 +14,7 @@ @if (showIcon) { - + } @if (hint) { @@ -30,7 +30,7 @@ i18n-matTooltip matTooltip="Désélectionner" > - + } @if (internalCtrl.value && navigateTo) { @@ -41,7 +41,7 @@ i18n-matTooltip matTooltip="Naviguer vers" > - + } diff --git a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.spec.ts b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.spec.ts index 3daaf91f..0d2b6cee 100644 --- a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.spec.ts +++ b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.spec.ts @@ -33,7 +33,8 @@ import {ItemService} from '../../../testing/item.service'; (blur)="onBlur()" [(ngModel)]="myValue" placeholder="ngModel" - > + i18n-placeholder + /> `, standalone: true, imports: [FormsModule, NaturalSelectHierarchicComponent], @@ -48,7 +49,8 @@ class TestHostWithHierarchicAndNgModelComponent extends AbstractTestHostWithNgMo (blur)="onBlur()" [formControl]="formControl" placeholder="formControl" - > + i18n-placeholder + /> `, standalone: true, imports: [ReactiveFormsModule, NaturalSelectHierarchicComponent], diff --git a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts index a4ae6788..2ea7f885 100644 --- a/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts +++ b/projects/natural/src/lib/modules/select/select-hierarchic/select-hierarchic.component.ts @@ -152,7 +152,7 @@ export class NaturalSelectHierarchicComponent .afterClosed() .subscribe(result => { this.lockOpenDialog = false; - if (result && result.hierarchicSelection) { + if (result?.hierarchicSelection) { const selection = result.hierarchicSelection; // Find the only selection amongst all possible keys const keyWithSelection = Object.keys(selection).find(key => selection[key][0]); diff --git a/projects/natural/src/lib/modules/select/select/select.component.html b/projects/natural/src/lib/modules/select/select/select.component.html index e7ba0012..7619737d 100644 --- a/projects/natural/src/lib/modules/select/select/select.component.html +++ b/projects/natural/src/lib/modules/select/select/select.component.html @@ -10,7 +10,7 @@ + /> } @if (moreNbItems > 0) { @@ -47,12 +47,12 @@ @if (!loading && showIcon) { - + } @if (loading) {
- +
} @@ -60,12 +60,12 @@
@if (internalCtrl.pristine && internalCtrl.value && internalCtrl.enabled && !clearLabel) { } @if (internalCtrl.dirty && internalCtrl.enabled && optionRequired) { } @if (internalCtrl.pristine && internalCtrl.value && navigateTo) { @@ -76,7 +76,7 @@ i18n-matTooltip matTooltip="Naviguer vers" > - + }
diff --git a/projects/natural/src/lib/modules/select/select/select.component.spec.ts b/projects/natural/src/lib/modules/select/select/select.component.spec.ts index cda563d1..506d0311 100644 --- a/projects/natural/src/lib/modules/select/select/select.component.spec.ts +++ b/projects/natural/src/lib/modules/select/select/select.component.spec.ts @@ -23,7 +23,8 @@ import {ItemService} from '../../../testing/item.service'; (blur)="onBlur()" [(ngModel)]="myValue" placeholder="ngModel" - > + i18n-placeholder + /> `, standalone: true, imports: [FormsModule, NaturalSelectComponent], @@ -38,7 +39,8 @@ class TestHostWithServiceAndNgModelComponent extends AbstractTestHostWithNgModel (blur)="onBlur()" [formControl]="formControl" placeholder="formControl" - > + i18n-placeholder + /> `, standalone: true, imports: [ReactiveFormsModule, NaturalSelectComponent], diff --git a/projects/natural/src/lib/modules/select/select/select.component.ts b/projects/natural/src/lib/modules/select/select/select.component.ts index dcfc37f1..e6c70394 100644 --- a/projects/natural/src/lib/modules/select/select/select.component.ts +++ b/projects/natural/src/lib/modules/select/select/select.component.ts @@ -1,6 +1,6 @@ import {AfterViewInit, Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {ControlValueAccessor, FormsModule, ReactiveFormsModule} from '@angular/forms'; -import {MatAutocompleteTrigger, MatAutocompleteModule} from '@angular/material/autocomplete'; +import {MatAutocompleteModule, MatAutocompleteTrigger} from '@angular/material/autocomplete'; import {merge} from 'lodash-es'; import {Observable} from 'rxjs'; import {debounceTime, distinctUntilChanged, finalize, map, takeUntil} from 'rxjs/operators'; @@ -108,11 +108,13 @@ export class NaturalSelectComponent< /** * The field on which to search for, default to 'custom'. */ + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents @Input() public searchField: 'custom' | string = 'custom'; /** * The operator with which to search for, default to 'search' if `searchField` is 'custom', else 'like'. */ + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents @Input() public searchOperator: 'search' | string | null = null; /** @@ -163,6 +165,11 @@ export class NaturalSelectComponent< disabled ? this.internalCtrl.disable() : this.internalCtrl.enable(); } + public override ngOnInit(): void { + super.ngOnInit(); + this.initService(); + } + public ngAfterViewInit(): void { this.internalCtrl.valueChanges .pipe(takeUntil(this.ngUnsubscribe), distinctUntilChanged(), debounceTime(300)) @@ -176,11 +183,6 @@ export class NaturalSelectComponent< } } - public override ngOnInit(): void { - super.ngOnInit(); - this.initService(); - } - public override onBlur(): void { if (this.internalCtrl.dirty) { this.reset(); diff --git a/projects/natural/src/lib/modules/sidenav/sidenav-container/sidenav-container.component.html b/projects/natural/src/lib/modules/sidenav/sidenav-container/sidenav-container.component.html index 4da8966d..6236666c 100644 --- a/projects/natural/src/lib/modules/sidenav/sidenav-container/sidenav-container.component.html +++ b/projects/natural/src/lib/modules/sidenav/sidenav-container/sidenav-container.component.html @@ -7,12 +7,12 @@ [style.width.px]="sidenavService.isMinimized && minimizedWidth ? minimizedWidth : null" [position]="position" > - +
- +
diff --git a/projects/natural/src/lib/modules/sidenav/sidenav-content/sidenav-content.component.ts b/projects/natural/src/lib/modules/sidenav/sidenav-content/sidenav-content.component.ts index b3c70b99..b35e5e9f 100644 --- a/projects/natural/src/lib/modules/sidenav/sidenav-content/sidenav-content.component.ts +++ b/projects/natural/src/lib/modules/sidenav/sidenav-content/sidenav-content.component.ts @@ -2,7 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'natural-sidenav-content', - template: '', + template: '', styleUrls: ['./sidenav-content.component.scss'], standalone: true, }) diff --git a/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.html b/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.html index 6dbc7430..40b37264 100644 --- a/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.html +++ b/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.html @@ -1 +1 @@ - + diff --git a/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.ts b/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.ts index ff8c0dfb..800cd8f7 100644 --- a/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.ts +++ b/projects/natural/src/lib/modules/sidenav/sidenav/sidenav.component.ts @@ -2,7 +2,7 @@ import {Component} from '@angular/core'; @Component({ selector: 'natural-sidenav', - template: '', + template: '', standalone: true, }) export class NaturalSidenavComponent {} diff --git a/projects/natural/src/lib/modules/table-button/table-button.component.html b/projects/natural/src/lib/modules/table-button/table-button.component.html index 5e456370..c90f29ee 100644 --- a/projects/natural/src/lib/modules/table-button/table-button.component.html +++ b/projects/natural/src/lib/modules/table-button/table-button.component.html @@ -4,7 +4,7 @@ @if (type === 'none') { @if (icon) { - + } @if (label) { {{ label }} @@ -26,7 +26,7 @@ mat-button > @if (icon) { - + } {{ label }} @@ -44,7 +44,7 @@ mat-icon-button > @if (icon) { - + } } @@ -52,7 +52,7 @@ @if (type === 'click' && label) { @if (icon) { - + } {{ label }} @@ -61,7 +61,7 @@ @if (type === 'click' && !label) { @if (icon) { - + } } @@ -69,7 +69,7 @@ @if (type === 'href' && label) { @if (icon) { - + } {{ label }} @@ -78,7 +78,7 @@ @if (type === 'href' && !label) { @if (icon) { - + } } @@ -98,7 +98,7 @@ mat-raised-button > @if (icon) { - + } {{ label }} @@ -117,7 +117,7 @@ class="mat-elevation-z4" > @if (icon) { - + } } @@ -125,7 +125,7 @@ @if (type === 'click' && label) { @if (icon) { - + } {{ label }} @@ -140,7 +140,7 @@ class="mat-elevation-z4" > @if (icon) { - + } } @@ -148,7 +148,7 @@ @if (type === 'href' && label) { @if (icon) { - + } {{ label }} @@ -164,7 +164,7 @@ target="_blank" > @if (icon) { - + } } diff --git a/projects/natural/src/lib/modules/table-button/table-button.component.ts b/projects/natural/src/lib/modules/table-button/table-button.component.ts index 09926e2c..12865e30 100644 --- a/projects/natural/src/lib/modules/table-button/table-button.component.ts +++ b/projects/natural/src/lib/modules/table-button/table-button.component.ts @@ -19,6 +19,7 @@ import {MatIconModule} from '@angular/material/icon'; selector: 'natural-table-button', templateUrl: './table-button.component.html', styleUrls: ['./table-button.component.scss'], + // eslint-disable-next-line @angular-eslint/use-component-view-encapsulation encapsulation: ViewEncapsulation.None, standalone: true, imports: [MatIconModule, NaturalIconDirective, MatButtonModule, RouterLink], diff --git a/projects/natural/src/lib/services/abstract-model.service.ts b/projects/natural/src/lib/services/abstract-model.service.ts index dea3d0a0..9de4c524 100644 --- a/projects/natural/src/lib/services/abstract-model.service.ts +++ b/projects/natural/src/lib/services/abstract-model.service.ts @@ -11,25 +11,19 @@ import {makePlural, mergeOverrideArray, relationsToIds, upperCaseFirstLetter} fr import {PaginatedData} from '../classes/data-source'; import {NaturalDebounceService} from './debounce.service'; -export interface FormValidators { - [key: string]: ValidatorFn[]; -} +export type FormValidators = Record; -export interface FormAsyncValidators { - [key: string]: AsyncValidatorFn[]; -} +export type FormAsyncValidators = Record; -export interface VariablesWithInput { +export type VariablesWithInput = { input: Literal; -} +}; -export interface FormControls { - [key: string]: AbstractControl; -} +export type FormControls = Record; -interface Resolve { +type Resolve = { model: TOne; -} +}; export type WithId = {id: string} & T; @@ -101,7 +95,7 @@ export abstract class NaturalAbstractModelService< const disabled = model.permissions ? !model.permissions.update : false; if (model.id) { - controls['id'] = new UntypedFormControl({value: model.id, disabled: true}); + controls.id = new UntypedFormControl({value: model.id, disabled: true}); } // Configure form for each field of model diff --git a/projects/natural/src/lib/services/enum.service.ts b/projects/natural/src/lib/services/enum.service.ts index 6dda4f5b..0a67f54e 100644 --- a/projects/natural/src/lib/services/enum.service.ts +++ b/projects/natural/src/lib/services/enum.service.ts @@ -16,20 +16,22 @@ const enumTypeQuery = gql` } `; -interface EnumType { +type EnumType = { __type: null | { __typename: string; - enumValues: null | Array<{ - name: string; - description: null | string; - }>; + enumValues: + | null + | { + name: string; + description: null | string; + }[]; }; -} +}; -export interface IEnum { +export type IEnum = { value: string; name: string; -} +}; @Injectable({ providedIn: 'root', @@ -51,7 +53,7 @@ export class NaturalEnumService { .pipe( map(result => { const values: IEnum[] = []; - if (result.data.__type && result.data.__type.enumValues) { + if (result.data.__type?.enumValues) { for (const enumValue of result.data.__type.enumValues) { values.push({ value: enumValue.name, diff --git a/projects/natural/src/lib/services/link-mutation.service.ts b/projects/natural/src/lib/services/link-mutation.service.ts index 4c777cc8..8efd0961 100644 --- a/projects/natural/src/lib/services/link-mutation.service.ts +++ b/projects/natural/src/lib/services/link-mutation.service.ts @@ -38,30 +38,32 @@ type IntrospectedArg = { }; }; -interface IntrospectedMutations { +type IntrospectedMutations = { __type: null | { - fields: null | Array<{ - name: string; - args: Array; - }>; + fields: + | null + | { + name: string; + args: IntrospectedArg[]; + }[]; }; -} +}; -export interface LinkableObject { +export type LinkableObject = { id: string; __typename: string; -} +}; -interface MutationArg { +type MutationArg = { name: string; type: string; -} +}; -interface Mutation { +type Mutation = { name: string; arg1: MutationArg; arg2: MutationArg; -} +}; @Injectable({ providedIn: 'root', @@ -136,7 +138,7 @@ export class NaturalLinkMutationService { }) .pipe( map(({data}) => { - if (data.__type && data.__type.fields) { + if (data.__type?.fields) { this.allMutations = data.__type.fields .filter(v => v.name.match(/^(link|unlink)/)) .map(v => { diff --git a/projects/natural/src/lib/services/persistence.service.ts b/projects/natural/src/lib/services/persistence.service.ts index 94de0915..2eb2dbf8 100644 --- a/projects/natural/src/lib/services/persistence.service.ts +++ b/projects/natural/src/lib/services/persistence.service.ts @@ -50,6 +50,7 @@ export class NaturalPersistenceService { * - When loading with url parameters, storage is updated to stay synced * - When loading without url, but with storage data, the url is updated */ + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents public get(key: string, route: ActivatedRoute, storageKey: string): any | null { // From url let params = this.getFromUrl(key, route); @@ -71,6 +72,7 @@ export class NaturalPersistenceService { /** * Get given key from the url parameters */ + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents public getFromUrl(key: string, route: ActivatedRoute): any | null { const value = route.snapshot.paramMap.get(key); @@ -101,6 +103,7 @@ export class NaturalPersistenceService { return this.router.navigate(['.', params], navigationExtras); } + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents public getFromStorage(key: string, storageKey: string): any | null { const value = this.sessionStorage.getItem(this.getStorageKey(key, storageKey)); @@ -130,6 +133,7 @@ export class NaturalPersistenceService { return value == null || value === ''; // == means null or undefined; } + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents private deserialize(key: string, storageKey: string | null, value: string | null): unknown | null { if (!value) { return null; diff --git a/projects/natural/src/lib/testing/item.service.ts b/projects/natural/src/lib/testing/item.service.ts index 18aa6020..46e2454b 100644 --- a/projects/natural/src/lib/testing/item.service.ts +++ b/projects/natural/src/lib/testing/item.service.ts @@ -11,14 +11,14 @@ import {deepFreeze, Literal} from '@ecodev/natural'; import {deepClone} from '../modules/search/classes/utils'; import {NaturalDebounceService} from '../services/debounce.service'; -export interface Item { +export type Item = { readonly __typename: 'Item'; readonly id: string; readonly name: string; readonly description: string; readonly children: readonly Item[]; readonly parent: Item | null; -} +}; export type ItemInput = Omit; diff --git a/projects/natural/src/lib/testing/mock-apollo.provider.ts b/projects/natural/src/lib/testing/mock-apollo.provider.ts index 3f9b4cca..4b6231d6 100644 --- a/projects/natural/src/lib/testing/mock-apollo.provider.ts +++ b/projects/natural/src/lib/testing/mock-apollo.provider.ts @@ -5,22 +5,22 @@ import {Injectable, NgZone, Provider} from '@angular/core'; import {buildSchema} from 'graphql'; import {addMocksToSchema, IMocks} from '@graphql-tools/mock'; -export interface Blog { +export type Blog = { id: string; -} +}; -export interface Post { +export type Post = { id: string; slug: string; blog: Blog | null; creationDate: string; updateDate: string | null; -} +}; -export interface PostInput { +export type PostInput = { slug: string; blog: string; -} +}; export const postsQuery = gql` query Posts($filter: PostFilter, $sorting: [String!], $pagination: PaginationInput) { diff --git a/projects/natural/src/lib/types/types.ts b/projects/natural/src/lib/types/types.ts index c200e8d6..410c3eef 100644 --- a/projects/natural/src/lib/types/types.ts +++ b/projects/natural/src/lib/types/types.ts @@ -6,9 +6,7 @@ import {ObservedValueOf} from 'rxjs'; /** * An object literal with any keys and values */ -export interface Literal { - [key: string]: any; -} +export type Literal = Record; /** * An object with either a name or a fullName (or maybe both) diff --git a/src/app/app.component.html b/src/app/app.component.html index c2188226..cac48c8b 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,3 +1,3 @@
- +
diff --git a/src/app/avatar/avatar.component.html b/src/app/avatar/avatar.component.html index 2a795245..d94aba8d 100644 --- a/src/app/avatar/avatar.component.html +++ b/src/app/avatar/avatar.component.html @@ -4,60 +4,60 @@

natural-avatar

Avatar from different sources

- - - - + + + +

Avatar with fallback

- + - + - +

Ignore special chars

- - - + + +

Colors

- - - - - - - - - - - + + + + + + + + + + +

Misc

- - - - + + + + - - - - + + + +

Failed sources are not retried

@@ -69,7 +69,7 @@

Failed sources are not retried

@for (i of failedSources; track i) { - + }
diff --git a/src/app/avatar/user.model.ts b/src/app/avatar/user.model.ts index 9cc99f45..16bfa48e 100644 --- a/src/app/avatar/user.model.ts +++ b/src/app/avatar/user.model.ts @@ -1,7 +1,7 @@ /** * Represents the User contract */ -export interface User { +export type User = { username: string; facebookId: string; -} +}; diff --git a/src/app/demo.error-handler.ts b/src/app/demo.error-handler.ts index 1d4da949..6c07beb1 100644 --- a/src/app/demo.error-handler.ts +++ b/src/app/demo.error-handler.ts @@ -3,7 +3,7 @@ import {MatSnackBar} from '@angular/material/snack-bar'; import {Observable, of} from 'rxjs'; import {NaturalLoggerExtra, NaturalLoggerType} from '@ecodev/natural'; -@Injectable() +@Injectable({providedIn: 'root'}) export class DemoLoggerExtra implements NaturalLoggerExtra { public constructor(private readonly snackBar: MatSnackBar) {} diff --git a/src/app/detail-header/detail-header.component.html b/src/app/detail-header/detail-header.component.html index c4b41ce1..a6868ad8 100644 --- a/src/app/detail-header/detail-header.component.html +++ b/src/app/detail-header/detail-header.component.html @@ -21,17 +21,12 @@

NaturalDetailHeaderComponent

Simple

- +

With icon

- +
@@ -54,6 +49,6 @@

All

Minimal

- +
diff --git a/src/app/detail/detail.component.html b/src/app/detail/detail.component.html index f1a37143..fb144612 100644 --- a/src/app/detail/detail.component.html +++ b/src/app/detail/detail.component.html @@ -43,7 +43,7 @@

NaturalAbstractDetail

[model]="data.model" (create)="create()" (delete)="delete()" - > + /> } @@ -75,5 +75,5 @@

Unlinkable tabs (set to false)

- + diff --git a/src/app/editable-list/editable-list.component.html b/src/app/editable-list/editable-list.component.html index e2e368e7..0a5dbf3f 100644 --- a/src/app/editable-list/editable-list.component.html +++ b/src/app/editable-list/editable-list.component.html @@ -45,7 +45,7 @@

NaturalAbstractEditableList

@@ -57,7 +57,7 @@

NaturalAbstractEditableList

As a `for` loop
@for (element of dataSource.data; track element) {
-
+
Name @@ -77,9 +77,7 @@

NaturalAbstractEditableList

@if (formArray.at($index).get('description')?.hasError('maxlength')) { Maximum - {{ - formArray.at($index).get('description')?.errors?.maxlength.requiredLength - }} + {{ formArray.at($index).get('description')?.errors?.maxlength.requiredLength }} caractères } diff --git a/src/app/editor/editor.component.html b/src/app/editor/editor.component.html index dedb5b55..171e521b 100644 --- a/src/app/editor/editor.component.html +++ b/src/app/editor/editor.component.html @@ -3,7 +3,7 @@
- +

Preview

@@ -23,7 +23,7 @@

Debug

(save)="update()" [naturalCustomCss]="css" [disabled]="disabled" - > + />

Preview

diff --git a/src/app/file/file.component.html b/src/app/file/file.component.html index 0af3d387..dcccd6b6 100644 --- a/src/app/file/file.component.html +++ b/src/app/file/file.component.html @@ -223,6 +223,6 @@

File component

[model]="model" [uploader]="uploadAndLink.bind(this)" action="upload" - > + />
diff --git a/src/app/file/file.component.ts b/src/app/file/file.component.ts index 65887ce6..f3e818e6 100644 --- a/src/app/file/file.component.ts +++ b/src/app/file/file.component.ts @@ -12,20 +12,20 @@ import {FormsModule} from '@angular/forms'; import {MatCheckboxModule} from '@angular/material/checkbox'; import {FlexModule} from '@ngbracket/ngx-layout/flex'; -interface JsonFile { +type JsonFile = { name: string; type: string; size: number; lastModified: number; -} +}; -interface JsonFileSelection { +type JsonFileSelection = { valid: JsonFile[]; invalid: { error: string; file: JsonFile; }[]; -} +}; function fileToJson(file: File): JsonFile { return { diff --git a/src/app/hierarchic/hierarchic.component.html b/src/app/hierarchic/hierarchic.component.html index 7aa9e0c5..bab08461 100644 --- a/src/app/hierarchic/hierarchic.component.html +++ b/src/app/hierarchic/hierarchic.component.html @@ -11,7 +11,7 @@

natural-search

[selected]="selected" [searchFacets]="searchFacets" [searchSelections]="searchSelections" - > + />
diff --git a/src/app/home/home.component.html b/src/app/home/home.component.html index 0f41d86d..aa6089ec 100644 --- a/src/app/home/home.component.html +++ b/src/app/home/home.component.html @@ -2,7 +2,7 @@ @if (menu) { } @@ -14,7 +14,7 @@
@@ -31,17 +31,17 @@ - + Home - + Search - + List A @@ -49,82 +49,82 @@ [routerLink]="['/nested/anyValue', {cat: 'cat'}, 'list', {dog: 'dog'}]" mat-list-item > - + List B - + Editable list - + Navigable list - + Detail - + Detail header - + Select - + Select hierarchic - + Select Enum - + Relation - + Hiérarchic - + Panels - + File - + Alert service - + Avatar - + Editor - + Other tools @@ -135,7 +135,7 @@ menu.isMinimized ? 'keyboard_arrow_right' : 'keyboard_arrow_left' " matListItemIcon - > + /> } @@ -147,7 +147,7 @@
- +
diff --git a/src/app/homepage/homepage.component.spec.ts b/src/app/homepage/homepage.component.spec.ts index de531dd6..bbe3e406 100644 --- a/src/app/homepage/homepage.component.spec.ts +++ b/src/app/homepage/homepage.component.spec.ts @@ -6,7 +6,7 @@ describe('Demo HomepageComponent', () => { let component: HomepageComponent; let fixture: ComponentFixture; - beforeEach(async () => { + beforeEach(() => { fixture = TestBed.createComponent(HomepageComponent); component = fixture.componentInstance; fixture.detectChanges(); diff --git a/src/app/list/list.component.html b/src/app/list/list.component.html index f9d3e0e0..1b09df29 100644 --- a/src/app/list/list.component.html +++ b/src/app/list/list.component.html @@ -31,14 +31,14 @@

NaturalAbstractList

(change)="$event ? masterToggle() : null" [checked]="selection.hasValue() && isAllSelected()" [indeterminate]="selection.hasValue() && !isAllSelected()" - > + /> + /> @@ -48,11 +48,7 @@

NaturalAbstractList

name - + @@ -78,7 +74,7 @@

NaturalAbstractList

} @if (!dataSource.data) {
- +
} NaturalAbstractList [pageIndex]="dataSource.data?.pageIndex" [pageSizeOptions]="pageSizeOptions" [pageSize]="dataSource.data?.pageSize" - > + />
columnsForTable : {{ columnsForTable | json }}
diff --git a/src/app/list/list.component.spec.ts b/src/app/list/list.component.spec.ts index 7911e475..b16e8612 100644 --- a/src/app/list/list.component.spec.ts +++ b/src/app/list/list.component.spec.ts @@ -13,7 +13,7 @@ import { import {MockApolloProvider} from '../../../projects/natural/src/lib/testing/mock-apollo.provider'; import {ListComponent} from './list.component'; -@Injectable() +@Injectable({providedIn: 'root'}) class MockNaturalPersistenceService extends NaturalPersistenceService { public override persistInUrl(): Promise { // Nullify the redirection, it crashes in testing environment and it's not the point to be tested here @@ -76,7 +76,7 @@ describe('Demo ListComponent', () => { beforeEach(fakeAsync(() => { fixture = TestBed.createComponent(ListComponent); component = fixture.componentInstance; - ngZone = fixture.ngZone as NgZone; + ngZone = fixture.ngZone!; router = TestBed.inject(Router); persistenceService = TestBed.inject(NaturalPersistenceService); diff --git a/src/app/list/list.component.ts b/src/app/list/list.component.ts index bf4b5a20..f8299191 100644 --- a/src/app/list/list.component.ts +++ b/src/app/list/list.component.ts @@ -102,7 +102,7 @@ export class ListComponent extends NaturalAbstractList implements O pageSize: 5, }; - protected override defaultSorting: Array = [{field: 'name', order: SortingOrder.DESC}]; + protected override defaultSorting: Sorting[] = [{field: 'name', order: SortingOrder.DESC}]; public constructor(service: ItemService) { super(service); diff --git a/src/app/navigable-list/navigable-list.component.html b/src/app/navigable-list/navigable-list.component.html index 76ae16b9..e1bf76af 100644 --- a/src/app/navigable-list/navigable-list.component.html +++ b/src/app/navigable-list/navigable-list.component.html @@ -6,7 +6,7 @@

NaturalAbstractNavigableList

@for (item of breadcrumbs; track item) { - + {{ item.name }} } @@ -23,8 +23,7 @@

NaturalAbstractNavigableList

[availableColumns]="availableColumns" [selections]="selectedColumns" (selectionChange)="selectColumns($event)" - > - + />
@@ -37,14 +36,14 @@

NaturalAbstractNavigableList

(change)="$event ? masterToggle() : null" [checked]="selection.hasValue() && isAllSelected()" [indeterminate]="selection.hasValue() && !isAllSelected()" - > + /> + /> @@ -55,7 +54,7 @@

NaturalAbstractNavigableList

[navigate]="getChildLink(element.item)" icon="chevron_right" matTooltip="Show children" - > + /> }
@@ -80,7 +79,7 @@

NaturalAbstractNavigableList

} @if (!dataSource.data) {
- +
} NaturalAbstractNavigableList [pageIndex]="dataSource.data?.pageIndex" [pageSizeOptions]="pageSizeOptions" [pageSize]="dataSource.data?.pageSize" - > + />
columnsForTable : {{ columnsForTable | json }}
diff --git a/src/app/navigable-list/navigable-list.component.ts b/src/app/navigable-list/navigable-list.component.ts index 6802592e..1aae65f3 100644 --- a/src/app/navigable-list/navigable-list.component.ts +++ b/src/app/navigable-list/navigable-list.component.ts @@ -64,7 +64,7 @@ export class NavigableListComponent extends NaturalAbstractNavigableList = [{field: 'name', order: SortingOrder.DESC}]; + protected override defaultSorting: Sorting[] = [{field: 'name', order: SortingOrder.DESC}]; public constructor(service: ItemService) { super(service); diff --git a/src/app/other/other.component.html b/src/app/other/other.component.html index c2f227d9..7dbf26b6 100644 --- a/src/app/other/other.component.html +++ b/src/app/other/other.component.html @@ -12,7 +12,7 @@

natural-table-button

[fragment]="conf.fragment" [preserveFragment]="conf.preserveFragment" [color]="conf.color" - > + /> } @for (conf of configurationsWithClick; track conf) { @@ -21,7 +21,7 @@

natural-table-button

[icon]="conf.icon" [color]="conf.color" (buttonClick)="log($event)" - > + /> }
@@ -64,16 +64,16 @@

HTTP Prefix

Icons

Native font mat-icon - + Font naturalIcon - - - - - + + + + + SVG naturalIcon - - + +
@@ -92,17 +92,17 @@

Icons

[matMenuTriggerFor]="menu" aria-label="Example icon-button with a menu" > - + Button with naturalIcon @@ -110,15 +110,15 @@

Icons

Test mat-icon - - + + Test naturalIcon - - + +
@@ -147,4 +147,4 @@

Error handler

custom ripple
- + diff --git a/src/app/panels/panels.component.html b/src/app/panels/panels.component.html index f6e9b042..77dac3f3 100644 --- a/src/app/panels/panels.component.html +++ b/src/app/panels/panels.component.html @@ -5,4 +5,4 @@

NaturalPanelsService

Panel 1 & 2 invalid url
- + diff --git a/src/app/relations/relations.component.html b/src/app/relations/relations.component.html index 26c1e226..53f4d290 100644 --- a/src/app/relations/relations.component.html +++ b/src/app/relations/relations.component.html @@ -13,7 +13,7 @@

natural-relations

[main]="data.model" [service]="service" placeholder="Link" - > + />
Service
@@ -22,7 +22,7 @@

natural-relations

[main]="data.model" [service]="service" placeholder="Link" - > + />
No result service
@@ -31,7 +31,7 @@

natural-relations

[main]="data.model" [service]="noResultService" placeholder="Link" - > + />
ErrorService
@@ -40,7 +40,7 @@

natural-relations

[main]="data.model" [service]="errorService" placeholder="Link" - > + />
} diff --git a/src/app/select-enum/select-enum.component.html b/src/app/select-enum/select-enum.component.html index 2e3cd5df..7f68600a 100644 --- a/src/app/select-enum/select-enum.component.html +++ b/src/app/select-enum/select-enum.component.html @@ -19,7 +19,7 @@

+ />
value: {{ withoutModelOutput | json }}

@@ -27,13 +27,9 @@

+ /> - +
+ />
value: {{ myValue | json }}
@@ -51,11 +47,7 @@

FormControl

- +
 touched: {{ formControl.touched | json }}
 dirty: {{ formControl.dirty | json }}
@@ -70,7 +62,7 @@ 

FormControl

enumName="FooEnum" [formControl]="formControlReplace" placeholder="FormControl (replace)" - > + />
 touched: {{ formControlReplace.touched | json }}
 dirty: {{ formControlReplace.dirty | json }}
@@ -89,7 +81,7 @@ 

FormGroup

enumName="FooEnum" formControlName="amazingField" placeholder="FormGroup (update)" - > + />
 touched: {{ formGroup.touched | json }}
@@ -104,7 +96,7 @@ 

FormGroup

enumName="FooEnum" formControlName="amazingField" placeholder="FormGroup (replace)" - > + />
@@ -122,14 +114,14 @@ 

Others

placeholder="Disabled option with formControl" [formControl]="formControl" [optionDisabled]="optionDisabled" - > + /> + />
 touched: {{ formControlMultiple.touched | json }}
diff --git a/src/app/select-hierarchic/select-hierarchic.component.html b/src/app/select-hierarchic/select-hierarchic.component.html
index ee5b4f49..ceace707 100644
--- a/src/app/select-hierarchic/select-hierarchic.component.html
+++ b/src/app/select-hierarchic/select-hierarchic.component.html
@@ -19,7 +19,7 @@ 

+ />
value: {{ withoutModelOutput | json }}
@@ -27,13 +27,13 @@

+ /> + />
+ />
value: {{ myValue | json }}
@@ -55,7 +55,7 @@

FormControl

[config]="hierarchicConfig" [formControl]="formControl" placeholder="FormControl (update)" - > + />
@@ -72,7 +72,7 @@ 

FormControl

[config]="hierarchicConfig" [formControl]="formControlReplace" placeholder="FormControl (replace)" - > + />
 touched: {{ formControlReplace.touched | json }}
 dirty: {{ formControlReplace.dirty | json }}
@@ -91,7 +91,7 @@ 

FormGroup

[config]="hierarchicConfig" formControlName="amazingField" placeholder="FormGroup (update)" - > + /> FormGroup

[config]="hierarchicConfig" formControlName="amazingField" placeholder="FormGroup (replace)" - > + />
@@ -132,23 +132,15 @@ 

Others

Basic input field to test alt+tab and focus on next field - + - + + /> diff --git a/src/app/select/select.component.html b/src/app/select/select.component.html index d9f74367..ea61f542 100644 --- a/src/app/select/select.component.html +++ b/src/app/select/select.component.html @@ -20,22 +20,14 @@

+ />
value: {{ withoutModelOutput | json }}
- + - +
+ />
value: {{ myValue | json }}
@@ -57,7 +49,7 @@

FormControl

[service]="service" [formControl]="formControl" placeholder="FormControl (update)" - >
+ />
@@ -74,7 +66,7 @@ 

FormControl

[service]="service" [formControl]="formControlReplace" placeholder="FormControl (replace)" - > + />
 touched: {{ formControlReplace.touched | json }}
 dirty: {{ formControlReplace.dirty | json }}
@@ -89,11 +81,7 @@ 

FormGroup

- +
FormGroup

- +
@@ -135,23 +119,19 @@ 

Others

Basic input field to test alt+tab and focus on next field - + - + - + - + - + - + Hello {{ item.name }} ! @@ -162,7 +142,7 @@

Others

[(ngModel)]="freeText" [optionRequired]="false" placeholder="Free text allowed" - >
+ />
freeText: {{ freeText }}
diff --git a/src/app/shared/services/theme.service.ts b/src/app/shared/services/theme.service.ts index 9677de81..fc8b0951 100644 --- a/src/app/shared/services/theme.service.ts +++ b/src/app/shared/services/theme.service.ts @@ -5,17 +5,17 @@ import {BehaviorSubject} from 'rxjs'; providedIn: 'root', }) export class ThemeService { - public readonly theme: BehaviorSubject = new BehaviorSubject('defaultDark'); + public readonly theme = new BehaviorSubject('defaultDark'); private darkActivated = false; public constructor() { - if (this.theme.value.indexOf('Dark') > -1) { + if (this.theme.value.includes('Dark')) { this.darkActivated = true; } } public set(theme: string): void { - if (this.darkActivated && theme.indexOf('Dark') === -1) { + if (this.darkActivated && !theme.includes('Dark')) { this.theme.next('defaultDark'); } else { this.theme.next('default');