From f738602a2fcb69482c2648b712045a38ec9b442d Mon Sep 17 00:00:00 2001 From: Sergej Sintschilin Date: Mon, 6 May 2024 13:50:49 +0200 Subject: [PATCH] wip --- .prettierignore | 2 +- angular.json | 47 +++++++++++++++++++ package.json | 2 + src/core/basics/form-reactivity/README.md | 2 + .../form-reactivity/index.23907451.spec.ts | 4 ++ src/core/basics/form-reactivity/index.ts | 4 ++ src/core/basics/lggajrsh/index.ts | 31 ++++++++++++ .../basics/provider-compitibility/README.md | 2 + .../index.45921704.spec.ts | 34 +++++++------- .../basics/provider-compitibility/index.ts | 1 + .../basics/validator-customization/README.md | 2 + .../async/index.96048722.spec.ts | 16 +++---- .../index.45529676.spec.ts | 16 +++---- src/core/composables/form-bridge/index.ts | 32 +++++++++++++ .../composables/form-fallthrough/README.md | 2 + .../form-fallthrough/index.51263815.spec.ts | 2 + .../composables/form-fallthrough/index.ts | 6 +++ src/core/composables/media-query/README.md | 2 + .../media-query/index.97978671.spec.ts | 19 ++++++++ src/core/composables/media-query/index.ts | 36 +++++++++++--- src/core/utils/computed-async/index.ts | 37 +++++++++++++++ src/docs/assets/i18n/en.json | 1 + src/docs/components/root/index.ts | 1 + src/docs/components/root/ng.component.html | 1 + src/docs/components/root/ng.component.ts | 23 +++++++++ src/docs/index.html | 20 ++++++++ src/docs/main.ts | 21 +++++++++ src/docs/plugins/router.ts | 11 +++++ src/docs/plugins/transloco.ts | 28 +++++++++++ src/docs/styles.scss | 0 src/docs/tsconfig.json | 9 ++++ src/misc/object-oven/index.ts | 1 + 32 files changed, 374 insertions(+), 41 deletions(-) create mode 100644 src/core/basics/lggajrsh/index.ts create mode 100755 src/core/composables/form-bridge/index.ts create mode 100644 src/core/composables/media-query/index.97978671.spec.ts create mode 100644 src/core/utils/computed-async/index.ts create mode 100644 src/docs/assets/i18n/en.json create mode 100644 src/docs/components/root/index.ts create mode 100644 src/docs/components/root/ng.component.html create mode 100644 src/docs/components/root/ng.component.ts create mode 100644 src/docs/index.html create mode 100644 src/docs/main.ts create mode 100644 src/docs/plugins/router.ts create mode 100644 src/docs/plugins/transloco.ts create mode 100644 src/docs/styles.scss create mode 100644 src/docs/tsconfig.json diff --git a/.prettierignore b/.prettierignore index dbf78c5..22b9b4c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -4,4 +4,4 @@ /src/core/basics/provider-compitibility /src/core/basics/validator-customization /src/core/composables/form-fallthrough -/src/core/composables/media-query \ No newline at end of file +-/src/core/composables/media-query \ No newline at end of file diff --git a/angular.json b/angular.json index 253791b..b42a9c1 100755 --- a/angular.json +++ b/angular.json @@ -26,6 +26,53 @@ }, "projectType": "library", "root": "." + }, + "docs": { + "architect": { + "build": { + "builder": "@angular-devkit/build-angular:browser-esbuild", + "configurations": { + "development": { + "extractLicenses": false, + "optimization": false, + "sourceMap": true + }, + "production": { + "outputHashing": "all" + } + }, + "defaultConfiguration": "production", + "options": { + "allowedCommonJsDependencies": ["@messageformat/core"], + "assets": ["./src/docs/favicon.ico", "./src/docs/assets"], + "index": "./src/docs/index.html", + "inlineStyleLanguage": "scss", + "main": "./src/docs/main.ts", + "outputPath": "./docs", + "polyfills": ["zone.js"], + "styles": ["./src/docs/styles.scss"], + "tsConfig": "./src/docs/tsconfig.json" + } + }, + "serve": { + "builder": "@angular-devkit/build-angular:dev-server", + "configurations": { + "development": { + "buildTarget": "docs:build:development" + }, + "production": { + "buildTarget": "docs:build:production" + } + }, + "defaultConfiguration": "development", + "options": { + "open": true + } + } + }, + "projectType": "application", + "root": ".", + "sourceRoot": "./src/docs" } }, "version": 1 diff --git a/package.json b/package.json index 0c2974f..b63186b 100755 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ }, "scripts": { "build": "ng build core", + "build:docs": "ng build docs", + "start:docs": "ng serve docs", "test": "ng test", "format": "prettier --write ." }, diff --git a/src/core/basics/form-reactivity/README.md b/src/core/basics/form-reactivity/README.md index 8f48605..1920bab 100644 --- a/src/core/basics/form-reactivity/README.md +++ b/src/core/basics/form-reactivity/README.md @@ -1,3 +1,5 @@ + + # Form Reactivity ## Usage diff --git a/src/core/basics/form-reactivity/index.23907451.spec.ts b/src/core/basics/form-reactivity/index.23907451.spec.ts index 49527d0..494256c 100644 --- a/src/core/basics/form-reactivity/index.23907451.spec.ts +++ b/src/core/basics/form-reactivity/index.23907451.spec.ts @@ -4,6 +4,8 @@ import {FormControl, FormGroup, Validators} from '@angular/forms'; import {formi} from '.'; +// todo: better descriptions + describe('formi', () => { it('should reflect actual state', fakeAsync(async () => { let form = new FormGroup({ @@ -15,6 +17,7 @@ describe('formi', () => { }), }); + // todo for await (let _ of (async function* () { yield; for (let fn of [ @@ -48,6 +51,7 @@ describe('formi', () => { it('should trigger changes properly', fakeAsync(async () => { let form = new FormControl(null); + // todo: rename let effectFn = jest.fn(() => { formi(form).disabled; }); diff --git a/src/core/basics/form-reactivity/index.ts b/src/core/basics/form-reactivity/index.ts index f8a7e8a..2601eae 100644 --- a/src/core/basics/form-reactivity/index.ts +++ b/src/core/basics/form-reactivity/index.ts @@ -29,13 +29,17 @@ export type ReadonlyReactiveFormProxy< & Readonly> ); +// todo: rework +// todo: rename export const formi: { ( control: ControlT, ): ReadonlyReactiveFormProxy; } = (() => { let create = (control) => { + // todo: use helper let changes$ = signal({}); + // todo: use helper [ '_updatePristine', '_updateTouched', diff --git a/src/core/basics/lggajrsh/index.ts b/src/core/basics/lggajrsh/index.ts new file mode 100644 index 0000000..679d38d --- /dev/null +++ b/src/core/basics/lggajrsh/index.ts @@ -0,0 +1,31 @@ +import {DestroyRef, ElementRef, Signal, computed, inject, isSignal, signal} from '@angular/core'; + +export const onDestroy: { + (fn: {(): void}): void; +} = (fn) => { + let ref = inject(DestroyRef, {optional: true}); + if (ref) { + ref.onDestroy(fn); + } +}; + +export type MaybeSignal = T | Signal; + +export const resolveSignal: { + (v: MaybeSignal): Signal; +} = (v) => (isSignal(v) ? v : signal(v).asReadonly()); + +export type MaybeElementSignal = MaybeSignal>; + +export const resolveElementSignal: { + (v: MaybeElementSignal): Signal; +} = (v) => { + let v$ = resolveSignal(v); + return computed(() => { + let v = v$(); + if (v instanceof ElementRef) { + return v.nativeElement; + } + return v; + }); +}; diff --git a/src/core/basics/provider-compitibility/README.md b/src/core/basics/provider-compitibility/README.md index d7129f9..7e6413f 100644 --- a/src/core/basics/provider-compitibility/README.md +++ b/src/core/basics/provider-compitibility/README.md @@ -1,3 +1,5 @@ + + # Provider Compitibility Simplifies the definition of providers and extends it with type safety. diff --git a/src/core/basics/provider-compitibility/index.45921704.spec.ts b/src/core/basics/provider-compitibility/index.45921704.spec.ts index 4f9f6d6..3031cda 100644 --- a/src/core/basics/provider-compitibility/index.45921704.spec.ts +++ b/src/core/basics/provider-compitibility/index.45921704.spec.ts @@ -25,18 +25,18 @@ describe('provide', () => { b = faker.number.int(); } - describe('regular', () => { + describe('single', () => { let token = new InjectionToken(''); describe('useValue', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token).useValue(A.asValue)).toEqual({ provide: token, useValue: A.asValue, }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token).useValue(B.asValue); @@ -51,14 +51,14 @@ describe('provide', () => { }); describe('useFactory', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token).useFactory(A.asFactory)).toEqual({ provide: token, useFactory: A.asFactory, }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token).useFactory(A.asValue); @@ -73,14 +73,14 @@ describe('provide', () => { }); describe('useClass', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token).useClass(A.asClass)).toEqual({ provide: token, useClass: A.asClass, }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token).useClass(A.asValue); @@ -95,14 +95,14 @@ describe('provide', () => { }); describe('useExisting', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token).useExisting(A.asExisting)).toEqual({ provide: token, useExisting: A.asExisting, }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token).useExisting(A.asValue); @@ -121,7 +121,7 @@ describe('provide', () => { let token = new InjectionToken>(''); describe('useValue', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token, {multi: true}).useValue(A.asValue)).toEqual({ provide: token, multi: true, @@ -129,7 +129,7 @@ describe('provide', () => { }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token, {multi: true}).useValue(B.asValue); @@ -144,7 +144,7 @@ describe('provide', () => { }); describe('useFactory', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token, {multi: true}).useFactory(A.asFactory)).toEqual({ provide: token, multi: true, @@ -152,7 +152,7 @@ describe('provide', () => { }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token, {multi: true}).useFactory(A.asValue); @@ -167,7 +167,7 @@ describe('provide', () => { }); describe('useClass', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token, {multi: true}).useClass(A.asClass)).toEqual({ provide: token, multi: true, @@ -175,7 +175,7 @@ describe('provide', () => { }); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token, {multi: true}).useClass(A.asValue); @@ -190,7 +190,7 @@ describe('provide', () => { }); describe('useExisting', () => { - it('should create correct provider', fakeAsync(async () => { + it('should create a correct provider', fakeAsync(async () => { expect(provide(token, {multi: true}).useExisting(A.asExisting)).toEqual( { provide: token, @@ -200,7 +200,7 @@ describe('provide', () => { ); })); - it('should ensure type safety', fakeAsync(async () => { + it('should enforce type safety', fakeAsync(async () => { expect(async () => { // @ts-expect-error provide(token, {multi: true}).useExisting(A.asValue); diff --git a/src/core/basics/provider-compitibility/index.ts b/src/core/basics/provider-compitibility/index.ts index 74f3fa1..5ec72b8 100644 --- a/src/core/basics/provider-compitibility/index.ts +++ b/src/core/basics/provider-compitibility/index.ts @@ -29,6 +29,7 @@ export const provide: { } = (token, { multi = false, } = {}) => { + // todo: rename let provider = {provide: token, ...(multi ? {multi} : {})}; return oo( ...[ diff --git a/src/core/basics/validator-customization/README.md b/src/core/basics/validator-customization/README.md index 4729aef..f9bce3e 100644 --- a/src/core/basics/validator-customization/README.md +++ b/src/core/basics/validator-customization/README.md @@ -1,3 +1,5 @@ + + # Validator Customization ## Usage diff --git a/src/core/basics/validator-customization/async/index.96048722.spec.ts b/src/core/basics/validator-customization/async/index.96048722.spec.ts index 9bc8f6b..698462d 100755 --- a/src/core/basics/validator-customization/async/index.96048722.spec.ts +++ b/src/core/basics/validator-customization/async/index.96048722.spec.ts @@ -4,7 +4,7 @@ import {FormControl} from '@angular/forms'; import {composeAsyncValidators, noopAsyncValidator, stubAsyncValidator, withAsyncValidators} from '.'; describe('withAsyncValidators', () => { - it('should work', fakeAsync(async () => { + it('should work in a common scenario', fakeAsync(async () => { let form = withAsyncValidators( new FormControl(1, { nonNullable: true, @@ -27,7 +27,7 @@ describe('withAsyncValidators', () => { expect(form.errors).toBeNull(); })); - it('should contain validators', fakeAsync(async () => { + it('should contain all provided validators', fakeAsync(async () => { let form = new FormControl(null); let validators = [null, null].map((v) => async () => v); withAsyncValidators(form, ...validators); @@ -62,7 +62,7 @@ describe('withAsyncValidators', () => { }); describe('composeAsyncValidators', () => { - it('should work', fakeAsync(async () => { + it('should work in a common scenario', fakeAsync(async () => { let form = withAsyncValidators( new FormControl(1, { nonNullable: true, @@ -96,7 +96,7 @@ describe('composeAsyncValidators', () => { expect(form.errors).toBeNull(); })); - it('should skip other validators after one fails', fakeAsync(async () => { + it('should skip remaining validators if one fails', fakeAsync(async () => { let validators = [null, {error: true}, null].map((v) => jest.fn(async () => v)); new FormControl(null, { asyncValidators: composeAsyncValidators(validators), @@ -109,19 +109,19 @@ describe('composeAsyncValidators', () => { expect(validators[2]).not.toHaveBeenCalled(); })); - it('should return same validator if only one provided', fakeAsync(async () => { + it('should return the same validator if only one provided', fakeAsync(async () => { let validator = async () => null; expect(composeAsyncValidators([validator])).toBe(validator); })); - it('should return no-op validator if nothing provided', fakeAsync(async () => { + it('should return a no-op validator if no validators provided', fakeAsync(async () => { expect(composeAsyncValidators([])).toBe(noopAsyncValidator); })); }); describe('noopAsyncValidator', () => { - it('should work', fakeAsync(async () => { + it('should return null', fakeAsync(async () => { let form = withAsyncValidators(new FormControl(null), noopAsyncValidator); tick(); @@ -131,7 +131,7 @@ describe('noopAsyncValidator', () => { }); describe('stubAsyncValidator', () => { - it('should work', fakeAsync(async () => { + it('should return provided errors', fakeAsync(async () => { let form = withAsyncValidators(new FormControl(null), stubAsyncValidator({error: true})); tick(); diff --git a/src/core/basics/validator-customization/index.45529676.spec.ts b/src/core/basics/validator-customization/index.45529676.spec.ts index 7a78ca9..fbf10eb 100755 --- a/src/core/basics/validator-customization/index.45529676.spec.ts +++ b/src/core/basics/validator-customization/index.45529676.spec.ts @@ -4,7 +4,7 @@ import {FormControl} from '@angular/forms'; import {composeValidators, noopValidator, stubValidator, withValidators} from '.'; describe('withValidators', () => { - it('should work', fakeAsync(async () => { + it('should work in a common scenario', fakeAsync(async () => { let form = withValidators( new FormControl(1, { nonNullable: true, @@ -19,7 +19,7 @@ describe('withValidators', () => { expect(form.errors).toBeNull(); })); - it('should contain validators', fakeAsync(async () => { + it('should contain all provided validators', fakeAsync(async () => { let form = new FormControl(null); let validators = [null, null].map((v) => () => v); withValidators(form, ...validators); @@ -54,7 +54,7 @@ describe('withValidators', () => { }); describe('composeValidators', () => { - it('should work', fakeAsync(async () => { + it('should work in a common scenario', fakeAsync(async () => { let form = withValidators( new FormControl(1, { nonNullable: true, @@ -76,7 +76,7 @@ describe('composeValidators', () => { expect(form.errors).toBeNull(); })); - it('should skip other validators after one fails', fakeAsync(async () => { + it('should skip remaining validators if one fails', fakeAsync(async () => { let validators = [null, {error: true}, null].map((v) => jest.fn(() => v)); new FormControl(null, { validators: composeValidators(validators), @@ -87,19 +87,19 @@ describe('composeValidators', () => { expect(validators[2]).not.toHaveBeenCalled(); })); - it('should return same validator if only one provided', fakeAsync(async () => { + it('should return the same validator if only one provided', fakeAsync(async () => { let validator = () => null; expect(composeValidators([validator])).toBe(validator); })); - it('should return no-op validator if nothing provided', fakeAsync(async () => { + it('should return a no-op validator if no validators provided', fakeAsync(async () => { expect(composeValidators([])).toBe(noopValidator); })); }); describe('noopValidator', () => { - it('should work', fakeAsync(async () => { + it('should return null', fakeAsync(async () => { let form = withValidators(new FormControl(null), noopValidator); expect(form.errors).toBeNull(); @@ -107,7 +107,7 @@ describe('noopValidator', () => { }); describe('stubValidator', () => { - it('should work', fakeAsync(async () => { + it('should return provided errors', fakeAsync(async () => { let form = withValidators(new FormControl(null), stubValidator({error: true})); expect(form.errors).toEqual({error: true}); diff --git a/src/core/composables/form-bridge/index.ts b/src/core/composables/form-bridge/index.ts new file mode 100755 index 0000000..7d0279f --- /dev/null +++ b/src/core/composables/form-bridge/index.ts @@ -0,0 +1,32 @@ +// @ts-nocheck + +import {Signal, WritableSignal} from '@angular/core'; +import {ValidationErrors} from '@angular/forms'; + +export module useFormBridge { + export type Options = Partial<{ + disabled: WritableSignal; + touched: WritableSignal; + pending: Signal; + errors: Signal; + }>; + + export type Result = { + disabled: Signal; + touched: Signal; + pending: Signal; + errors: Signal; + }; +} + +// todo: should work without injection context + +export const useFormBridge: { + ( + // + value: WritableSignal, + options?: useFormBridge.Options, + ): useFormBridge.Result; +} = () => { + // todo: implement +}; diff --git a/src/core/composables/form-fallthrough/README.md b/src/core/composables/form-fallthrough/README.md index 4819244..74e091e 100644 --- a/src/core/composables/form-fallthrough/README.md +++ b/src/core/composables/form-fallthrough/README.md @@ -1,3 +1,5 @@ + + # Form Fallthrough Passes a control from a control directive through. diff --git a/src/core/composables/form-fallthrough/index.51263815.spec.ts b/src/core/composables/form-fallthrough/index.51263815.spec.ts index 54fb1cf..9545fe3 100644 --- a/src/core/composables/form-fallthrough/index.51263815.spec.ts +++ b/src/core/composables/form-fallthrough/index.51263815.spec.ts @@ -6,6 +6,8 @@ import {simpleFaker as faker} from '@faker-js/faker'; import {useFormFallthrough} from '.'; +// todo: better descriptions + describe('useFormFallthrough', () => { it('should work with FormControlDirective', fakeAsync(async () => { let form = new FormRecord({ diff --git a/src/core/composables/form-fallthrough/index.ts b/src/core/composables/form-fallthrough/index.ts index fabde73..5d66d09 100644 --- a/src/core/composables/form-fallthrough/index.ts +++ b/src/core/composables/form-fallthrough/index.ts @@ -5,6 +5,8 @@ import {AbstractControl, ControlContainer, NgControl} from '@angular/forms'; import oo from '../../../misc/object-oven'; +// todo: should work without injection context + export const useFormFallthrough: { ( controlCtor?: AbstractType, @@ -16,6 +18,7 @@ export const useFormFallthrough: { }; } = oo.extend( (controlCtor = AbstractControl) => { + // todo: rework let ref = inject(NgControl, {self: true, optional: true}); if (ref != null) { ref.valueAccessor ??= { @@ -27,7 +30,9 @@ export const useFormFallthrough: { ref = inject(ControlContainer, {self: true, optional: true}); } if (ref != null) { + // todo: use helper let changes$ = signal({}); + // todo: use helper ['ngOnChanges'].forEach((key) => { let method = ref[key]; if (method) { @@ -39,6 +44,7 @@ export const useFormFallthrough: { }); } }); + // todo: use helper ['name'].forEach((key) => { let value = ref[key]; oo.extend(ref, { diff --git a/src/core/composables/media-query/README.md b/src/core/composables/media-query/README.md index 787e92d..dcbd2c7 100644 --- a/src/core/composables/media-query/README.md +++ b/src/core/composables/media-query/README.md @@ -1,3 +1,5 @@ + + # Media Query ## Usage diff --git a/src/core/composables/media-query/index.97978671.spec.ts b/src/core/composables/media-query/index.97978671.spec.ts new file mode 100644 index 0000000..08ecf0f --- /dev/null +++ b/src/core/composables/media-query/index.97978671.spec.ts @@ -0,0 +1,19 @@ +import {signal} from '@angular/core'; +import {fakeAsync} from '@angular/core/testing'; + +import {useMediaQuery} from '.'; + +// todo: better tests + +describe.skip('useMediaQuery', () => { + it('should work in a common scenario', fakeAsync(async () => { + let query$ = signal('(min-width: 2560px)'); + let matches$ = useMediaQuery(query$); + + expect(matches$).toBe(window.matchMedia(query$()).matches); + + query$.set('(max-width: 2560px)'); + + expect(matches$).toBe(window.matchMedia(query$()).matches); + })); +}); diff --git a/src/core/composables/media-query/index.ts b/src/core/composables/media-query/index.ts index 938b083..bbbbc18 100644 --- a/src/core/composables/media-query/index.ts +++ b/src/core/composables/media-query/index.ts @@ -1,14 +1,36 @@ // @ts-nocheck -import {Signal, signal} from '@angular/core'; +import {Signal, computed, signal} from '@angular/core'; + +import {MaybeSignal, onDestroy, resolveSignal} from '../../basics/lggajrsh'; + +// todo: should work without injection context export const useMediaQuery: { - (query: string): Signal; + (query: MaybeSignal): Signal; + get supported(): boolean; // todo: implement } = (query) => { - let queryResult = window.matchMedia(query); - let queryMatches$ = signal(queryResult.matches); - queryResult.addEventListener('change', (event: MediaQueryListEvent) => { - queryMatches$.set(event.matches); + let {window} = globalThis; + if (!window) { + return signal(false).asReadonly(); + } + let query$ = resolveSignal(query); + // todo: use helper + let onCleanupFn = () => {}; + let hfakwyoe = computed(() => { + onCleanupFn(); + let query = query$(); + let mvtujzpg = window.matchMedia(query); + let imwrjqwq = signal(mvtujzpg.matches); + let onChangeFn = (event) => imwrjqwq.set(event.matches); + mvtujzpg.addEventListener('change', onChangeFn); + onCleanupFn = () => { + mvtujzpg.removeEventListener('change', onChangeFn); + }; + return imwrjqwq; + }); + onDestroy(() => { + onCleanupFn(); }); - return queryMatches$.asReadonly(); + return computed(() => hfakwyoe()()); }; diff --git a/src/core/utils/computed-async/index.ts b/src/core/utils/computed-async/index.ts new file mode 100644 index 0000000..34296e8 --- /dev/null +++ b/src/core/utils/computed-async/index.ts @@ -0,0 +1,37 @@ +// @ts-nocheck + +import {CreateComputedOptions, EffectCleanupRegisterFn, EffectRef, Signal} from '@angular/core'; + +// prettier-ignore +export type CreateComputedAsyncOptions = ( + & CreateComputedOptions + & Partial<{ + initialValue: T; + lazy: boolean; // todo: is needed? + }> +); + +// prettier-ignore +export interface ComputedAsyncRef + extends EffectRef, Signal // todo: is EffectRef needed? +{ + get pending(): boolean; + abort(): void; // todo: is needed? +} + +// todo: should work without injection context + +export const computedAsync: { + ( + // + fn: {(onCleanup: EffectCleanupRegisterFn): Promise}, + options: CreateComputedAsyncOptions & {initialValue: T}, + ): ComputedAsyncRef; + ( + // + fn: {(onCleanup: EffectCleanupRegisterFn): Promise}, + options?: CreateComputedAsyncOptions, + ): ComputedAsyncRef; +} = () => { + // todo: implement +}; diff --git a/src/docs/assets/i18n/en.json b/src/docs/assets/i18n/en.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/src/docs/assets/i18n/en.json @@ -0,0 +1 @@ +{} diff --git a/src/docs/components/root/index.ts b/src/docs/components/root/index.ts new file mode 100644 index 0000000..8f49d69 --- /dev/null +++ b/src/docs/components/root/index.ts @@ -0,0 +1 @@ +export * from './ng.component'; diff --git a/src/docs/components/root/ng.component.html b/src/docs/components/root/ng.component.html new file mode 100644 index 0000000..65e3eb4 --- /dev/null +++ b/src/docs/components/root/ng.component.html @@ -0,0 +1 @@ +{{ wlxlelhm() }} diff --git a/src/docs/components/root/ng.component.ts b/src/docs/components/root/ng.component.ts new file mode 100644 index 0000000..f2635a7 --- /dev/null +++ b/src/docs/components/root/ng.component.ts @@ -0,0 +1,23 @@ +import {CommonModule} from '@angular/common'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {TranslocoModule} from '@jsverse/transloco'; +import {useMediaQuery} from 'ngx-craft'; + +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + // + CommonModule, + RouterModule, + TranslocoModule, + ], + selector: 'my-root', + standalone: true, + templateUrl: './ng.component.html', +}) +export class MyRootComponent { + constructor() {} + + wlxlelhm = useMediaQuery('(min-width: 800px)'); +} diff --git a/src/docs/index.html b/src/docs/index.html new file mode 100644 index 0000000..dbddd3d --- /dev/null +++ b/src/docs/index.html @@ -0,0 +1,20 @@ + + + + + ngx-craft + + + + + + + + diff --git a/src/docs/main.ts b/src/docs/main.ts new file mode 100644 index 0000000..2c6f804 --- /dev/null +++ b/src/docs/main.ts @@ -0,0 +1,21 @@ +import {provideHttpClient} from '@angular/common/http'; +import {bootstrapApplication} from '@angular/platform-browser'; +import {provideAnimations} from '@angular/platform-browser/animations'; + +import {MyRootComponent} from '@/components/root'; +import {myTranslocoProviders} from '@/plugins/transloco'; + +(async () => { + try { + await bootstrapApplication(MyRootComponent, { + providers: [ + provideAnimations(), + provideHttpClient(), + // + myTranslocoProviders, + ], + }); + } catch (error) { + console.error(error); + } +})(); diff --git a/src/docs/plugins/router.ts b/src/docs/plugins/router.ts new file mode 100644 index 0000000..2c77717 --- /dev/null +++ b/src/docs/plugins/router.ts @@ -0,0 +1,11 @@ +import {provideRouter} from '@angular/router'; + +export const myRouterProviders = provideRouter([ + { + path: '/', + }, + { + path: '**', + redirectTo: '/', + }, +]); diff --git a/src/docs/plugins/transloco.ts b/src/docs/plugins/transloco.ts new file mode 100644 index 0000000..e006d48 --- /dev/null +++ b/src/docs/plugins/transloco.ts @@ -0,0 +1,28 @@ +import {HttpClient} from '@angular/common/http'; +import {inject, Injectable, makeEnvironmentProviders} from '@angular/core'; +import {provideTransloco, Translation, TranslocoLoader} from '@jsverse/transloco'; +import {provideTranslocoMessageformat} from '@jsverse/transloco-messageformat'; + +@Injectable({ + providedIn: 'root', +}) +export class MyTranslocoLoader implements TranslocoLoader { + constructor() {} + + http = inject(HttpClient); + + getTranslation(lang: string) { + return this.http.get(`/assets/i18n/${lang}.json`); + } +} + +export const myTranslocoProviders = makeEnvironmentProviders([ + provideTransloco({ + config: { + availableLangs: ['en'], + defaultLang: 'en', + }, + loader: MyTranslocoLoader, + }), + provideTranslocoMessageformat(), +]); diff --git a/src/docs/styles.scss b/src/docs/styles.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/docs/tsconfig.json b/src/docs/tsconfig.json new file mode 100644 index 0000000..bdbb420 --- /dev/null +++ b/src/docs/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./*"], + "ngx-craft": [".."] + } + }, + "extends": "../tsconfig.json" +} diff --git a/src/misc/object-oven/index.ts b/src/misc/object-oven/index.ts index 0b2f690..35d48a1 100644 --- a/src/misc/object-oven/index.ts +++ b/src/misc/object-oven/index.ts @@ -13,6 +13,7 @@ function create(...sources: any[]): any { } function toString() { + // todo return ''; }