mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
feat(deps): upgrade Angular to v21
- Update @angular/* packages to v21.0.8 - Update @angular/material and @angular/cdk to v21.0.6 - Update @angular-eslint/* to v21.1.0 - Update @ngrx/* to v21.0.1 - Update TypeScript to 5.9.3 - Update ngx-markdown to v21 and marked to v17 - Update typia to v11 for TypeScript 5.9 support - Update @types/node to v22, chart.js to v4.5.1 Breaking changes addressed: - Replace deep imports with public API imports (idb, formly, ngrx) - Update marked-options-factory for marked v17 API changes - Add custom FormlySliderComponent (formly slider incompatible with Mat v21) - Update ngx-markdown SANITIZE configuration - Fix HostListener decorators with unused $event args - Fix crypto.subtle type compatibility - Add skipLibCheck for dependency type conflicts - Update tsconfig module settings for Angular 21 Removed: - @angular-builders/custom-webpack (unused)
This commit is contained in:
parent
3f86044147
commit
132947a69b
20 changed files with 257 additions and 1527 deletions
1636
package-lock.json
generated
1636
package-lock.json
generated
File diff suppressed because it is too large
Load diff
18
package.json
18
package.json
|
|
@ -140,7 +140,6 @@
|
|||
"node-fetch": "^2.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-builders/custom-webpack": "^20.0.0",
|
||||
"@angular-devkit/build-angular": "^21.0.5",
|
||||
"@angular-eslint/builder": "^21.1.0",
|
||||
"@angular-eslint/eslint-plugin": "^21.1.0",
|
||||
|
|
@ -181,18 +180,18 @@
|
|||
"@ngrx/schematics": "^21.0.1",
|
||||
"@ngrx/store": "21.0.1",
|
||||
"@ngrx/store-devtools": "^21.0.1",
|
||||
"@ngx-formly/core": "7.0.0",
|
||||
"@ngx-formly/material": "7.0.0",
|
||||
"@ngx-formly/core": "^7.0.1",
|
||||
"@ngx-formly/material": "^7.0.1",
|
||||
"@ngx-translate/core": "^17.0.0",
|
||||
"@ngx-translate/http-loader": "^17.0.0",
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@schematics/angular": "^20.1.4",
|
||||
"@schematics/angular": "^21.0.0",
|
||||
"@types/electron": "^1.4.38",
|
||||
"@types/electron-localshortcut": "^3.1.3",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/jasmine": "^3.10.2",
|
||||
"@types/jasminewd2": "~2.0.13",
|
||||
"@types/node": "20.12.4",
|
||||
"@types/node": "^22.19.5",
|
||||
"@types/node-fetch": "^2.6.6",
|
||||
"@types/object-path": "^0.11.4",
|
||||
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
||||
|
|
@ -203,7 +202,7 @@
|
|||
"baseline-browser-mapping": "^2.9.11",
|
||||
"canvas-confetti": "^1.9.4",
|
||||
"chai": "^5.1.2",
|
||||
"chart.js": "^4.4.7",
|
||||
"chart.js": "^4.5.1",
|
||||
"chrono-node": "^2.8.3",
|
||||
"clipboard": "^2.0.11",
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
|
|
@ -235,11 +234,11 @@
|
|||
"karma-jasmine": "~5.1.0",
|
||||
"karma-jasmine-html-reporter": "^1.6.0",
|
||||
"karma-spec-reporter": "^0.0.36",
|
||||
"marked": "^12.0.2",
|
||||
"marked": "^17.0.0",
|
||||
"nanoid": "^5.1.6",
|
||||
"new-github-issue-url": "^1.1.0",
|
||||
"ng2-charts": "^8.0.0",
|
||||
"ngx-markdown": "^20.0.0",
|
||||
"ngx-markdown": "^21.0.0",
|
||||
"playwright": "^1.56.1",
|
||||
"prettier": "^3.5.1",
|
||||
"pretty-quick": "^4.1.1",
|
||||
|
|
@ -258,9 +257,6 @@
|
|||
"typia": "^11.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"ngx-markdown": {
|
||||
"marked": "12.0.2"
|
||||
},
|
||||
"@conventional-changelog/git-client": "^2.5.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Injectable, signal } from '@angular/core';
|
||||
import { IDBPDatabase } from 'idb/build';
|
||||
import { DBSchema, openDB } from 'idb';
|
||||
import { DBSchema, IDBPDatabase, openDB } from 'idb';
|
||||
import { DBAdapter } from './db-adapter.model';
|
||||
import { Log } from '../log';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { AppBaseData } from '../../imex/sync/sync.model';
|
||||
import { Action } from '@ngrx/store';
|
||||
import { ActionReducer } from '@ngrx/store/src/models';
|
||||
import { ActionReducer } from '@ngrx/store';
|
||||
|
||||
export interface PersistenceLegacyBaseModel<T> {
|
||||
appDataKey: keyof AppBaseData;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import {
|
|||
import { nanoid } from 'nanoid';
|
||||
import { T } from '../../t.const';
|
||||
import { DEFAULT_PANEL_CFG } from './boards.const';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core/lib/models/fieldconfig';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
|
||||
const getNewPanel = (): BoardPanelCfg => ({
|
||||
...DEFAULT_PANEL_CFG,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import {
|
|||
import { T } from '../../../t.const';
|
||||
import { EMPTY_SIMPLE_COUNTER } from '../../simple-counter/simple-counter.const';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core/lib/models/fieldconfig';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
|
||||
export const SIMPLE_COUNTER_FORM: ConfigFormSection<SimpleCounterConfig> = {
|
||||
title: T.F.SIMPLE_COUNTER.FORM.TITLE,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { TaskCopy } from '../../tasks/task.model';
|
|||
import { IssueProviderActions } from './issue-provider.actions';
|
||||
import { first, tap } from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Update } from '@ngrx/entity/src/models';
|
||||
import { Update } from '@ngrx/entity';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { __updateMultipleTaskSimple } from '../../tasks/store/task.actions';
|
||||
import { TaskArchiveService } from '../../time-tracking/task-archive.service';
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export class PlannerCalendarEventComponent {
|
|||
return this.isBeingSubmitted;
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
@HostListener('click')
|
||||
async onClick(): Promise<void> {
|
||||
if (this.isBeingSubmitted) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export class PlannerTaskComponent extends BaseComponent implements OnInit, OnDes
|
|||
return this.task.id === this._taskService.currentTaskId();
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
@HostListener('click')
|
||||
async clickHandler(): Promise<void> {
|
||||
if (this.task) {
|
||||
// Use bottom panel on mobile, dialog on desktop
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { DEFAULT_SIMPLE_COUNTERS } from '../simple-counter.const';
|
|||
import { arrayToDictionary } from '../../../util/array-to-dictionary';
|
||||
import { loadAllData } from '../../../root-store/meta/load-all-data.action';
|
||||
import { updateAllInDictionary } from '../../../util/update-all-in-dictionary';
|
||||
import { Update } from '@ngrx/entity/src/models';
|
||||
import { Update } from '@ngrx/entity';
|
||||
import {
|
||||
addSimpleCounter,
|
||||
decreaseSimpleCounterCounterToday,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { WorkContextCommon, WorkContextThemeCfg } from './work-context.model';
|
|||
import { WorklogExportSettings, WorklogGrouping } from '../worklog/worklog.model';
|
||||
import { ConfigFormSection } from '../config/global-config.model';
|
||||
import { T } from '../../t.const';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core/lib/models/fieldconfig';
|
||||
import { FormlyFieldConfig } from '@ngx-formly/core';
|
||||
|
||||
export const WORKLOG_EXPORT_DEFAULTS: WorklogExportSettings = {
|
||||
cols: ['DATE', 'START', 'END', 'TIME_CLOCK', 'TITLES_INCLUDING_SUB'],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { IDBPDatabase } from 'idb/build';
|
||||
import { DBSchema, openDB } from 'idb';
|
||||
import { DBSchema, IDBPDatabase, openDB } from 'idb';
|
||||
import { DatabaseAdapter } from './database-adapter.model';
|
||||
import { MiniObservable } from '../util/mini-observable';
|
||||
import { PFLog } from '../../../core/log';
|
||||
|
|
|
|||
|
|
@ -87,10 +87,13 @@ const _deriveKeyArgon = async (
|
|||
outputType: 'binary',
|
||||
});
|
||||
|
||||
return window.crypto.subtle.importKey('raw', derivedBytes, { name: ALGORITHM }, false, [
|
||||
'encrypt',
|
||||
'decrypt',
|
||||
]);
|
||||
return window.crypto.subtle.importKey(
|
||||
'raw',
|
||||
derivedBytes.buffer as ArrayBuffer,
|
||||
{ name: ALGORITHM },
|
||||
false,
|
||||
['encrypt', 'decrypt'],
|
||||
);
|
||||
};
|
||||
|
||||
const decryptArgon = async (data: string, password: string): Promise<string> => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import { TASK_FEATURE_NAME } from '../../features/tasks/store/task.reducer';
|
|||
import { TAG_FEATURE_NAME, tagAdapter } from '../../features/tag/store/tag.reducer';
|
||||
import { taskAdapter } from '../../features/tasks/store/task.adapter';
|
||||
import { Project } from '../../features/project/project.model';
|
||||
import { Action, ActionReducer } from '@ngrx/store/src/models';
|
||||
import { Action, ActionReducer } from '@ngrx/store';
|
||||
import { TODAY_TAG } from '../../features/tag/tag.const';
|
||||
import { Log } from '../../core/log';
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export class EnlargeImgDirective {
|
|||
this.imageEl = this._el.nativeElement;
|
||||
}
|
||||
|
||||
@HostListener('click', ['$event']) onClick(): void {
|
||||
@HostListener('click') onClick(): void {
|
||||
this.isImg = this.imageEl.tagName.toLowerCase() === 'img';
|
||||
|
||||
if (this.isImg || this.enlargeImg()) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { KeyboardInputComponent } from '../features/config/keyboard-input/keyboa
|
|||
import { IconInputComponent } from '../features/config/icon-input/icon-input.component';
|
||||
import { SelectProjectComponent } from '../features/config/select-project/select-project.component';
|
||||
import { RepeatSectionTypeComponent } from '../features/config/repeat-section-type/repeat-section-type.component';
|
||||
import { FormlyMatSliderModule } from '@ngx-formly/material/slider';
|
||||
import { FormlySliderComponent } from './formly-slider/formly-slider.component';
|
||||
import { FormlyTagSelectionComponent } from './formly-tag-selection/formly-tag-selection.component';
|
||||
import { FormlyBtnComponent } from './formly-button/formly-btn.component';
|
||||
import { FormlyImageInputComponent } from './formly-image-input/formly-image-input.component';
|
||||
|
|
@ -28,7 +28,7 @@ import { ColorInputComponent } from '../features/config/color-input/color-input.
|
|||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
FormlyMatSliderModule,
|
||||
FormlySliderComponent,
|
||||
ReactiveFormsModule,
|
||||
FormlyModule.forRoot({
|
||||
validationMessages: [
|
||||
|
|
@ -40,6 +40,7 @@ import { ColorInputComponent } from '../features/config/color-input/color-input.
|
|||
{ name: 'maxLength', message: 'Value is too long' },
|
||||
],
|
||||
types: [
|
||||
{ name: 'slider', component: FormlySliderComponent, wrappers: ['form-field'] },
|
||||
{ name: 'link', component: FormlyLinkWidgetComponent },
|
||||
{
|
||||
name: 'duration',
|
||||
|
|
|
|||
55
src/app/ui/formly-slider/formly-slider.component.ts
Normal file
55
src/app/ui/formly-slider/formly-slider.component.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { FieldType, FieldTypeConfig } from '@ngx-formly/core';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormlyFieldProps } from '@ngx-formly/material/form-field';
|
||||
|
||||
interface SliderProps extends FormlyFieldProps {
|
||||
displayWith?: (value: number) => string;
|
||||
discrete?: boolean;
|
||||
showTickMarks?: boolean;
|
||||
thumbLabel?: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'formly-field-mat-slider',
|
||||
standalone: true,
|
||||
imports: [MatSliderModule, ReactiveFormsModule],
|
||||
template: `
|
||||
<mat-slider
|
||||
[min]="props.min ?? 0"
|
||||
[max]="props.max ?? 100"
|
||||
[step]="props.step ?? 1"
|
||||
[discrete]="props.discrete ?? props.thumbLabel ?? true"
|
||||
[showTickMarks]="props.showTickMarks ?? false"
|
||||
[displayWith]="props.displayWith ?? defaultDisplayWith"
|
||||
>
|
||||
<input
|
||||
matSliderThumb
|
||||
[formControl]="formControl"
|
||||
/>
|
||||
</mat-slider>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
:host {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
mat-slider {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FormlySliderComponent extends FieldType<FieldTypeConfig<SliderProps>> {
|
||||
defaultDisplayWith = (value: number): string => `${value}`;
|
||||
|
||||
override defaultOptions = {
|
||||
props: {
|
||||
hideFieldUnderline: true,
|
||||
floatLabel: 'always' as const,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
@ -3,18 +3,18 @@ import { MarkedOptions, MarkedRenderer } from 'ngx-markdown';
|
|||
export const markedOptionsFactory = (): MarkedOptions => {
|
||||
const renderer = new MarkedRenderer();
|
||||
|
||||
renderer.checkbox = (isChecked: boolean) =>
|
||||
`<span class="checkbox material-icons">${isChecked ? 'check_box' : 'check_box_outline_blank'}</span>`;
|
||||
renderer.checkbox = ({ checked }) =>
|
||||
`<span class="checkbox material-icons">${checked ? 'check_box' : 'check_box_outline_blank'}</span>`;
|
||||
|
||||
renderer.listitem = (text: string) =>
|
||||
renderer.listitem = ({ text }) =>
|
||||
text.includes('checkbox')
|
||||
? `<li class="checkbox-wrapper ${text.includes('check_box_outline_blank') ? 'undone' : 'done'}">${text}</li>`
|
||||
: `<li>${text}</li>`;
|
||||
|
||||
renderer.link = (href, title, text) =>
|
||||
`<a target="_blank" href="${href}" title="${title}">${text}</a>`;
|
||||
renderer.link = ({ href, title, text }) =>
|
||||
`<a target="_blank" href="${href}" title="${title || ''}">${text}</a>`;
|
||||
|
||||
renderer.paragraph = (text) => {
|
||||
renderer.paragraph = ({ text }) => {
|
||||
const split = text.split('\n');
|
||||
return split.reduce((acc, p, i) => {
|
||||
const result = /h(\d)\./.exec(p);
|
||||
|
|
@ -35,13 +35,15 @@ export const markedOptionsFactory = (): MarkedOptions => {
|
|||
const urlPattern =
|
||||
/\b((([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/?#]*))([^?#]*)(\?([^#]*))?(#(.*))?)\b/gi;
|
||||
|
||||
const rendererTxtOld = renderer.text;
|
||||
renderer.text = (text) => {
|
||||
return rendererTxtOld(
|
||||
text.replace(urlPattern, (url) => {
|
||||
const rendererTxtOld = renderer.text.bind(renderer);
|
||||
renderer.text = (token) => {
|
||||
const modifiedToken = {
|
||||
...token,
|
||||
text: token.text.replace(urlPattern, (url) => {
|
||||
return `<a href="${url}" target="_blank">${url}</a>`;
|
||||
}),
|
||||
);
|
||||
};
|
||||
return rendererTxtOld(modifiedToken);
|
||||
};
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { App as CapacitorApp } from '@capacitor/app';
|
|||
import { GlobalErrorHandler } from './app/core/error-handler/global-error-handler.class';
|
||||
import { bootstrapApplication, BrowserModule } from '@angular/platform-browser';
|
||||
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
|
||||
import { MarkdownModule, MARKED_OPTIONS, provideMarkdown } from 'ngx-markdown';
|
||||
import { MarkdownModule, MARKED_OPTIONS, provideMarkdown, SANITIZE } from 'ngx-markdown';
|
||||
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
||||
import { FeatureStoresModule } from './app/root-store/feature-stores.module';
|
||||
import { MATERIAL_ANIMATIONS, MatNativeDateModule } from '@angular/material/core';
|
||||
|
|
@ -93,7 +93,7 @@ bootstrapApplication(AppComponent, {
|
|||
provide: MARKED_OPTIONS,
|
||||
useFactory: markedOptionsFactory,
|
||||
},
|
||||
sanitize: SecurityContext.HTML,
|
||||
sanitize: { provide: SANITIZE, useValue: SecurityContext.HTML },
|
||||
}),
|
||||
MaterialCssVarsModule.forRoot(),
|
||||
MatSidenavModule,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@
|
|||
"outDir": "./dist/out-tsc",
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"module": "es2022",
|
||||
"moduleResolution": "node",
|
||||
"skipLibCheck": true,
|
||||
"module": "preserve",
|
||||
"moduleResolution": "bundler",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue