From bb26b09f7199cfd34ddaa9c9a8a71229af326f45 Mon Sep 17 00:00:00 2001 From: Valerio Date: Thu, 31 Oct 2024 09:42:37 +0100 Subject: [PATCH 01/21] feat: component transfer --- .devcontainer/devcontainer.json | 70 +++-- .devcontainer/scripts/postCreateCommand.sh | 11 +- .devcontainer/scripts/postStartCommand.sh | 9 + angular.json | 3 +- .../src/lib/components/form/form.module.ts | 2 + .../form/transfer/store/transfer.reducers.ts | 242 ++++++++++++++++++ .../form/transfer/store/transfer.state.ts | 20 ++ .../form/transfer/store/transfer.store.ts | 88 +++++++ .../transfer-list.component.html | 42 +++ .../transfer-list.component.spec.ts | 31 +++ .../transfer-list/transfer-list.component.ts | 93 +++++++ .../form/transfer/transfer.component.html | 52 ++++ .../form/transfer/transfer.component.spec.ts | 34 +++ .../form/transfer/transfer.component.ts | 157 ++++++++++++ .../form/transfer/transfer.model.ts | 13 + projects/design-angular-kit/src/public_api.ts | 42 +-- src/app/app-routing.module.ts | 1 + .../transfer-default-example.component.html | 9 + .../transfer-default-example.component.ts | 30 +++ .../transfer-examples.component.tpl | 37 +++ .../transfer-examples.component.ts | 7 + .../transfer-index.component.html | 13 + .../transfer-index.component.ts | 14 + ...nsfer-reactive-form-example.component.html | 11 + ...ransfer-reactive-form-example.component.ts | 30 +++ src/app/transfer/transfer-routing.module.ts | 16 ++ ...nsfer-template-form-example.component.html | 9 + ...ransfer-template-form-example.component.ts | 27 ++ src/app/transfer/transfer.module.ts | 22 ++ src/assets/table-of-content.json | 4 + 30 files changed, 1078 insertions(+), 61 deletions(-) create mode 100755 .devcontainer/scripts/postStartCommand.sh create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.reducers.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.state.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.store.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.html create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.spec.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.html create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.spec.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.ts create mode 100644 projects/design-angular-kit/src/lib/components/form/transfer/transfer.model.ts create mode 100644 src/app/transfer/transfer-default-example/transfer-default-example.component.html create mode 100644 src/app/transfer/transfer-default-example/transfer-default-example.component.ts create mode 100644 src/app/transfer/transfer-examples/transfer-examples.component.tpl create mode 100644 src/app/transfer/transfer-examples/transfer-examples.component.ts create mode 100644 src/app/transfer/transfer-index/transfer-index.component.html create mode 100644 src/app/transfer/transfer-index/transfer-index.component.ts create mode 100644 src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.html create mode 100644 src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.ts create mode 100644 src/app/transfer/transfer-routing.module.ts create mode 100644 src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.html create mode 100644 src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.ts create mode 100644 src/app/transfer/transfer.module.ts diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index edddb14b..bcfe1fb4 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,38 +1,36 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node { - "name": "design-angular-kit", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - // "image": "mcr.microsoft.com/devcontainers/javascript-node:1-20-bookworm", - "build": { - "dockerfile": "Dockerfile" - }, - // Features to add to the dev container. More info: https://containers.dev/features. - "features": { - "ghcr.io/devcontainers-contrib/features/angular-cli:2": { - "version": "18.0.7" - }, - "ghcr.io/devcontainers-contrib/features/cz-cli:1": {} - }, - "mounts": [ - "source=design-angular-kit-bundle-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" // deps volume - ], - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "sh .devcontainer/scripts/postCreateCommand.sh", - // Configure tool-specific properties. - "customizations": { - "vscode": { - "extensions": [ - "angular.ng-template", - "cyrilletuzi.angular-schematics", - "esbenp.prettier-vscode", - "dbaeumer.vscode-eslint", - "vivaxy.vscode-conventional-commits" - ] - } - } - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + "name": "design-angular-kit", + "build": { + "dockerfile": "Dockerfile" + }, + "mounts": [ + "source=design-angular-kit-bundle-node_modules,target=${containerWorkspaceFolder}/node_modules,type=volume" // deps volume + ], + "remoteUser": "root", + "postCreateCommand": "sh .devcontainer/scripts/postCreateCommand.sh", + // "postStartCommand": "/bin/bash -c '.devcontainer/scripts/postStartCommand.sh \"${containerWorkspaceFolder}\"'", + "postStartCommand": "sh .devcontainer/scripts/postStartCommand.sh \"${containerWorkspaceFolder}\"", + "customizations": { + "vscode": { + "extensions": [ + "angular.ng-template", + "cyrilletuzi.angular-schematics", + "esbenp.prettier-vscode", + "dbaeumer.vscode-eslint", + "vivaxy.vscode-conventional-commits" + ], + "settings": { + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "always" + } + } + } + }, + "features": { + "ghcr.io/devcontainers-contrib/features/angular-cli:2": { + "version": "18.0.7" + }, + "ghcr.io/devcontainers-contrib/features/cz-cli:1": {} + } } diff --git a/.devcontainer/scripts/postCreateCommand.sh b/.devcontainer/scripts/postCreateCommand.sh index 8e3de4ba..3019de0a 100644 --- a/.devcontainer/scripts/postCreateCommand.sh +++ b/.devcontainer/scripts/postCreateCommand.sh @@ -1,9 +1,12 @@ #/bin/bash +echo "lifecycle hook: postCreateCommand => start" -echo "Set node_modules permission" -sudo chown -R node:node node_modules -echo "Set node_modules permission: done" +# echo "Set node_modules permission" +# sudo chown -R node:node node_modules +# echo "Set node_modules permission: done" echo "Installing deps" npm i -echo "Installing deps: done" \ No newline at end of file +echo "Installing deps: done" + +echo "lifecycle hook: postCreateCommand => done" diff --git a/.devcontainer/scripts/postStartCommand.sh b/.devcontainer/scripts/postStartCommand.sh new file mode 100755 index 00000000..83b73b96 --- /dev/null +++ b/.devcontainer/scripts/postStartCommand.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +WORKSPACE_FOLDER=$1 + +echo "lifecycle hook: postStartCommand => start" +echo "Set $WORKSPACE_FOLDER as git safe directory" +git config --global --add safe.directory $WORKSPACE_FOLDER +# git config --global --add safe.directory /workspaces/design-angular-kit +echo "lifecycle hook: postStartCommand => done" diff --git a/angular.json b/angular.json index a58113d2..f9e65bf3 100644 --- a/angular.json +++ b/angular.json @@ -106,7 +106,8 @@ "serve": { "builder": "@angular-devkit/build-angular:dev-server", "options": { - "buildTarget": "design-angular-kit-bundle:build" + "buildTarget": "design-angular-kit-bundle:build", + "host": "127.0.0.1" }, "configurations": { "production": { diff --git a/projects/design-angular-kit/src/lib/components/form/form.module.ts b/projects/design-angular-kit/src/lib/components/form/form.module.ts index ba56fcf7..ff00adc9 100644 --- a/projects/design-angular-kit/src/lib/components/form/form.module.ts +++ b/projects/design-angular-kit/src/lib/components/form/form.module.ts @@ -10,6 +10,7 @@ import { ItTextareaComponent } from './textarea/textarea.component'; import { ItUploadDragDropComponent } from './upload-drag-drop/upload-drag-drop.component'; import { ItUploadFileListComponent } from './upload-file-list/upload-file-list.component'; import { ItAutocompleteComponent } from './autocomplete/autocomplete.component'; +import { ItTransferComponent } from './transfer/transfer.component'; const formComponents = [ ItAutocompleteComponent, @@ -21,6 +22,7 @@ const formComponents = [ ItRatingComponent, ItSelectComponent, ItTextareaComponent, + ItTransferComponent, ItUploadDragDropComponent, ItUploadFileListComponent, ]; diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.reducers.ts b/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.reducers.ts new file mode 100644 index 00000000..644e47e0 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.reducers.ts @@ -0,0 +1,242 @@ +import { TransferItem, TransferItemSelection } from '../transfer.model'; +import { SelectionState, State } from './transfer.state'; + +//#region private utility functions +const generateSelectAll = (checked: boolean, items: TransferItem[]) => { + const selected = new Set>(); + if (checked) { + items.forEach(item => selected.add(item)); + } + + return selected; +}; + +const updateSelected = (set: Set>, item: TransferItem) => { + if (set.has(item)) { + set.delete(item); + } else { + set.add(item); + } + + return set; +}; +//#endregion + +//#region reducers +const init = (state: State, { source, target }: SelectionState) => ({ + ...state, + initialItems: { + source: [...source], + target: [...target], + }, + current: { + source: [...source], + target: [...target], + }, +}); + +const transfer = (state: State) => { + return { + ...state, + current: { + ...state.current, + source: state.current.source.filter(i => !state.selections.source.has(i)), + target: Array.from(new Set([...state.current.target, ...Array.from(state.selections.source)] as TransferItemSelection)), + }, + selections: { + ...state.selections, + source: new Set>(), + }, + operationsEnabled: { + ...state.operationsEnabled, + transfer: false, + reset: true, + }, + } satisfies State; +}; + +const backtransfer = (state: State) => { + return { + ...state, + current: { + ...state.current, + target: state.current.target.filter(i => !state.selections.target.has(i)), + source: Array.from(new Set([...state.current.source, ...Array.from(state.selections.target)] as TransferItemSelection)), + }, + selections: { + ...state.selections, + target: new Set>(), + }, + operationsEnabled: { + ...state.operationsEnabled, + backtransfer: false, + reset: true, + }, + } satisfies State; +}; + +const reset = (state: State) => { + return { + ...state, + current: { + source: [...state.initialItems.source], + target: [...state.initialItems.target], + }, + operationsEnabled: { + ...state.operationsEnabled, + reset: false, + }, + } satisfies State; +}; + +const selectAllSource = (state: State, { checked }: { checked: boolean }) => { + const items = state.current.source; + const selected = generateSelectAll(checked, items); + const transfer = Boolean(selected.size); + + return { + ...state, + selections: { + ...state.selections, + source: selected, + }, + operationsEnabled: { + ...state.operationsEnabled, + transfer, + }, + } satisfies State; +}; + +const selectAllTarget = (state: State, { checked }: { checked: boolean }) => { + const items = state.current.target; + const selected = generateSelectAll(checked, items); + const backtransfer = Boolean(selected.size); + + return { + ...state, + selections: { + ...state.selections, + target: selected, + }, + operationsEnabled: { + ...state.operationsEnabled, + backtransfer, + }, + } satisfies State; +}; + +const selectionItemSource = (previousState: State, { item }: { item: TransferItem }) => { + const selected = updateSelected(previousState.selections.source, item); + const selectedItems = Array.from(selected); + const transfer = Boolean(selectedItems.length); + const source = new Set([...selectedItems]); + + const state = { + ...previousState, + selections: { + ...previousState.selections, + source, + }, + operationsEnabled: { + ...previousState.operationsEnabled, + transfer, + }, + } satisfies State; + + return state; +}; + +const selectionItemTarget = (previousState: State, { item }: { item: TransferItem }) => { + const selected = updateSelected(previousState.selections.target, item); + const selectedItems = Array.from(selected); + const backtransfer = Boolean(selectedItems.length); + const target = new Set([...selectedItems]); + + const state = { + ...previousState, + selections: { + ...previousState.selections, + target, + }, + operationsEnabled: { + ...previousState.operationsEnabled, + backtransfer, + }, + } satisfies State; + + return state; +}; +//#endregion reducers + +//#region public reducers +const initialStateFn = () => ({ + initialItems: { + source: [], + target: [], + }, + current: { + source: [], + target: [], + }, + selections: { + source: new Set>(), + target: new Set>(), + }, + operationsEnabled: { + transfer: false, + backtransfer: false, + reset: false, + }, +}); +const initFn = + (payload: SelectionState) => + (state: State) => + init(state, payload); + +const transferFn = + () => + (state: State) => + transfer(state); + +const backtransferFn = + () => + (state: State) => + backtransfer(state); + +const resetFn = + () => + (state: State) => + reset(state); + +const selectAllSourceFn = + ({ checked }: { checked: boolean }) => + (state: State) => + selectAllSource(state, { checked }) as State; + +const selectAllTargetFn = + ({ checked }: { checked: boolean }) => + (state: State) => + selectAllTarget(state, { checked }) as State; + +const selectionItemSourceFn = + ({ item }: { item: TransferItem }) => + (state: State) => + selectionItemSource(state, { item }) as State; + +const selectionItemTargetFn = + ({ item }: { item: TransferItem }) => + (state: State) => + selectionItemTarget(state, { item }) as State; +//#endregion + +export default { + initialStateFn, + initFn, + transferFn, + backtransferFn, + resetFn, + selectAllSourceFn, + selectAllTargetFn, + selectionItemSourceFn, + selectionItemTargetFn, +}; diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.state.ts b/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.state.ts new file mode 100644 index 00000000..5c6962ec --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.state.ts @@ -0,0 +1,20 @@ +import { TransferItem } from '../transfer.model'; + +export interface SelectionState { + source: Array>; + target: Array>; +} + +export interface State { + initialItems: SelectionState; + current: SelectionState; + selections: { + source: Set>; + target: Set>; + }; + operationsEnabled: { + transfer: boolean; + backtransfer: boolean; + reset: boolean; + }; +} diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.store.ts b/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.store.ts new file mode 100644 index 00000000..81e3cca4 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/store/transfer.store.ts @@ -0,0 +1,88 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, distinctUntilChanged, map, of } from 'rxjs'; +import { SourceType, TransferItem } from '../transfer.model'; +import reducers from './transfer.reducers'; +import { SelectionState, State } from './transfer.state'; + +@Injectable() +export class TransferStore { + private readonly _state = new BehaviorSubject>(reducers.initialStateFn()); + + private readonly sourceItems = this._state.pipe(map(state => state.current.source)); + + private readonly targetItems = this._state.pipe(map(state => state.current.target)); + + readonly valueChanged = this._state.pipe( + map(state => state.current.target), + distinctUntilChanged() + ); + + readonly selectItems = (sourceType: SourceType) => { + if (sourceType === 'source') { + return this.sourceItems; + } + + if (sourceType === 'target') { + return this.targetItems; + } + + return of[]>([]); + }; + + readonly selectSelectedItems = (sourceType: SourceType) => { + if (sourceType === 'source') { + return this._state.pipe(map(state => state.selections.source)); + } + + if (sourceType === 'target') { + return this._state.pipe(map(state => state.selections.target)); + } + + return of(new Set>()); + }; + + readonly transferEnabled = this._state.pipe(map(state => state.operationsEnabled.transfer)); + + readonly backtransferEnabled = this._state.pipe(map(state => state.operationsEnabled.backtransfer)); + + readonly resetEnabled = this._state.pipe(map(state => state.operationsEnabled.reset)); + + init({ source, target }: SelectionState) { + this.updateState(reducers.initFn({ source, target })); + } + + transfer() { + this.updateState(reducers.transferFn()); + } + backtransfer() { + this.updateState(reducers.backtransferFn()); + } + + reset() { + this.updateState(reducers.resetFn()); + } + + checkboxSelection(item: TransferItem, sourceType: SourceType) { + if (sourceType === 'source') { + this.updateState(reducers.selectionItemSourceFn({ item })); + } + + if (sourceType === 'target') { + this.updateState(reducers.selectionItemTargetFn({ item })); + } + } + + selectAllSelection(checked: boolean, sourceType: SourceType) { + if (sourceType === 'source') { + this.updateState(reducers.selectAllSourceFn({ checked })); + } + + if (sourceType === 'target') { + this.updateState(reducers.selectAllTargetFn({ checked })); + } + } + + private updateState(reducerFn: (state: State) => State) { + this._state.next(reducerFn(this._state.value)); + } +} diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.html b/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.html new file mode 100644 index 00000000..fdbf34b7 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.html @@ -0,0 +1,42 @@ +
+
+
+ + +
+ +
+ +
+
+ @for (item of items$ | async; track item.value) { +
+ + +
+ } +
+
+
+ diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.spec.ts b/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.spec.ts new file mode 100644 index 00000000..a8953f07 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.spec.ts @@ -0,0 +1,31 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { HostAttributeToken } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { TransferStore } from '../store/transfer.store'; +import { ItTransferListComponent } from './transfer-list.component'; + +describe('ItTransferListComponent', () => { + let component: ItTransferListComponent; + let fixture: ComponentFixture>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ItTransferListComponent], + providers: [ + TransferStore, + TranslateService, + { provide: new HostAttributeToken('title'), useValue: 'title' }, + { provide: new HostAttributeToken('sourceType'), useValue: 'source' }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ItTransferListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.ts b/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.ts new file mode 100644 index 00000000..3859a446 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer-list/transfer-list.component.ts @@ -0,0 +1,93 @@ +import { AsyncPipe, TitleCasePipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, ElementRef, HostAttributeToken, inject, ViewChild } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { combineLatest, distinctUntilChanged, map, shareReplay, skip, startWith, tap } from 'rxjs'; +import { ItAbstractComponent } from '../../../../abstracts/abstract.component'; +import { TransferStore } from '../store/transfer.store'; + +import { SourceType, TransferItem } from '../transfer.model'; + +interface SelectableTransferItem extends TransferItem { + selected: boolean; +} + +@Component({ + selector: 'it-transfer-list', + standalone: true, + imports: [AsyncPipe, TitleCasePipe], + templateUrl: './transfer-list.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ItTransferListComponent extends ItAbstractComponent { + /** + * Widget title + */ + readonly title = inject(new HostAttributeToken('title'), { optional: true }); + + readonly sourceType = inject(new HostAttributeToken('sourceType'), { optional: true }) as SourceType; + + private readonly items = this.store.selectItems(this.sourceType).pipe(distinctUntilChanged(), shareReplay()); + private readonly selected = this.store.selectSelectedItems(this.sourceType).pipe(distinctUntilChanged(), shareReplay()); + + readonly numberOfItems$ = this.items.pipe( + map(items => ({ length: items.length })), + startWith({ length: 0 }) + ); + readonly selectAllDisabled = this.items.pipe(map(items => items.length === 0)); + /** + * Items of the list + * @default [] + */ + readonly items$ = combineLatest([this.items, this.selected]).pipe( + map(([items, selected]) => + items.map(item => { + (item as SelectableTransferItem).selected = selected.has(item); + return item as SelectableTransferItem; + }) + ) + ); + + @ViewChild('selectAllCheckbox') + selectAllCheckboxRef!: ElementRef; + + readonly instanceId = this.getInstanceId(); + + constructor(private readonly store: TransferStore) { + super(); + this.onItemsUpdate(); + } + /** + * Checkbox selection click handler + */ + checkboxSelectionHandler(item: TransferItem) { + this.store.checkboxSelection(item, this.sourceType); + } + /** + * Checkbox select all selection handler + */ + checkboxSelectAllHandler(event: Event) { + const checked = ((event as PointerEvent).target as HTMLInputElement).checked; + this.store.selectAllSelection(checked, this.sourceType); + } + + /** + * Items update subscription + */ + private onItemsUpdate() { + this.items + .pipe( + takeUntilDestroyed(), + skip(1), + tap(() => { + if (this.selectAllCheckboxRef) { + this.selectAllCheckboxRef.nativeElement.checked = false; + } + }) + ) + .subscribe(); + } + + private getInstanceId() { + return Math.floor(Math.random() * 100000000).toString(); + } +} diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.html b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.html new file mode 100644 index 00000000..61f30baf --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.html @@ -0,0 +1,52 @@ +
+ @if (label) { + + } +
+
+ +
+ +
+ +
+ + + + Etichetta per freccia destra + + + + Etichetta for freccia sinistra + + + + Etichetta per icona di reset +
+
+
+ +
+
+
diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.spec.ts b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.spec.ts new file mode 100644 index 00000000..9e7120d1 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.spec.ts @@ -0,0 +1,34 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TranslateFakeLoader, TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; +import { TransferStore } from './store/transfer.store'; +import { ItTransferComponent } from './transfer.component'; + +describe('ItTransferComponent', () => { + let component: ItTransferComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + ItTransferComponent, + //https://stackoverflow.com/a/52461467/2642723 + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useClass: TranslateFakeLoader, + }, + }), + ], + providers: [TransferStore, TranslateService], + }).compileComponents(); + + fixture = TestBed.createComponent(ItTransferComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.ts b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.ts new file mode 100644 index 00000000..08c9801b --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.component.ts @@ -0,0 +1,157 @@ +import { AsyncPipe, NgClass } from '@angular/common'; +import { ChangeDetectionStrategy, Component, DestroyRef, EventEmitter, inject, Input, OnInit, Optional, Output, Self } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { FormControlName, NgControl, NgModel, ReactiveFormsModule } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; +import { tap } from 'rxjs'; +import { ItAbstractFormComponent } from '../../../abstracts/abstract-form.component'; +import { TransferStore } from './store/transfer.store'; +import { ItTransferListComponent } from './transfer-list/transfer-list.component'; +import { TransferItem } from './transfer.model'; + +/** + * Transfer + * @description Component that allows the creation of checkbox lists. + */ +@Component({ + selector: 'it-transfer', + standalone: true, + templateUrl: './transfer.component.html', + imports: [ItTransferListComponent, NgClass, AsyncPipe, ReactiveFormsModule], + providers: [TransferStore], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ItTransferComponent extends ItAbstractFormComponent implements OnInit { + /** + * The select options (left side) + */ + @Input() options = []; + /** + * The selected options (right side) + */ + @Input() selected = []; + /** + * Fired when there is a transfer, a backtransfer or a reset event + */ + @Output() readonly transferChanges = new EventEmitter[]>(); + + /** + * Enable transfer button + * @default false + */ + readonly transferEnabled = this.store.transferEnabled; + /** + * Enable backtransfer button + * @default false + */ + readonly backtransferEnabled = this.store.backtransferEnabled; + /** + * Enable reset button + * @default false + */ + readonly resetEnabled = this.store.resetEnabled; + + private readonly destroyRef = inject(DestroyRef); + + constructor( + @Self() + @Optional() + override readonly _ngControl: NgControl, + override readonly _translateService: TranslateService, + private readonly store: TransferStore + ) { + super(_translateService, _ngControl); + } + + override ngOnInit() { + super.ngOnInit(); + this.storeInit(); + this.onStoreValueChanged(); + } + + /** + * Transfer button click handler + */ + transferClickHandler(event: MouseEvent) { + this.buttonEventHandler(event, () => this.store.transfer()); + } + /** + * Transfer button keypress handler + */ + transferKeyPressHandler(event: KeyboardEvent) { + this.buttonEventHandler(event, () => this.store.transfer()); + } + /** + * Backtransfer button click handler + */ + backtransferClickHandler(event: MouseEvent) { + this.buttonEventHandler(event, () => this.store.backtransfer()); + } + /** + * Backtransfer button keypress handler + */ + backtransferKeyPressHandler(event: KeyboardEvent) { + this.buttonEventHandler(event, () => this.store.backtransfer()); + } + /** + * Reset button click handler + */ + resetClickHandler(event: MouseEvent) { + this.buttonEventHandler(event, () => this.store.reset()); + } + /** + * Reset button keypress handler + */ + resetKeyPressHandler(event: KeyboardEvent) { + this.buttonEventHandler(event, () => this.store.reset()); + } + + private buttonEventHandler(event: Event, updateStoreCb: () => void) { + event.preventDefault(); + updateStoreCb(); + } + + private storeInit() { + let target = []; + const ngControl = this._ngControl; + const isNgControlDefined = Boolean(this._ngControl); + + // if ngControl is defined, take values from it. Input() target will be ignored + if (isNgControlDefined) { + console.debug('ngControl instanceof NgModel:', ngControl instanceof NgModel); + console.debug('ngControl instanceof FormControlName:', ngControl instanceof FormControlName); + + // if ngControl is an ngModel (template-driven form use case), take values from it + if (ngControl instanceof NgModel) { + console.debug('ngControl instanceof NgModel'); + const model = (ngControl as NgModel).model; + target = Array.isArray(model) ? model : []; + } + + // if ngControl is an FormControlName (reactive form use case), take values from it + if (ngControl instanceof FormControlName) { + console.debug('ngControl instanceof FormControlName'); + const model = (ngControl as FormControlName).control.value; + target = Array.isArray(model) ? model : []; + } + + console.debug('ngControl is defined. Input() target will be ignored'); + } else if (this.selected && Array.isArray(this.selected)) { + target = [...this.selected]; + } + + console.debug('target:', this.selected, 'formControl:', this.control.value, 'ngModel:', this._ngControl); + this.store.init({ source: [...this.options], target }); + } + + private onStoreValueChanged() { + this.store.valueChanged + .pipe( + takeUntilDestroyed(this.destroyRef), + tap(value => this.writeValue(value as T)), + tap(value => this.onChange(value as T)), + tap(value => this.transferChanges.emit(value)) + ) + .subscribe(); + } +} diff --git a/projects/design-angular-kit/src/lib/components/form/transfer/transfer.model.ts b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.model.ts new file mode 100644 index 00000000..8d77bc3a --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/form/transfer/transfer.model.ts @@ -0,0 +1,13 @@ +//Qs +//Aria hidden? +//state management with service? +//interface? + +export interface TransferItem { + text: string; + value: ValueType; +} + +export type TransferItemSelection = Array>; + +export type SourceType = 'source' | 'target'; diff --git a/projects/design-angular-kit/src/public_api.ts b/projects/design-angular-kit/src/public_api.ts index b484a609..63fbfc95 100644 --- a/projects/design-angular-kit/src/public_api.ts +++ b/projects/design-angular-kit/src/public_api.ts @@ -2,45 +2,45 @@ * Public API Surface of design-angular-kit */ -export * from './lib/provide-design-angular-kit'; export * from './lib/design-angular-kit.module'; +export * from './lib/provide-design-angular-kit'; // Core components export * from './lib/components/core/accordion/accordion.component'; export * from './lib/components/core/alert/alert.component'; -export * from './lib/components/core/avatar/avatar.module'; -export * from './lib/components/core/avatar/avatar.directive'; export * from './lib/components/core/avatar/avatar-dropdown/avatar-dropdown.component'; export * from './lib/components/core/avatar/avatar-group/avatar-group.component'; +export * from './lib/components/core/avatar/avatar.directive'; +export * from './lib/components/core/avatar/avatar.module'; export * from './lib/components/core/badge/badge.directive'; export * from './lib/components/core/button/button.directive'; export * from './lib/components/core/callout/callout.component'; export * from './lib/components/core/card/card.component'; +export * from './lib/components/core/carousel/carousel-item/carousel-item.component'; export * from './lib/components/core/carousel/carousel.module'; export * from './lib/components/core/carousel/carousel/carousel.component'; -export * from './lib/components/core/carousel/carousel-item/carousel-item.component'; export * from './lib/components/core/chip/chip.component'; export * from './lib/components/core/collapse/collapse.component'; -export * from './lib/components/core/dimmer/dimmer.module'; -export * from './lib/components/core/dimmer/dimmer.component'; export * from './lib/components/core/dimmer/dimmer-buttons/dimmer-buttons.component'; export * from './lib/components/core/dimmer/dimmer-icon/dimmer-icon.component'; +export * from './lib/components/core/dimmer/dimmer.component'; +export * from './lib/components/core/dimmer/dimmer.module'; +export * from './lib/components/core/dropdown/dropdown-item/dropdown-item.component'; export * from './lib/components/core/dropdown/dropdown.module'; export * from './lib/components/core/dropdown/dropdown/dropdown.component'; -export * from './lib/components/core/dropdown/dropdown-item/dropdown-item.component'; export * from './lib/components/core/forward/forward.directive'; export * from './lib/components/core/link/link.component'; +export * from './lib/components/core/list/list-item/list-item.component'; export * from './lib/components/core/list/list.module'; export * from './lib/components/core/list/list/list.component'; -export * from './lib/components/core/list/list-item/list-item.component'; export * from './lib/components/core/modal/modal.component'; export * from './lib/components/core/notifications/notifications.component'; @@ -50,29 +50,29 @@ export * from './lib/components/core/progress-bar/progress-bar.component'; export * from './lib/components/core/progress-button/progress-button.component'; export * from './lib/components/core/spinner/spinner.component'; -export * from './lib/components/core/steppers/steppers.module'; export * from './lib/components/core/steppers/steppers-container/steppers-container.component'; export * from './lib/components/core/steppers/steppers-item/steppers-item.component'; +export * from './lib/components/core/steppers/steppers.module'; -export * from './lib/components/core/tab/tab.module'; export * from './lib/components/core/tab/tab-container/tab-container.component'; export * from './lib/components/core/tab/tab-item/tab-item.component'; +export * from './lib/components/core/tab/tab.module'; -export * from './lib/components/core/table/table.module'; -export * from './lib/components/core/table/table.component'; -export * from './lib/components/core/table/sort/sort.directive'; export * from './lib/components/core/table/sort/sort-header/sort-header.component'; +export * from './lib/components/core/table/sort/sort.directive'; +export * from './lib/components/core/table/table.component'; +export * from './lib/components/core/table/table.module'; -export * from './lib/components/core/timeline/timeline.module'; -export * from './lib/components/core/timeline/timeline.component'; export * from './lib/components/core/timeline/timeline-item/timeline-item.component'; +export * from './lib/components/core/timeline/timeline.component'; +export * from './lib/components/core/timeline/timeline.module'; export * from './lib/components/core/tooltip/tooltip.directive'; // Forms components -export * from './lib/components/form/form.module'; export * from './lib/components/form/autocomplete/autocomplete.component'; export * from './lib/components/form/checkbox/checkbox.component'; +export * from './lib/components/form/form.module'; export * from './lib/components/form/input/input.component'; export * from './lib/components/form/password-input/password-input.component'; export * from './lib/components/form/radio-button/radio-button.component'; @@ -80,6 +80,8 @@ export * from './lib/components/form/range/range.component'; export * from './lib/components/form/rating/rating.component'; export * from './lib/components/form/select/select.component'; export * from './lib/components/form/textarea/textarea.component'; +export * from './lib/components/form/transfer/transfer.component'; +export * from './lib/components/form/transfer/transfer.model'; export * from './lib/components/form/upload-drag-drop/upload-drag-drop.component'; export * from './lib/components/form/upload-file-list/upload-file-list.component'; @@ -87,16 +89,16 @@ export * from './lib/components/form/upload-file-list/upload-file-list.component export * from './lib/components/navigation/back-button/back-button.component'; export * from './lib/components/navigation/back-to-top/back-to-top.component'; -export * from './lib/components/navigation/breadcrumbs/breadcrumbs.module'; -export * from './lib/components/navigation/breadcrumbs/breadcrumb/breadcrumb.component'; export * from './lib/components/navigation/breadcrumbs/breadcrumb-item/breadcrumb-item.component'; +export * from './lib/components/navigation/breadcrumbs/breadcrumb/breadcrumb.component'; +export * from './lib/components/navigation/breadcrumbs/breadcrumbs.module'; export * from './lib/components/navigation/header/header.component'; export * from './lib/components/navigation/megamenu/megamenu.component'; +export * from './lib/components/navigation/navbar/navbar-item/navbar-item.component'; export * from './lib/components/navigation/navbar/navbar.module'; export * from './lib/components/navigation/navbar/navbar/navbar.component'; -export * from './lib/components/navigation/navbar/navbar-item/navbar-item.component'; export * from './lib/components/navigation/sidebar/sidebar.component'; @@ -123,9 +125,9 @@ export * from './lib/interfaces/icon'; export * from './lib/interfaces/utils'; // Utils -export * from './lib/utils/regex'; export * from './lib/utils/date-utils'; export * from './lib/utils/file-utils'; +export * from './lib/utils/regex'; // Validators export * from './lib/validators/it-validators'; diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index c6ee44db..ec7d72cb 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -68,6 +68,7 @@ const routes: Routes = [ { path: 'autocomplete', loadChildren: () => import('src/app/autocomplete/autocomplete.module').then(m => m.AutocompleteModule) }, { path: 'sidebar', loadChildren: () => import('src/app/sidebar/sidebar.module').then(m => m.SidebarModule) }, { path: 'timeline', loadChildren: () => import('src/app/timeline/timeline.module').then(m => m.TimelineModule) }, + { path: 'transfer', loadChildren: () => import('src/app/transfer/transfer.module').then(m => m.TransferModule) }, ], }, { path: 'error/not-found', component: ItErrorPageComponent, data: { errorCode: 404 } }, diff --git a/src/app/transfer/transfer-default-example/transfer-default-example.component.html b/src/app/transfer/transfer-default-example/transfer-default-example.component.html new file mode 100644 index 00000000..2fa06b68 --- /dev/null +++ b/src/app/transfer/transfer-default-example/transfer-default-example.component.html @@ -0,0 +1,9 @@ +

Esempio senza form

+
+
+
+
Esempio di default
+ +
+
+
diff --git a/src/app/transfer/transfer-default-example/transfer-default-example.component.ts b/src/app/transfer/transfer-default-example/transfer-default-example.component.ts new file mode 100644 index 00000000..33c9cd35 --- /dev/null +++ b/src/app/transfer/transfer-default-example/transfer-default-example.component.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core'; +import { TransferItem } from 'projects/design-angular-kit/src/public_api'; + +@Component({ + selector: 'it-transfer-default-example', + templateUrl: './transfer-default-example.component.html', +}) +export class TransferDefaultExampleComponent { + readonly options: TransferItem[] = [ + { + text: 'Item 1', + value: 1, + }, + { + text: 'Item 2', + value: 2, + }, + ]; + readonly selected: TransferItem[] = [ + { + text: 'Item 3', + value: 3, + }, + ]; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + transferChangesHandler(_: TransferItem[]) { + // handle changing data + } +} diff --git a/src/app/transfer/transfer-examples/transfer-examples.component.tpl b/src/app/transfer/transfer-examples/transfer-examples.component.tpl new file mode 100644 index 00000000..2b02fc50 --- /dev/null +++ b/src/app/transfer/transfer-examples/transfer-examples.component.tpl @@ -0,0 +1,37 @@ +{% from "../../macro.template.njk" import sanitize as sanitize %} + +{% set html %} + {% include "../transfer-default-example/transfer-default-example.component.html" %} +{% endset %} + +{% set typescript %} + {% include "../transfer-default-example/transfer-default-example.component.ts" %} +{% endset %} + + + + + +{% set html %} + {% include "../transfer-template-form-example/transfer-template-form-example.component.html" %} +{% endset %} + +{% set typescript %} + {% include "../transfer-template-form-example/transfer-template-form-example.component.ts" %} +{% endset %} + + + + + +{% set html %} + {% include "../transfer-reactive-form-example/transfer-reactive-form-example.component.html" %} +{% endset %} + +{% set typescript %} + {% include "../transfer-reactive-form-example/transfer-reactive-form-example.component.ts" %} +{% endset %} + + + + \ No newline at end of file diff --git a/src/app/transfer/transfer-examples/transfer-examples.component.ts b/src/app/transfer/transfer-examples/transfer-examples.component.ts new file mode 100644 index 00000000..1658cc03 --- /dev/null +++ b/src/app/transfer/transfer-examples/transfer-examples.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'it-transfer-examples', + templateUrl: './transfer-examples.component.html', +}) +export class TransferExamplesComponent {} diff --git a/src/app/transfer/transfer-index/transfer-index.component.html b/src/app/transfer/transfer-index/transfer-index.component.html new file mode 100644 index 00000000..acf170b1 --- /dev/null +++ b/src/app/transfer/transfer-index/transfer-index.component.html @@ -0,0 +1,13 @@ +

Transfer

+

Componente che consente la creazione di liste di checkbox

+
+ + + + + + +

ItTransferComponent

+ +
+
diff --git a/src/app/transfer/transfer-index/transfer-index.component.ts b/src/app/transfer/transfer-index/transfer-index.component.ts new file mode 100644 index 00000000..b837f641 --- /dev/null +++ b/src/app/transfer/transfer-index/transfer-index.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import Documentation from '../../../assets/documentation.json'; + +@Component({ + selector: 'it-transfer-index', + templateUrl: './transfer-index.component.html', +}) +export class TransferIndexComponent { + component: any; + + constructor() { + this.component = (Documentation).components.find(component => component.name === 'ItTransferComponent'); + } +} diff --git a/src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.html b/src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.html new file mode 100644 index 00000000..68e80434 --- /dev/null +++ b/src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.html @@ -0,0 +1,11 @@ +

Esempio con Reactive Form

+
+
+
+
FormGroup
+
+ +
+
+
+
diff --git a/src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.ts b/src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.ts new file mode 100644 index 00000000..c3c4b008 --- /dev/null +++ b/src/app/transfer/transfer-reactive-form-example/transfer-reactive-form-example.component.ts @@ -0,0 +1,30 @@ +import { Component, inject } from '@angular/core'; +import { FormBuilder } from '@angular/forms'; +import { TransferItem } from 'projects/design-angular-kit/src/public_api'; + +@Component({ + selector: 'it-transfer-reactive-form-example', + templateUrl: './transfer-reactive-form-example.component.html', +}) +export class TransferReactiveFormExampleComponent { + readonly options: TransferItem[] = [ + { + text: 'Item 1', + value: 1, + }, + { + text: 'Item 2', + value: 2, + }, + ]; + readonly selected: TransferItem[] = [ + { + text: 'Item 3', + value: 3, + }, + ]; + + readonly formGroup = inject(FormBuilder).group({ + transfer: [this.selected], + }); +} diff --git a/src/app/transfer/transfer-routing.module.ts b/src/app/transfer/transfer-routing.module.ts new file mode 100644 index 00000000..cd058c97 --- /dev/null +++ b/src/app/transfer/transfer-routing.module.ts @@ -0,0 +1,16 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { TransferIndexComponent } from './transfer-index/transfer-index.component'; + +const routes = [ + { + path: '', + component: TransferIndexComponent, + }, +] satisfies Routes; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class TransferRoutingModule {} diff --git a/src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.html b/src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.html new file mode 100644 index 00000000..7311b2d2 --- /dev/null +++ b/src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.html @@ -0,0 +1,9 @@ +

Esempio con Template Form

+
+
+
+
NgModel
+ +
+
+
diff --git a/src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.ts b/src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.ts new file mode 100644 index 00000000..b18b4e35 --- /dev/null +++ b/src/app/transfer/transfer-template-form-example/transfer-template-form-example.component.ts @@ -0,0 +1,27 @@ +import { Component } from '@angular/core'; +import { TransferItem } from 'projects/design-angular-kit/src/public_api'; + +@Component({ + selector: 'it-transfer-template-form-example', + templateUrl: './transfer-template-form-example.component.html', +}) +export class TransferTemplateFormExampleComponent { + readonly options: TransferItem[] = [ + { + text: 'Item 1', + value: 1, + }, + { + text: 'Item 2', + value: 2, + }, + ]; + readonly selected: TransferItem[] = [ + { + text: 'Item 3', + value: 3, + }, + ]; + + transferModel = this.selected; +} diff --git a/src/app/transfer/transfer.module.ts b/src/app/transfer/transfer.module.ts new file mode 100644 index 00000000..e54fca8c --- /dev/null +++ b/src/app/transfer/transfer.module.ts @@ -0,0 +1,22 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { SharedModule } from '../shared/shared.module'; +import { TransferDefaultExampleComponent } from './transfer-default-example/transfer-default-example.component'; +import { TransferExamplesComponent } from './transfer-examples/transfer-examples.component'; +import { TransferIndexComponent } from './transfer-index/transfer-index.component'; +import { TransferReactiveFormExampleComponent } from './transfer-reactive-form-example/transfer-reactive-form-example.component'; +import { TransferRoutingModule } from './transfer-routing.module'; +import { TransferTemplateFormExampleComponent } from './transfer-template-form-example/transfer-template-form-example.component'; + +@NgModule({ + declarations: [ + TransferIndexComponent, + TransferDefaultExampleComponent, + TransferTemplateFormExampleComponent, + TransferReactiveFormExampleComponent, + TransferExamplesComponent, + ], + imports: [TransferRoutingModule, SharedModule, FormsModule, ReactiveFormsModule, CommonModule], +}) +export class TransferModule {} diff --git a/src/assets/table-of-content.json b/src/assets/table-of-content.json index 172e5c26..cb284d99 100644 --- a/src/assets/table-of-content.json +++ b/src/assets/table-of-content.json @@ -186,6 +186,10 @@ { "label": "Timeline", "link": "/componenti/timeline" + }, + { + "label": "Transfer", + "link": "/componenti/transfer" } ] } From 1b65209414a8fcdba00bb3ae5fd3b5031570f06c Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 10:09:04 +0100 Subject: [PATCH 02/21] fix: groups for radios toggles and checkboxes --- .../button-example-color-dark.component.scss | 4 +- .../button-example-color.component.scss | 4 +- .../button-example-size.component.scss | 4 +- .../button-examples.component.scss | 4 +- .../checkbox-example-group.component.html | 35 ++++++++------- .../checkbox-example-group.component.scss | 2 - .../checkbox-example-inline.component.html | 9 ++-- .../checkbox-example-inline.component.scss | 2 - .../checkbox-example.component.html | 21 +++++---- .../checkbox-example.component.scss | 2 - .../popover-examples.component.scss | 11 +++-- .../progress-bar-example.component.scss | 5 +-- .../radio-checked-example.component.html | 10 +++-- .../radio-example.component.html | 45 ++++++++++++------- .../source-display.component.scss | 6 +-- .../table-of-content-item.component.scss | 31 +++++++------ .../table-of-content.component.scss | 10 ++--- .../toggle-example.component.html | 7 +-- .../toggle-example.component.scss | 9 ++-- 19 files changed, 115 insertions(+), 106 deletions(-) diff --git a/src/app/button/button-example-color-dark/button-example-color-dark.component.scss b/src/app/button/button-example-color-dark/button-example-color-dark.component.scss index ac902f23..f12efc71 100644 --- a/src/app/button/button-example-color-dark/button-example-color-dark.component.scss +++ b/src/app/button/button-example-color-dark/button-example-color-dark.component.scss @@ -1,4 +1,4 @@ button { - margin: 4px 8px; - width: 200px; + margin: 4px 8px; + width: 200px; } diff --git a/src/app/button/button-example-color/button-example-color.component.scss b/src/app/button/button-example-color/button-example-color.component.scss index ac902f23..f12efc71 100644 --- a/src/app/button/button-example-color/button-example-color.component.scss +++ b/src/app/button/button-example-color/button-example-color.component.scss @@ -1,4 +1,4 @@ button { - margin: 4px 8px; - width: 200px; + margin: 4px 8px; + width: 200px; } diff --git a/src/app/button/button-example-size/button-example-size.component.scss b/src/app/button/button-example-size/button-example-size.component.scss index ac902f23..f12efc71 100644 --- a/src/app/button/button-example-size/button-example-size.component.scss +++ b/src/app/button/button-example-size/button-example-size.component.scss @@ -1,4 +1,4 @@ button { - margin: 4px 8px; - width: 200px; + margin: 4px 8px; + width: 200px; } diff --git a/src/app/button/button-examples/button-examples.component.scss b/src/app/button/button-examples/button-examples.component.scss index ac902f23..f12efc71 100644 --- a/src/app/button/button-examples/button-examples.component.scss +++ b/src/app/button/button-examples/button-examples.component.scss @@ -1,4 +1,4 @@ button { - margin: 4px 8px; - width: 200px; + margin: 4px 8px; + width: 200px; } diff --git a/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.html b/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.html index 03e52e14..0a0eb30f 100644 --- a/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.html +++ b/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.html @@ -1,20 +1,23 @@

Gruppi

-

- - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero - - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero. Lorem ipsum dolor sit amet, consectetur adipiscing - elit. Maecenas molestie liber - - - - Terza checkbox raggruppata +

+
+ Gruppo di checkbox + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero. Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Maecenas molestie liber + + + + Terza checkbox raggruppata - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero. Lorem ipsum dolor sit amet, consectetur adipiscing - elit. Maecenas molestie libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero - -

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero. Lorem ipsum dolor sit amet, consectetur + adipiscing elit. Maecenas molestie libero. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas molestie libero + +
+
diff --git a/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.scss b/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.scss index 139597f9..e69de29b 100644 --- a/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.scss +++ b/src/app/checkbox/checkbox-example-group/checkbox-example-group.component.scss @@ -1,2 +0,0 @@ - - diff --git a/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.html b/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.html index 4af32c12..e18e6183 100644 --- a/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.html +++ b/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.html @@ -1,6 +1,9 @@

Inline

- - - +
+ Gruppo di checkbox + + + +
diff --git a/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.scss b/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.scss index 139597f9..e69de29b 100644 --- a/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.scss +++ b/src/app/checkbox/checkbox-example-inline/checkbox-example-inline.component.scss @@ -1,2 +0,0 @@ - - diff --git a/src/app/checkbox/checkbox-example/checkbox-example.component.html b/src/app/checkbox/checkbox-example/checkbox-example.component.html index 599d2145..c3d2e95b 100644 --- a/src/app/checkbox/checkbox-example/checkbox-example.component.html +++ b/src/app/checkbox/checkbox-example/checkbox-example.component.html @@ -1,16 +1,19 @@

Configurazione checkbox

-

- - - - -

- +
+
+ Gruppo di checkbox + + + + +
+
+

Risultato

-

+

@@ -19,5 +22,5 @@

Risultato

Sono una checkbox
-

+
diff --git a/src/app/checkbox/checkbox-example/checkbox-example.component.scss b/src/app/checkbox/checkbox-example/checkbox-example.component.scss index 139597f9..e69de29b 100644 --- a/src/app/checkbox/checkbox-example/checkbox-example.component.scss +++ b/src/app/checkbox/checkbox-example/checkbox-example.component.scss @@ -1,2 +0,0 @@ - - diff --git a/src/app/popover/popover-examples/popover-examples.component.scss b/src/app/popover/popover-examples/popover-examples.component.scss index 67b7c114..80895767 100644 --- a/src/app/popover/popover-examples/popover-examples.component.scss +++ b/src/app/popover/popover-examples/popover-examples.component.scss @@ -1,7 +1,6 @@ .example-section { - display: flex; - align-content: center; - align-items: center; - height: 60px; - } - \ No newline at end of file + display: flex; + align-content: center; + align-items: center; + height: 60px; +} diff --git a/src/app/progress-bar/progress-bar-example/progress-bar-example.component.scss b/src/app/progress-bar/progress-bar-example/progress-bar-example.component.scss index b0b9c35a..e43b83aa 100644 --- a/src/app/progress-bar/progress-bar-example/progress-bar-example.component.scss +++ b/src/app/progress-bar/progress-bar-example/progress-bar-example.component.scss @@ -1,5 +1,4 @@ .example-section { - align-content: center; - align-items: center; + align-content: center; + align-items: center; } - \ No newline at end of file diff --git a/src/app/radio/radio-checked-example/radio-checked-example.component.html b/src/app/radio/radio-checked-example/radio-checked-example.component.html index 2db87f96..5414aec7 100644 --- a/src/app/radio/radio-checked-example/radio-checked-example.component.html +++ b/src/app/radio/radio-checked-example/radio-checked-example.component.html @@ -1,12 +1,14 @@

Radio checked

- +
+ Seleziona la nazionalità + - + - + +
-
Nazione selezionata: {{ selectedNation }}
diff --git a/src/app/radio/radio-example/radio-example.component.html b/src/app/radio/radio-example/radio-example.component.html index 81857164..23efe00a 100644 --- a/src/app/radio/radio-example/radio-example.component.html +++ b/src/app/radio/radio-example/radio-example.component.html @@ -7,25 +7,33 @@

Configurazione Radio button

Risultato

- +
+ Seleziona il tuo colore preferito + +
Colore selezionato: {{ selectedColor }}

Radio con link

- - Label con link - - - Altra label con link - +
+ Gruppo di radio + + Label con link + + + Altra label con link + +
Link selezionato: {{ link }} @@ -36,10 +44,13 @@

Radio con link

Radio in Reactive Form

Qual è il tuo sesso?

- - - - +
+ Gruppo di radio + + + + +
diff --git a/src/app/shared/source-display/source-display.component.scss b/src/app/shared/source-display/source-display.component.scss index a5ac1fdd..22b3ba22 100644 --- a/src/app/shared/source-display/source-display.component.scss +++ b/src/app/shared/source-display/source-display.component.scss @@ -1,5 +1,5 @@ .bd-example { - border-top: none; - margin-top: auto; - padding-top: 20px; + border-top: none; + margin-top: auto; + padding-top: 20px; } diff --git a/src/app/table-of-content-item/table-of-content-item.component.scss b/src/app/table-of-content-item/table-of-content-item.component.scss index b9a5d258..c33e8a96 100644 --- a/src/app/table-of-content-item/table-of-content-item.component.scss +++ b/src/app/table-of-content-item/table-of-content-item.component.scss @@ -20,10 +20,10 @@ .bd-toc-link { display: block; - padding: .5rem 1rem; + padding: 0.5rem 1rem; @include media-breakpoint-up(md) { - padding-top: .25rem; - padding-bottom: .25rem; + padding-top: 0.25rem; + padding-bottom: 0.25rem; } color: $gray-800; @@ -33,33 +33,32 @@ } .bd-toc-item { - background-color: $white; - &.active { - > .bd-toc-link { - color: $primary; - font-weight: 600; + background-color: $white; + &.active { + > .bd-toc-link { + color: $primary; + font-weight: 600; - &:hover { - background-color: transparent; - } + &:hover { + background-color: transparent; } + } - .bd-sidenav { - display: block; - } + .bd-sidenav { + display: block; } + } } .bd-sidenav { display: none; } - // All levels of nav .nav { > li > a { display: inline-block; - padding: .25rem .5rem .25rem 1.5rem; + padding: 0.25rem 0.5rem 0.25rem 1.5rem; font-size: 16px; color: $gray-800; &:hover { diff --git a/src/app/table-of-content/table-of-content.component.scss b/src/app/table-of-content/table-of-content.component.scss index 33c77c07..435a9853 100644 --- a/src/app/table-of-content/table-of-content.component.scss +++ b/src/app/table-of-content/table-of-content.component.scss @@ -4,7 +4,7 @@ .bd-sidebar { order: 0; - border-bottom: 1px solid rgba(0, 0, 0, .1); + border-bottom: 1px solid rgba(0, 0, 0, 0.1); @include media-breakpoint-up(md) { @supports (position: sticky) { position: sticky; @@ -12,12 +12,12 @@ height: calc(100vh - 5rem); overflow-y: auto; } - border-right: 1px solid rgba(0, 0, 0, .1); + border-right: 1px solid rgba(0, 0, 0, 0.1); } @include media-breakpoint-up(md) { padding: 1.5rem 0; - border-right: 1px solid rgba(0, 0, 0, .1); + border-right: 1px solid rgba(0, 0, 0, 0.1); } @include media-breakpoint-up(xl) { @@ -40,7 +40,3 @@ display: block !important; } } - - - - diff --git a/src/app/toggle/toggle-example/toggle-example.component.html b/src/app/toggle/toggle-example/toggle-example.component.html index 3420e4bf..711aa134 100644 --- a/src/app/toggle/toggle-example/toggle-example.component.html +++ b/src/app/toggle/toggle-example/toggle-example.component.html @@ -1,10 +1,11 @@

Configurazione toggle

-

+

+ Seleziona le caratteristiche -

- +
+

Risultato

diff --git a/src/app/toggle/toggle-example/toggle-example.component.scss b/src/app/toggle/toggle-example/toggle-example.component.scss index ed6303f7..80895767 100644 --- a/src/app/toggle/toggle-example/toggle-example.component.scss +++ b/src/app/toggle/toggle-example/toggle-example.component.scss @@ -1,7 +1,6 @@ .example-section { - display: flex; - align-content: center; - align-items: center; - height: 60px; + display: flex; + align-content: center; + align-items: center; + height: 60px; } - \ No newline at end of file From 7e81151da547a6e167101749d1aeb0395da03515 Mon Sep 17 00:00:00 2001 From: Valerio Date: Thu, 31 Oct 2024 13:27:42 +0100 Subject: [PATCH 03/21] feat: add angular schematics --- .devcontainer/scripts/postStartCommand.sh | 0 .github/workflows/run-tests.yml | 1 + README.EN.md | 110 ++++++------ README.md | 126 ++++++------- package-lock.json | 167 +++++++++++++++++- package.json | 7 +- postbuildSchematics.js | 23 +++ projects/design-angular-kit/package.json | 14 +- .../schematics/collection.json | 19 ++ .../schematics/ng-add/index.spec.ts | 45 +++++ .../schematics/ng-add/index.ts | 36 ++++ .../rules/setup-project/add-animations.ts | 7 + .../ng-add/rules/setup-project/add-assets.ts | 45 +++++ .../setup-project/add-design-angular-kit.ts | 40 +++++ .../rules/setup-project/add-http-client.ts | 7 + .../setup-project/add-import-to-style-file.ts | 120 +++++++++++++ .../rules/setup-project/add-localisation.ts | 45 +++++ .../setup-project/angular-json-helper.ts | 11 ++ .../ng-add/rules/setup-project/exceptions.ts | 13 ++ .../ng-add/rules/setup-project/index.ts | 6 + .../schematics/ng-add/schema.json | 16 ++ .../schematics/ng-add/schema.ts | 3 + .../schematics/ng-add/setup-project.spec.ts | 166 +++++++++++++++++ .../schematics/ng-add/setup-project.ts | 31 ++++ .../schematics/ng-add/utils.ts | 71 ++++++++ .../schematics/ng-add/versions-helper.ts | 41 +++++ .../src/lib/provide-design-angular-kit.ts | 8 +- .../tsconfig.schematics.json | 25 +++ 28 files changed, 1083 insertions(+), 120 deletions(-) mode change 100755 => 100644 .devcontainer/scripts/postStartCommand.sh create mode 100644 postbuildSchematics.js create mode 100644 projects/design-angular-kit/schematics/collection.json create mode 100644 projects/design-angular-kit/schematics/ng-add/index.spec.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/index.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-animations.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-assets.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-design-angular-kit.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-http-client.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-import-to-style-file.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-localisation.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/angular-json-helper.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/exceptions.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/rules/setup-project/index.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/schema.json create mode 100644 projects/design-angular-kit/schematics/ng-add/schema.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/setup-project.spec.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/setup-project.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/utils.ts create mode 100644 projects/design-angular-kit/schematics/ng-add/versions-helper.ts create mode 100644 projects/design-angular-kit/tsconfig.schematics.json diff --git a/.devcontainer/scripts/postStartCommand.sh b/.devcontainer/scripts/postStartCommand.sh old mode 100755 new mode 100644 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 87c17ac8..b32176e0 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -24,4 +24,5 @@ jobs: - run: npm run build - run: npm run build:lib - run: npm run test + - run: npm run schematics:test - uses: codecov/codecov-action@v2 diff --git a/README.EN.md b/README.EN.md index 47399c47..43cd5ab7 100644 --- a/README.EN.md +++ b/README.EN.md @@ -64,10 +64,25 @@ npm install design-angular-kit --save Choose the version corresponding to your Angular version: | Angular | design-angular-kit | -|---------|--------------------| +| ------- | ------------------ | | 18+ | v1.1.0 + | | 17+ | v1.0.0 + | +Alternatively, you can run the command + +```sh +ng add design-angular-kit --project +``` + +He will consecutively perform: + +- choose the correct Angular version +- install the dep +- add the dep to package.json +- application configuration + +The instruction of the next chapter, **Configuration**, will be executed automatically, except for the customization of \_i18n. + ## Configuration ### App configuration @@ -84,11 +99,8 @@ initialise the library's functionality. import { provideDesignAngularKit } from 'design-angular-kit'; export const appConfig: ApplicationConfig = { - providers: [ - ... - provideDesignAngularKit(), - ] -} + providers: [...provideDesignAngularKit()], +}; ``` #### Modular application @@ -100,28 +112,21 @@ using the `forRoot` method in order to initialise the library functionality and import { DesignAngularKitModule } from 'design-angular-kit'; @NgModule({ - imports: [ - ... - DesignAngularKitModule.forRoot() - ] + imports: [...DesignAngularKitModule.forRoot()], }) -export class AppModule { } +export class AppModule {} ``` Uses the `forChild` method when importing the `DesignAngularKitModule` into other modules of the application to **import all library components**. - ```typescript import { DesignAngularKitModule } from 'design-angular-kit'; @NgModule({ - imports: [ - ... - DesignAngularKitModule.forChild() - ], + imports: [...DesignAngularKitModule.forChild()], exports: [DesignAngularKitModule], }) -export class SharedModule { } +export class SharedModule {} ``` #### Hybrid application @@ -134,11 +139,9 @@ import { provideDesignAngularKit } from 'design-angular-kit'; @NgModule({ imports: [], - providers: [ - provideDesignAngularKit(), - ] + providers: [provideDesignAngularKit()], }) -export class AppModule { } +export class AppModule {} ``` #### Configuration Parameters @@ -161,7 +164,7 @@ const initConfig: DesignAngularKitConfig | undefined = { * @default true */ loadFont: boolean | undefined, - + ... }; @@ -171,11 +174,12 @@ DesignAngularKitModule.forRoot(initConfig) ``` ### Importing bootstrap styles + Configure the required styles in the file `styles.scss`. Import the SCSS library as shown in the example below. ```scss // Importing bootstrap-italia SCSS library -@import "bootstrap-italia/src/scss/bootstrap-italia"; +@import 'bootstrap-italia/src/scss/bootstrap-italia'; ```

@@ -190,7 +194,6 @@ The use of blue #0066CC should, however, be reserved for the central administrat State, and thus one may find oneself in the position of having to customise the values of the variables colour of Bootstrap Italy, setting new values for their own needs. - This colour and the other tones are generated from the HSB triad, so the variables primary-h, primary-s and primary-b must be modified. To obtain the correspondence between the hexadecimal value of the colour and HSB, one can use the rgb.to portal, e.g. https://rgb.to/0066CC. @@ -213,6 +216,7 @@ $font-family-monospace: 'Custom Font', 'Courier New', Courier, monospace; // Importing bootstrap-italia SCSS library @import 'bootstrap-italia/src/scss/bootstrap-italia'; ``` +
### Icon and asset support @@ -250,6 +254,7 @@ Edit your `angular.json` by adding: ] } ``` + You can use the localised labels of the `design-angular-kit` library in your application, e.g. `{{'en.errors.required-field' | translate}}`. [See our labels](projects/design-angular-kit/assets/i18n/en.json) #### Existing location @@ -257,7 +262,6 @@ You can use the localised labels of the `design-angular-kit` library in your app If you already use localisation files in your app, you can use the library [ngx-translate-multi-http-loader](https://www.npmjs.com/package/ngx-translate-multi-http-loader) to load both the app's localisation files and those of the `design-angular-kit` library - **Using the `provideDesignAngularKit` function:** ```typescript @@ -269,16 +273,18 @@ import { provideDesignAngularKit } from 'design-angular-kit'; provideDesignAngularKit({ translateLoader: (itPrefix: string, itSuffix: string) => ({ provide: TranslateLoader, - useFactory: (http: HttpBackend) => new MultiTranslateHttpLoader(http, [ - { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file - { prefix: './assets/i18n/' }, // Your i18n location - ]), - deps: [HttpBackend] + useFactory: (http: HttpBackend) => + new MultiTranslateHttpLoader(http, [ + { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file + { prefix: './assets/i18n/' }, // Your i18n location + ]), + deps: [HttpBackend], }), -}) +}); ``` **Using the `DesignAngularKitModule`:** + ```typescript import { HttpBackend } from '@angular/common/http'; import { TranslateLoader } from '@ngx-translate/core'; @@ -288,18 +294,20 @@ import { DesignAngularKitModule } from 'design-angular-kit'; DesignAngularKitModule.forRoot({ translateLoader: (itPrefix: string, itSuffix: string) => ({ provide: TranslateLoader, - useFactory: (http: HttpBackend) => new MultiTranslateHttpLoader(http, [ - { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file - { prefix: './assets/i18n/' }, // Your i18n location - ]), - deps: [HttpBackend] + useFactory: (http: HttpBackend) => + new MultiTranslateHttpLoader(http, [ + { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file + { prefix: './assets/i18n/' }, // Your i18n location + ]), + deps: [HttpBackend], }), -}) +}); ``` #### Localisation customisation If you want to customise our labels: + - Do not include i18n support in your `angular.json`. - Create your custom location files in your `assets/bootstrap-italia/i18n/` folder (create the path if it does not exist) - The json must have [this format](projects/design-angular-kit/assets/i18n/en.json). @@ -317,13 +325,12 @@ import { ItAlertComponent, ItPaginationComponent, ItBreadcrumbsModule } from 'de @NgModule({ imports: [ - ItAlertComponent, - ItPaginationComponent, - ItBreadcrumbsModule // Includes ItBreadcrumbComponent and ItBreadcrumbItemComponent + ItAlertComponent, + ItPaginationComponent, + ItBreadcrumbsModule, // Includes ItBreadcrumbComponent and ItBreadcrumbItemComponent ], }) -export class YourAppModule { -} +export class YourAppModule {} ``` ```typescript @@ -333,19 +340,18 @@ import { ItAlertComponent, ItPaginationComponent, ItBreadcrumbsModule } from 'de selector: 'app-product', standalone: true, imports: [ItAlertComponent, ItPaginationComponent, ItBreadcrumbsModule], - templateUrl: './product.component.html' + templateUrl: './product.component.html', }) -export class ProductComponent { -} +export class ProductComponent {} ``` ## How to contribute 💙 👉🏻 You can contribute to the library in various ways: -* With your own code, taking charge of an issue among those open and not already assigned among [the issues](https://github.com/italia/design-angular-kit/issues) of Angular Kit (a comment on the issue is also sufficient to notify the willingness to take charge). -* By reporting bugs or improvements to the [official repository](https://github.com/italia/design-angular-kit/) of Angular Kit. -* By writing to us on Slack's [dedicated channel](https://developersitalia.slack.com/messages/C04H3C19D52/). +- With your own code, taking charge of an issue among those open and not already assigned among [the issues](https://github.com/italia/design-angular-kit/issues) of Angular Kit (a comment on the issue is also sufficient to notify the willingness to take charge). +- By reporting bugs or improvements to the [official repository](https://github.com/italia/design-angular-kit/) of Angular Kit. +- By writing to us on Slack's [dedicated channel](https://developersitalia.slack.com/messages/C04H3C19D52/). ## How to contribute the code @@ -438,9 +444,9 @@ npm run test Special thanks to those who made the development of this library possible! -[![Antonino Bonanno](https://github.com/AntoninoBonanno.png?size=100)](https://github.com/AntoninoBonanno) | [![Cristian Borelli](https://github.com/cri99.png?size=100)](https://github.com/cri99) | [![Alessio Napolitano](https://github.com/alenap93.png?size=100)](https://github.com/alenap93) | ---- | --- | --- | -Antonino Bonanno | Cristian Borelli | Alessio Napolitano | +| [![Antonino Bonanno](https://github.com/AntoninoBonanno.png?size=100)](https://github.com/AntoninoBonanno) | [![Cristian Borelli](https://github.com/cri99.png?size=100)](https://github.com/cri99) | [![Alessio Napolitano](https://github.com/alenap93.png?size=100)](https://github.com/alenap93) | +| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| Antonino Bonanno | Cristian Borelli | Alessio Napolitano | and thanks to [NetService team](https://www.net-serv.it/): @@ -448,7 +454,7 @@ and thanks to [NetService team](https://www.net-serv.it/): --- -All contributors (*made with [contributors-img](https://contrib.rocks)*) +All contributors (_made with [contributors-img](https://contrib.rocks)_) diff --git a/README.md b/README.md index 0bfb9dd7..c024c206 100644 --- a/README.md +++ b/README.md @@ -64,10 +64,25 @@ npm install design-angular-kit --save Scegli la versione corrispondente alla tua versione Angular: | Angular | design-angular-kit | -|---------|--------------------| +| ------- | ------------------ | | 18+ | v1.1.0 + | | 17+ | v1.0.0 + | +In alternativa, puoi lanciar il comando + +```sh +ng add design-angular-kit --project +``` + +Eseguirà in serie: + +- scelta della corretta versione da installare +- installazione della dipendenza +- aggiunta della dipendenza al package.json +- configurazione dell'applicazione + +Le istruzioni del paragrafo successivo, **Configurazione**, verranno eseguite automaticamente, eccetto per la personalizzazione della configurazione di _i18n_. + ## Configurazione ### Configurazione app @@ -84,49 +99,39 @@ poter inizializzare le funzionalità della libreria. import { provideDesignAngularKit } from 'design-angular-kit'; export const appConfig: ApplicationConfig = { - providers: [ - ... - provideDesignAngularKit(), - ] -} + providers: [...provideDesignAngularKit()], +}; ``` #### Applicazione modulare -È necessario importare `DesignAngularKitModule` all'interno del modulo principale dell'applicazione (solitamente denominato **AppModule**) +È necessario importare `DesignAngularKitModule` all'interno del modulo principale dell'applicazione (solitamente denominato **AppModule**) utilizzando il metodo `forRoot` per poter inizializzare le funzionalità della libreria e **importare tutti i componenti**. ```typescript import { DesignAngularKitModule } from 'design-angular-kit'; @NgModule({ - imports: [ - ... - DesignAngularKitModule.forRoot() - ] + imports: [...DesignAngularKitModule.forRoot()], }) -export class AppModule { } +export class AppModule {} ``` Utilizza il metodo `forChild` durante l'importazione del modulo `DesignAngularKitModule` in altri moduli dell'applicazione per **importare tutti i componenti** della libreria. - ```typescript import { DesignAngularKitModule } from 'design-angular-kit'; @NgModule({ - imports: [ - ... - DesignAngularKitModule.forChild() - ], + imports: [...DesignAngularKitModule.forChild()], exports: [DesignAngularKitModule], }) -export class SharedModule { } +export class SharedModule {} ``` #### Applicazione ibrida -Se nella tua applicazione è presente il modulo **AppModule** ma vuoi utilizzare i nostri **componenti con la configurazione _standalone_**, +Se nella tua applicazione è presente il modulo **AppModule** ma vuoi utilizzare i nostri **componenti con la configurazione _standalone_**, utilizza la funzione `provideDesignAngularKit` all'interno del modulo principale dell'applicazione per poter inizializzare le funzionalità della libreria. ```typescript @@ -134,11 +139,9 @@ import { provideDesignAngularKit } from 'design-angular-kit'; @NgModule({ imports: [], - providers: [ - provideDesignAngularKit(), - ] + providers: [provideDesignAngularKit()], }) -export class AppModule { } +export class AppModule {} ``` #### Parametri di configurazione @@ -161,7 +164,7 @@ const initConfig: DesignAngularKitConfig | undefined = { * @default true */ loadFont: boolean | undefined, - + ... }; @@ -171,11 +174,12 @@ DesignAngularKitModule.forRoot(initConfig) ``` ### Importazione stili bootstrap-italia + Configura gli stili richiesti nel file `styles.scss`. Importa la libreria SCSS come mostrato nell'esempio qui sotto. ```scss // Importazione libreria SCSS di bootstrap-italia -@import "bootstrap-italia/src/scss/bootstrap-italia"; +@import 'bootstrap-italia/src/scss/bootstrap-italia'; ```
@@ -190,7 +194,6 @@ L’utilizzo del blu #0066CC dovrebbe però essere riservato alle amministrazion Stato, e quindi ci si può trovare nella condizione di dover personalizzare i valori delle variabili colore di Bootstrap Italia, impostando nuovi valori per le proprie necessità. - Questo colore e le altre tonalità vengono generate a partire dalla terna HSB, pertanto occorre modificare le variabili primary-h, primary-s e primary-b. Per avere la corrispondenza tra valore esadecimale del colore e HSB si può utilizzare il portale rgb.to, ad esempio https://rgb.to/0066CC. @@ -213,6 +216,7 @@ $font-family-monospace: 'Custom Font', 'Courier New', Courier, monospace; // Importazione libreria SCSS di bootstrap-italia @import 'bootstrap-italia/src/scss/bootstrap-italia'; ``` +
### Supporto icone e assets @@ -250,6 +254,7 @@ Modifica il tuo `angular.json` aggiungendo: ] } ``` + Puoi utilizzare le label localizzate della libreria `design-angular-kit` nella tua applicazione, ad esempio `{{'it.errors.required-field' | translate}}`. [Vedi le nostre label](projects/design-angular-kit/assets/i18n/it.json) #### Localizzazione esistente @@ -257,7 +262,6 @@ Puoi utilizzare le label localizzate della libreria `design-angular-kit` nella t Se utilizzi già i file di localizzazione nella tua app, puoi utilizzare la libreria [ngx-translate-multi-http-loader](https://www.npmjs.com/package/ngx-translate-multi-http-loader) per caricare sia i file di localizzazione dell'app che quelli della libreria `design-angular-kit` - **Utilizzando la funzione `provideDesignAngularKit`:** ```typescript @@ -269,16 +273,18 @@ import { provideDesignAngularKit } from 'design-angular-kit'; provideDesignAngularKit({ translateLoader: (itPrefix: string, itSuffix: string) => ({ provide: TranslateLoader, - useFactory: (http: HttpBackend) => new MultiTranslateHttpLoader(http, [ - { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file - { prefix: './assets/i18n/' }, // Your i18n location - ]), - deps: [HttpBackend] + useFactory: (http: HttpBackend) => + new MultiTranslateHttpLoader(http, [ + { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file + { prefix: './assets/i18n/' }, // Your i18n location + ]), + deps: [HttpBackend], }), -}) +}); ``` **Utilizzando il modulo `DesignAngularKitModule`:** + ```typescript import { HttpBackend } from '@angular/common/http'; import { TranslateLoader } from '@ngx-translate/core'; @@ -288,22 +294,24 @@ import { DesignAngularKitModule } from 'design-angular-kit'; DesignAngularKitModule.forRoot({ translateLoader: (itPrefix: string, itSuffix: string) => ({ provide: TranslateLoader, - useFactory: (http: HttpBackend) => new MultiTranslateHttpLoader(http, [ - { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file - { prefix: './assets/i18n/' }, // Your i18n location - ]), - deps: [HttpBackend] + useFactory: (http: HttpBackend) => + new MultiTranslateHttpLoader(http, [ + { prefix: itPrefix, suffix: itSuffix }, // Load library translations first, so you can edit the keys in your localization file + { prefix: './assets/i18n/' }, // Your i18n location + ]), + deps: [HttpBackend], }), -}) +}); ``` #### Personalizzazione della localizzazione Se vuoi personalizzare le nostre label: -- Non includere il supporto i18n nel tuo `angular.json` - - Crea i tuoi file di localizzazione personalizzati nella tua cartella `assets/bootstrap-italia/i18n/` (crea il percorso se non esiste) - - Il json deve avere [questo formato](projects/design-angular-kit/assets/i18n/it.json). - - Aggiungi nella configurazione iniziale della libreria il `translateLoader` custom, sostituendo la stringa `assets/bootstrap-italia/i18n/` all'attributo `itPrefix` + +- Non includere il supporto i18n nel tuo `angular.json` + - Crea i tuoi file di localizzazione personalizzati nella tua cartella `assets/bootstrap-italia/i18n/` (crea il percorso se non esiste) + - Il json deve avere [questo formato](projects/design-angular-kit/assets/i18n/it.json). + - Aggiungi nella configurazione iniziale della libreria il `translateLoader` custom, sostituendo la stringa `assets/bootstrap-italia/i18n/` all'attributo `itPrefix` - Oppure, aggiungi le localizzazioni nei tuoi file json, sovrascrivendo le [chiavi del json della libreria](projects/design-angular-kit/assets/i18n/it.json). ### Utilizzo @@ -317,13 +325,12 @@ import { ItAlertComponent, ItPaginationComponent, ItBreadcrumbsModule } from 'de @NgModule({ imports: [ - ItAlertComponent, - ItPaginationComponent, - ItBreadcrumbsModule // Include ItBreadcrumbComponent e ItBreadcrumbItemComponent + ItAlertComponent, + ItPaginationComponent, + ItBreadcrumbsModule, // Include ItBreadcrumbComponent e ItBreadcrumbItemComponent ], }) -export class YourAppModule { -} +export class YourAppModule {} ``` ```typescript @@ -333,24 +340,23 @@ import { ItAlertComponent, ItPaginationComponent, ItBreadcrumbsModule } from 'de selector: 'app-product', standalone: true, imports: [ItAlertComponent, ItPaginationComponent, ItBreadcrumbsModule], - templateUrl: './product.component.html' + templateUrl: './product.component.html', }) -export class ProductComponent { -} +export class ProductComponent {} ``` ## Come contribuire 💙 👉🏻 È possibile contribuire alla libreria in vari modi: -* Con il proprio codice, prendendo in carico una issue tra quelle aperte e non già assegnate tra [le issue](https://github.com/italia/design-angular-kit/issues) di Angular Kit (è sufficiente anche un commento sulla issue per notificare la volontà di presa in carico). -* Attraverso la segnalazione di bug o miglioramenti al [repository ufficiale](https://github.com/italia/design-angular-kit/) di Angular Kit. -* Scrivendoci sul [canale dedicato](https://developersitalia.slack.com/messages/C04H3C19D52/) di Slack. +- Con il proprio codice, prendendo in carico una issue tra quelle aperte e non già assegnate tra [le issue](https://github.com/italia/design-angular-kit/issues) di Angular Kit (è sufficiente anche un commento sulla issue per notificare la volontà di presa in carico). +- Attraverso la segnalazione di bug o miglioramenti al [repository ufficiale](https://github.com/italia/design-angular-kit/) di Angular Kit. +- Scrivendoci sul [canale dedicato](https://developersitalia.slack.com/messages/C04H3C19D52/) di Slack. ## Come contribuire con il codice Vorresti dare una mano su Design Angular Kit? **Sei nel posto giusto!** - + Se non l'hai già fatto, inizia spendendo qualche minuto per approfondire la tua conoscenza sulle [linee guida di design per i servizi web della PA](https://design-italia.readthedocs.io/it/stable/index.html), e fai riferimento alle [indicazioni su come contribuire a Design Angular Kit](https://github.com/italia/design-angular-kit/blob/main/CONTRIBUTING.md). @@ -410,7 +416,7 @@ git clone https://github.com/italia/design-angular-kit.git 3. Al caricamento, Visual Studio Code riconoscerà la presenta della configurazione di un Devcontainer. Aprire il progetto con il devcontainer. Altre info [qui](https://code.visualstudio.com/docs/devcontainers/create-dev-container). -4. Visual Studio Code effettuerà il setup del container, andando ad installare la corretta versione di NodeJs, di npm e delle estensioni dell'IDE. Le dipendenze di progetto saranno installate nel processo di creazione del container. L'ambiente di sviuppo sarà pronto a setup ultimato. +4. Visual Studio Code effettuerà il setup del container, andando ad installare la corretta versione di NodeJs, di npm e delle estensioni dell'IDE. Le dipendenze di progetto saranno installate nel processo di creazione del container. L'ambiente di sviuppo sarà pronto a setup ultimato. 5. Lanciare l'applicazione in locale @@ -438,9 +444,9 @@ npm run test Un grazie speciale a chi ha reso possibile lo sviluppo di questa libreria -[![Antonino Bonanno](https://github.com/AntoninoBonanno.png?size=100)](https://github.com/AntoninoBonanno) | [![Cristian Borelli](https://github.com/cri99.png?size=100)](https://github.com/cri99) | [![Alessio Napolitano](https://github.com/alenap93.png?size=100)](https://github.com/alenap93) | ---- | --- | --- | -Antonino Bonanno | Cristian Borelli | Alessio Napolitano | +| [![Antonino Bonanno](https://github.com/AntoninoBonanno.png?size=100)](https://github.com/AntoninoBonanno) | [![Cristian Borelli](https://github.com/cri99.png?size=100)](https://github.com/cri99) | [![Alessio Napolitano](https://github.com/alenap93.png?size=100)](https://github.com/alenap93) | +| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| Antonino Bonanno | Cristian Borelli | Alessio Napolitano | e grazie in particolare al team di [NetService](https://www.net-serv.it/): @@ -448,7 +454,7 @@ e grazie in particolare al team di [NetService](https://www.net-serv.it/): --- -Tutti i contributor (*made with [contributors-img](https://contrib.rocks)*) +Tutti i contributor (_made with [contributors-img](https://contrib.rocks)_)
diff --git a/package-lock.json b/package-lock.json index db1ef7f3..7e867ee2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@angular-devkit/schematics": "^18.0.7", "@angular/animations": "^18.0.6", + "@angular/cdk": "^18.2.10", "@angular/common": "^18.0.6", "@angular/compiler": "^18.0.6", "@angular/core": "^18.0.6", @@ -54,12 +55,14 @@ "@typescript-eslint/parser": "^7.15.0", "browserslist-config-design-italia": "^1.1.0", "concurrently": "^8.2.2", + "copyfiles": "^2.4.1", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "gh-pages": "^6.1.1", "husky": "^9.0.11", "is-ci": "^3.0.1", + "jasmine": "^5.4.0", "jasmine-core": "~5.1.2", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.3", @@ -718,6 +721,23 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@angular/cdk": { + "version": "18.2.10", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-18.2.10.tgz", + "integrity": "sha512-Weh0slrfWNp5N6UO4m3tXzs2QBFexNsnJf1dq0oaLDBgfkuqUmxdCkurSv5+lWZRkTPLYmd/hQeJpvrhxMCleg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^18.0.0 || ^19.0.0", + "@angular/core": "^18.0.0 || ^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, "node_modules/@angular/cli": { "version": "18.0.7", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-18.0.7.tgz", @@ -9675,6 +9695,80 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, + "node_modules/copyfiles/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/copyfiles/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/copyfiles/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/core-js": { "version": "3.37.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz", @@ -10810,7 +10904,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12" }, @@ -14455,6 +14549,20 @@ "node": ">=8" } }, + "node_modules/jasmine": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.4.0.tgz", + "integrity": "sha512-E2u4ylX5tgGYvbynImU6EUBKKrSVB1L72FEPjGh4M55ov1VsxR26RA2JU91L9YSPFgcjo4mCLyKn/QXvEYGBkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.2.2", + "jasmine-core": "~5.4.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, "node_modules/jasmine-core": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz", @@ -14471,6 +14579,13 @@ "colors": "1.4.0" } }, + "node_modules/jasmine/node_modules/jasmine-core": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.4.0.tgz", + "integrity": "sha512-T4fio3W++llLd7LGSGsioriDHgWyhoL6YTu4k37uwJLF7DzOzspz7mNxRoM3cQdLWtL/ebazQpIf/yZGJx/gzg==", + "dev": true, + "license": "MIT" + }, "node_modules/java-properties": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/java-properties/-/java-properties-1.0.2.tgz", @@ -17085,6 +17200,44 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "dev": true, + "license": "ISC", + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, + "node_modules/noms/node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/noms/node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "node_modules/noms/node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nopt": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", @@ -20860,7 +21013,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, + "devOptional": true, "dependencies": { "entities": "^4.4.0" }, @@ -24996,6 +25149,16 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", diff --git a/package.json b/package.json index f4925160..a48ef1fb 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "start": "npm run compodoc-compile && concurrently \"npm run nunjucks-compile -- -w --path .\" \"ng serve\"", "build": "npm run nunjucks-compile -- --path . && npm run compodoc-compile && ng build --base-href design-angular-kit", "build:lib": "ng build design-angular-kit", + "postbuild:lib": "tsc -p projects/design-angular-kit/tsconfig.schematics.json && node postbuildSchematics.js", "watch:lib": "ng build --configuration development --watch design-angular-kit", "test": "ng test design-angular-kit --browsers=ChromeHeadless --no-watch", "e2e": "npm run compodoc-compile && npm run nunjucks-compile && ng e2e", @@ -19,7 +20,8 @@ "postversion": "npm version $npm_package_version --prefix projects/design-angular-kit/ --no-git-tag-version", "documentation-deploy-to-gh-pages": "gh-pages -d dist/design-angular-kit-bundle/browser -m 'chore: update documentation'", "compodoc-compile": "compodoc -p projects/design-angular-kit/tsconfig.lib.json -e json -d src/assets", - "nunjucks-compile": "nunjucks --options nunjucks.json src/app/**/*.tpl" + "nunjucks-compile": "nunjucks --options nunjucks.json src/app/**/*.tpl", + "schematics:test": "ts-node --project projects/design-angular-kit/tsconfig.schematics.json node_modules/jasmine/bin/jasmine projects/design-angular-kit/schematics/**/*.spec.ts" }, "browserslist": [ "extends browserslist-config-design-italia" @@ -63,6 +65,7 @@ "dependencies": { "@angular-devkit/schematics": "^18.0.7", "@angular/animations": "^18.0.6", + "@angular/cdk": "^18.2.10", "@angular/common": "^18.0.6", "@angular/compiler": "^18.0.6", "@angular/core": "^18.0.6", @@ -106,12 +109,14 @@ "@typescript-eslint/parser": "^7.15.0", "browserslist-config-design-italia": "^1.1.0", "concurrently": "^8.2.2", + "copyfiles": "^2.4.1", "eslint": "^8.57.0", "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.1.3", "gh-pages": "^6.1.1", "husky": "^9.0.11", "is-ci": "^3.0.1", + "jasmine": "^5.4.0", "jasmine-core": "~5.1.2", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.3", diff --git a/postbuildSchematics.js b/postbuildSchematics.js new file mode 100644 index 00000000..744100e6 --- /dev/null +++ b/postbuildSchematics.js @@ -0,0 +1,23 @@ +var copyfiles = require('copyfiles'); + +var callback = function(e) { + if(e) { + console.error(e) + } +}; + +// COPY collection.json + +//copyfiles -f projects/design-angular-kit/schematics/collection.json dist/design-angular-kit/schematics/ +var collectionSourcePath = 'projects/design-angular-kit/schematics/collection.json'; +var collectionDestionationPath = 'dist/design-angular-kit/schematics/'; +var collectionConfig = {verbose: true, flat: true, up: 3}; +copyfiles([collectionSourcePath, collectionDestionationPath],collectionConfig, callback); + +// COPY schematics/*/schema.json + +//copyfiles -f projects/design-angular-kit/schematics/*/schema.json dist/design-angular-kit/" +var schemajsonSourcePath = 'projects/design-angular-kit/schematics/*/schema.json'; +var schemajsonDestionationPath = 'dist/design-angular-kit/'; +var schemajsonConfig = {verbose: true, flat: false, up: 2}; +copyfiles([schemajsonSourcePath, schemajsonDestionationPath], schemajsonConfig, callback); \ No newline at end of file diff --git a/projects/design-angular-kit/package.json b/projects/design-angular-kit/package.json index d92a8e39..6c2b6443 100644 --- a/projects/design-angular-kit/package.json +++ b/projects/design-angular-kit/package.json @@ -27,6 +27,14 @@ "url": "https://www.net-serv.it/" } ], + "scripts": { + "build": "tsc -p tsconfig.schematics.json", + "postbuild": "copyfiles schematics/*/schema.json schematics/*/files/** schematics/collection.json ../../dist/my-lib/" + }, + "schematics": "./schematics/collection.json", + "ng-add": { + "save": "dependencies" + }, "dependencies": { "tslib": "^2.6.3" }, @@ -40,5 +48,9 @@ "@ngx-translate/http-loader": "^8.0.0", "bootstrap-italia": "^2.8.8" }, - "sideEffects": false + "sideEffects": false, + "devDependencies": { + "copyfiles": "file:../../node_modules/copyfiles", + "typescript": "file:../../node_modules/typescript" + } } diff --git a/projects/design-angular-kit/schematics/collection.json b/projects/design-angular-kit/schematics/collection.json new file mode 100644 index 00000000..2e9239fb --- /dev/null +++ b/projects/design-angular-kit/schematics/collection.json @@ -0,0 +1,19 @@ +{ + "$schema": "../../../node_modules/@angular-devkit/schematics/collection-schema.json", + "schematics": { + "ng-add": { + "description": "Add my Design Angular Kit to the project.", + "factory": "./ng-add/index", + "schema": "./ng-add/schema.json", + "hidden": true, + "aliases": ["install"] + }, + "ng-add-setup-project": { + "description": "Sets up the specified project after the ng-add dependencies have been installed", + "factory": "./ng-add/setup-project", + "schema": "./ng-add/schema.json", + "hidden": true, + "private": true + } + } + } \ No newline at end of file diff --git a/projects/design-angular-kit/schematics/ng-add/index.spec.ts b/projects/design-angular-kit/schematics/ng-add/index.spec.ts new file mode 100644 index 00000000..27738944 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/index.spec.ts @@ -0,0 +1,45 @@ +import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; +import * as path from 'path'; +import { Schema as SchematicOptions } from './schema'; + +describe(`ng add design-angular-kit`, () => { + const collectionPath = path.join(__dirname, '../collection.json'); + const runner = new SchematicTestRunner('schematics', collectionPath); + + const defaultOptions: SchematicOptions = { + project: 'test-project', // Set your default project name + }; + + let appTree: UnitTestTree; + + beforeEach(async () => { + // Simulate an Angular workspace project before running the schematic + appTree = await runner.runExternalSchematic('@schematics/angular', 'workspace', { + name: 'workspace', + version: '6.0.0', + newProjectRoot: 'projects', + }); + + appTree = await runner.runExternalSchematic( + '@schematics/angular', + 'application', + { + name: defaultOptions.project, + standalone: false, + }, + appTree + ); + }); + + it('should add DesignAngularKitModule to package.json', async () => { + const tree = await runner.runSchematic('ng-add', defaultOptions, appTree); + + // Check if the package.json file exists + const packageJsonPath = '/package.json'; + expect(tree.files).toContain(packageJsonPath); + + // Check if the correct dependency was added + const content = tree.readContent(packageJsonPath); + expect(content).toContain(`"design-angular-kit"`); + }); +}); diff --git a/projects/design-angular-kit/schematics/ng-add/index.ts b/projects/design-angular-kit/schematics/ng-add/index.ts new file mode 100644 index 00000000..5fbf89a9 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/index.ts @@ -0,0 +1,36 @@ +import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; +import { NodePackageInstallTask, RunSchematicTask } from '@angular-devkit/schematics/tasks'; + +import { Schema } from './schema'; +import { addPackageToPackageJson, getPackageVersionFromPackageJson, toSemVerObject } from './utils'; +import { getPackageVersion } from './versions-helper'; + +const DESIGN_ANGULAR_KIT_PACKAGE_NAME = 'design-angular-kit'; +const NG_LATEST_VERSION = '19.0.0'; + +export default function (options: Schema): Rule { + return (host: Tree, context: SchematicContext) => { + // Get the version of `@angular/core` + // This tag should be preferred because all Angular dependencies should have the same version tag if possible. + const ngCoreVersionTag = getPackageVersionFromPackageJson(host, '@angular/core'); + const designAngularKitVersionRange = getPackageVersionFromPackageJson(host, DESIGN_ANGULAR_KIT_PACKAGE_NAME); + const angularDependencyVersion = ngCoreVersionTag || NG_LATEST_VERSION; + + const { major: angularMajorVersion } = toSemVerObject(angularDependencyVersion); + + const designAngularKitPackageVersion = getPackageVersion({ angularMajorVersion }); + + // The CLI inserts `design-angular-kit` into the `package.json` before this schematic runs. + // This means that we do not need to insert Design Angular Kit into `package.json` files again. + if (designAngularKitVersionRange === null) { + addPackageToPackageJson(host, DESIGN_ANGULAR_KIT_PACKAGE_NAME, designAngularKitPackageVersion); + } + + addPackageToPackageJson(host, '@angular/cdk', angularDependencyVersion); + addPackageToPackageJson(host, '@angular/animations', angularDependencyVersion); + + const installTaskId = context.addTask(new NodePackageInstallTask()); + + context.addTask(new RunSchematicTask('ng-add-setup-project', options), [installTaskId]); + }; +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-animations.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-animations.ts new file mode 100644 index 00000000..861c5ef5 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-animations.ts @@ -0,0 +1,7 @@ +import { callRule, noop, Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; + +export function addAnimations(): Rule { + //this dependency should be provided at application level instead of lib level. ref: provideDesignAngularKit + //provideAnimationsAsync(), + return (host: Tree, context: SchematicContext) => callRule(noop(), host, context); +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-assets.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-assets.ts new file mode 100644 index 00000000..2ad0f95d --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-assets.ts @@ -0,0 +1,45 @@ +import { JsonArray, JsonObject, workspaces } from '@angular-devkit/core'; +import { Rule, Tree } from '@angular-devkit/schematics'; +import { readWorkspace, writeWorkspace } from '@schematics/angular/utility'; + +import { Schema } from '../../schema'; +import { getProjectTargetOptions } from './angular-json-helper'; +import { NoProjectException } from './exceptions'; + +const ASSETS_CONFIG = { + glob: '**/*', + input: './node_modules/bootstrap-italia/', + output: '/bootstrap-italia/', +}; + +export function addAssets(options: Schema): Rule { + return async (host: Tree) => { + const workspace = await readWorkspace(host); + + const projectName = options.project || workspace.extensions.defaultProject!.toString(); + const project = workspace.projects.get(projectName); + if (!project) { + throw new NoProjectException(projectName); + } + + addAssetsToAngularJson(project); + await writeWorkspace(host, workspace); + }; +} + +function addAssetsToAngularJson(project: workspaces.ProjectDefinition) { + const targetOptions = getProjectTargetOptions(project, 'build'); + const assets = targetOptions.assets as JsonArray | undefined; + if (!assets) { + targetOptions.assets = [ASSETS_CONFIG]; + } else { + const existingAssets = assets.map(s => (typeof s === 'string' ? s : (s as JsonObject)!['input'])) as Array; + + for (const [, input] of existingAssets.entries()) { + if (input === ASSETS_CONFIG.input) { + return; + } + } + assets.push(ASSETS_CONFIG); + } +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-design-angular-kit.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-design-angular-kit.ts new file mode 100644 index 00000000..505c3791 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-design-angular-kit.ts @@ -0,0 +1,40 @@ +import { Rule, SchematicContext, Tree, callRule } from '@angular-devkit/schematics'; +import { isStandaloneApp } from '@angular/cdk/schematics'; +import { addRootImport, addRootProvider, readWorkspace } from '@schematics/angular/utility'; +import { getMainFilePath } from '@schematics/angular/utility/standalone/util'; +import { firstValueFrom, map } from 'rxjs'; +import { Schema } from '../../schema'; +import { NoMainFileException, NoProjectException } from './exceptions'; + +export function addDesignAngularKit(options: Schema): Rule { + return async (host: Tree, context: SchematicContext) => { + const workspace = await readWorkspace(host); + const projectName = options.project || (workspace.extensions.defaultProject as string); + + // 1. getting project by name + const project = workspace.projects.get(projectName); + if (!project) { + throw new NoProjectException(projectName); + } + + // 2. getting main file for project + const mainFilePath = await getMainFilePath(host, projectName); + if (!mainFilePath || !host.read(mainFilePath)) { + throw new NoMainFileException(projectName); + } + + const isNotStandaloneApp = !isStandaloneApp(host, mainFilePath); + + const rule = isNotStandaloneApp + ? addRootImport(projectName, ({ code, external }) => code`${external('DesignAngularKitModule', 'design-angular-kit')}.forRoot()\n`) + : addRootProvider(projectName, ({ code, external }) => code`${external('provideDesignAngularKit', 'design-angular-kit')}()`); + + return firstValueFrom( + callRule(rule, host, context).pipe( + map(() => { + return (): Rule => () => {}; + }) + ) + ); + }; +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-http-client.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-http-client.ts new file mode 100644 index 00000000..f0de5cae --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-http-client.ts @@ -0,0 +1,7 @@ +import { Rule, SchematicContext, Tree, callRule, noop } from '@angular-devkit/schematics'; + +export function addHttpClient(): Rule { + //this dependency should be provided at application level instead of lib level. ref: provideDesignAngularKit + //provideHttpClient(), + return (host: Tree, context: SchematicContext) => callRule(noop(), host, context); +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-import-to-style-file.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-import-to-style-file.ts new file mode 100644 index 00000000..fbbfc71a --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-import-to-style-file.ts @@ -0,0 +1,120 @@ +import { JsonArray, JsonObject, normalize, workspaces } from '@angular-devkit/core'; +import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; + +import { readWorkspace, writeWorkspace } from '@schematics/angular/utility'; +import * as path from 'path'; + +import { Schema } from '../../schema'; +import { getProjectTargetOptions } from './angular-json-helper'; +import { NoProjectException } from './exceptions'; + +const BOOTSTRAP_ITALIA_CSS_FILEPATH = 'node_modules/bootstrap-italia/dist/css/bootstrap-italia.min.css'; +const SUPPORTED_BOOTSTRAP_ITALIA_STYLE_MAP: Record = { + '.sass': ` + /* Importazione libreria SCSS di bootstrap-italia */ + @import 'bootstrap-italia/scss/bootstrap-italia' + `, + '.scss': ` + /* Importazione libreria SCSS di bootstrap-italia */ + @import '../node_modules/bootstrap-italia/src/scss/bootstrap-italia.scss'; + `, +}; + +/** + * if supported + * add to styles.scss or to style.sass + * else + * add css to assets in angular.json + * @param options + * @returns Rule + */ +export function addImportToStyleFile(options: Schema): Rule { + return async (host: Tree, context: SchematicContext) => { + const workspace = await readWorkspace(host); + + const projectName = options.project || workspace.extensions.defaultProject!.toString(); + const project = workspace.projects.get(projectName); + if (!project) { + throw new NoProjectException(projectName); + } + + const styleFilePath = getProjectStyleFile(project as any) || ''; + const styleFileExtension = path.extname(styleFilePath); + const styleFilePatch = SUPPORTED_BOOTSTRAP_ITALIA_STYLE_MAP[styleFileExtension]; + + // found supported styles + if (styleFilePatch) { + return addBootstrapItaliaToStylesFile(styleFilePath, styleFilePatch); + } else { + // found some styles, but unsupported + if (styleFileExtension !== '.css' && styleFileExtension !== '') { + context.logger.warn('messages.unsupportedStyles(styleFilePath)'); + } + + // just patching 'angular.json' + addBootstrapItaliaToAngularJson(project as any); + await writeWorkspace(host, workspace); + } + }; +} + +function addBootstrapItaliaToStylesFile(styleFilePath: string, styleFilePatch: string): Rule { + return async (host: Tree) => { + const styleContent = host.read(styleFilePath)!.toString('utf-8'); + + const recorder = host.beginUpdate(styleFilePath); + recorder.insertRight(styleContent.length, styleFilePatch); + + host.commitUpdate(recorder); + }; +} + +function addBootstrapItaliaToAngularJson(project: workspaces.ProjectDefinition) { + const targetOptions = getProjectTargetOptions(project, 'build'); + const styles = targetOptions.styles as JsonArray | undefined; + if (!styles) { + targetOptions.styles = [BOOTSTRAP_ITALIA_CSS_FILEPATH]; + } else { + const existingStyles = styles.map(s => (typeof s === 'string' ? s : (s as JsonObject)!['input'])) as Array; + + for (const [, stylePath] of existingStyles.entries()) { + if (stylePath === BOOTSTRAP_ITALIA_CSS_FILEPATH) { + return; + } + } + styles.unshift(BOOTSTRAP_ITALIA_CSS_FILEPATH); + } +} + +// Regular expression that matches all possible Angular CLI default style files +const defaultStyleFileRegex = /styles\.(c|le|sc|sa)ss/; + +// Regular expression that matches all files that have a proper stylesheet extension +const validStyleFileRegex = /\.(c|le|sc|sa)ss/; + +function getProjectStyleFile(project: workspaces.ProjectDefinition, extension?: string): string | null { + const buildOptions = getProjectTargetOptions(project, 'build'); + + if (buildOptions.styles && Array.isArray(buildOptions.styles) && buildOptions.styles.length) { + const styles = buildOptions.styles.map(s => (typeof s === 'string' ? s : (s as JsonObject)!['input'])) as Array; + + // Look for the default style file that is generated for new projects by the Angular CLI. This + // default style file is usually called `styles.ext` unless it has been changed explicitly. + const defaultMainStylePath = styles.find(file => (extension ? file === `styles.${extension}` : defaultStyleFileRegex.test(file))); + + if (defaultMainStylePath) { + return normalize(defaultMainStylePath); + } + + // If no default style file could be found, use the first style file that matches the given + // extension. If no extension specified explicitly, we look for any file with a valid style + // file extension. + const fallbackStylePath = styles.find(file => (extension ? file.endsWith(`.${extension}`) : validStyleFileRegex.test(file))); + + if (fallbackStylePath) { + return normalize(fallbackStylePath); + } + } + + return null; +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-localisation.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-localisation.ts new file mode 100644 index 00000000..8aed28eb --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/add-localisation.ts @@ -0,0 +1,45 @@ +import { JsonArray, JsonObject, workspaces } from '@angular-devkit/core'; +import { Rule, Tree } from '@angular-devkit/schematics'; +import { readWorkspace, writeWorkspace } from '@schematics/angular/utility'; + +import { Schema } from '../../schema'; +import { getProjectTargetOptions } from './angular-json-helper'; +import { NoProjectException } from './exceptions'; + +const LOCALISATION_CONFIG = { + glob: '**/*', + input: './node_modules/design-angular-kit/assets/i18n', + output: '/bootstrap-italia/i18n/', +}; + +export function addLocalisation(options: Schema): Rule { + return async (host: Tree) => { + const workspace = await readWorkspace(host); + + const projectName = options.project || workspace.extensions.defaultProject!.toString(); + const project = workspace.projects.get(projectName); + if (!project) { + throw new NoProjectException(projectName); + } + + addLocalisationToAngularJson(project); + await writeWorkspace(host, workspace); + }; +} + +function addLocalisationToAngularJson(project: workspaces.ProjectDefinition) { + const targetOptions = getProjectTargetOptions(project, 'build'); + const assets = targetOptions.assets as JsonArray | undefined; + if (!assets) { + targetOptions.assets = [LOCALISATION_CONFIG]; + } else { + const existingAssets = assets.map(s => (typeof s === 'string' ? s : (s as JsonObject)!['input'])) as Array; + + for (const [, input] of existingAssets.entries()) { + if (input === LOCALISATION_CONFIG.input) { + return; + } + } + assets.push(LOCALISATION_CONFIG); + } +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/angular-json-helper.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/angular-json-helper.ts new file mode 100644 index 00000000..ad042cf8 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/angular-json-helper.ts @@ -0,0 +1,11 @@ +import { workspaces } from '@angular-devkit/core'; +import { SchematicsException } from '@angular-devkit/schematics'; + +export function getProjectTargetOptions(project: workspaces.ProjectDefinition, buildTarget: string) { + const buildTargetObject = project.targets.get(buildTarget); + if (buildTargetObject && buildTargetObject.options) { + return buildTargetObject.options; + } + + throw new SchematicsException(`Cannot determine project target configuration for: ${buildTarget}.`); +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/exceptions.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/exceptions.ts new file mode 100644 index 00000000..40940452 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/exceptions.ts @@ -0,0 +1,13 @@ +import { SchematicsException } from '@angular-devkit/schematics'; + +export class NoProjectException extends SchematicsException { + constructor(project: string) { + super(`Unable to find project '${project}' in the workspace`); + } +} + +export class NoMainFileException extends SchematicsException { + constructor(project: string) { + super(`Unable to find 'build.options.main' file path for project "${project}`); + } +} diff --git a/projects/design-angular-kit/schematics/ng-add/rules/setup-project/index.ts b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/index.ts new file mode 100644 index 00000000..d9ba7d49 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/rules/setup-project/index.ts @@ -0,0 +1,6 @@ +export * from './add-animations'; +export * from './add-assets'; +export * from './add-design-angular-kit'; +export * from './add-http-client'; +export * from './add-import-to-style-file'; +export * from './add-localisation'; diff --git a/projects/design-angular-kit/schematics/ng-add/schema.json b/projects/design-angular-kit/schematics/ng-add/schema.json new file mode 100644 index 00000000..678a440a --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/schema.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/schema", + "$id": "design-angular-kit-ng-add", + "title": "Design Angular Hit ng-add schematic", + "type": "object", + "properties": { + "project": { + "type": "string", + "description": "Name of the project.", + "$default": { + "$source": "projectName" + } + } + }, + "required": [] +} \ No newline at end of file diff --git a/projects/design-angular-kit/schematics/ng-add/schema.ts b/projects/design-angular-kit/schematics/ng-add/schema.ts new file mode 100644 index 00000000..e7654820 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/schema.ts @@ -0,0 +1,3 @@ +export interface Schema { + project: string; +} diff --git a/projects/design-angular-kit/schematics/ng-add/setup-project.spec.ts b/projects/design-angular-kit/schematics/ng-add/setup-project.spec.ts new file mode 100644 index 00000000..780cc6ad --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/setup-project.spec.ts @@ -0,0 +1,166 @@ +import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; +import * as path from 'path'; +import { Schema } from './schema'; + +const createApp = async ( + runner: SchematicTestRunner, + options: Schema, + { standalone, style }: { standalone: boolean; style: 'scss' | 'css' | 'sass' } +) => { + let tree = await runner.runExternalSchematic('@schematics/angular', 'workspace', { + name: 'workspace', + version: '18.0.0', + newProjectRoot: 'projects', + }); + + tree = await runner.runExternalSchematic( + '@schematics/angular', + 'application', + { + name: options.project, + standalone, + style, + }, + tree + ); + + return { tree }; +}; + +describe(`ng add design-angular-kit | setup-project - library providing`, () => { + const collectionPath = path.join(__dirname, '../collection.json'); + const runner = new SchematicTestRunner('schematics', collectionPath); + + const defaultOptions: Schema = { + project: 'test-project', // Set your default project name + }; + + it('should add DesignAngularKitModule to the root module - NgModule app', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone: false, style: 'scss' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the app.module.ts file exists + const modulePath = '/projects/test-project/src/app/app.module.ts'; + expect(tree.files).toContain(modulePath); + + // Check if the correct import statement was added + const content = tree.readContent(modulePath); + expect(content).toContain(`import { DesignAngularKitModule } from 'design-angular-kit';`); + expect(content).toContain(`DesignAngularKitModule.forRoot()`); + }); + + it('should add provideDesignAngularKit() to the app config - Standalone app', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone: true, style: 'scss' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the app.module.ts file exists + const appConfigPath = '/projects/test-project/src/app/app.config.ts'; + expect(tree.files).toContain(appConfigPath); + + // Check if the correct import statement was added + const content = tree.readContent(appConfigPath); + expect(content).toContain(`import { provideDesignAngularKit } from 'design-angular-kit';`); + expect(content).toContain(`provideDesignAngularKit()`); + }); +}); + +for (const standalone of [true, false]) { + describe(`ng add design-angular-kit | setup-project for ${standalone ? 'standalone' : 'NgModule'} app - styles`, () => { + const collectionPath = path.join(__dirname, '../collection.json'); + const runner = new SchematicTestRunner('schematics', collectionPath); + + const defaultOptions: Schema = { + project: 'test-project', // Set your default project name + }; + + it('should add .scss import to project style file', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone, style: 'scss' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the styles.scss file exists + const styleFilePath = '/projects/test-project/src/styles.scss'; + expect(tree.files).toContain(styleFilePath); + // Check if the correct import statement was added + const content = tree.readContent(styleFilePath); + expect(content).toContain(`@import '../node_modules/bootstrap-italia/src/scss/bootstrap-italia.scss';`); + }); + + it('should add .sass import to project style file', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone, style: 'sass' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the styles.sass file exists + const styleFilePath = '/projects/test-project/src/styles.sass'; + expect(tree.files).toContain(styleFilePath); + // Check if the correct import statement was added + const content = tree.readContent(styleFilePath); + expect(content).toContain(`@import 'bootstrap-italia/scss/bootstrap-italia'`); + }); + + it('should add .css import to angular.json', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone, style: 'css' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the angular.json file exists + const angularJsonPath = '/angular.json'; + expect(tree.files).toContain(angularJsonPath); + // Check if the correct import statement was added + const content = tree.readContent(angularJsonPath); + expect(content).toContain(`node_modules/bootstrap-italia/dist/css/bootstrap-italia.min.css`); + }); + }); + + describe(`ng add design-angular-kit | setup-project for ${standalone ? 'standalone' : 'NgModule'} app - assets`, () => { + const collectionPath = path.join(__dirname, '../collection.json'); + const runner = new SchematicTestRunner('schematics', collectionPath); + + const defaultOptions: Schema = { + project: 'test-project', // Set your default project name + }; + + it('should add bootstrap italia assets to angular.json', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone, style: 'css' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the angular.json file exists + const angularJsonPath = '/angular.json'; + expect(tree.files).toContain(angularJsonPath); + // Check if the correct import statement was added + const content = tree.readContent(angularJsonPath); + //"./node_modules/bootstrap-italia/" + expect(content).toContain(`./node_modules/bootstrap-italia/`); + const angularJson = JSON.parse(content); + const assetsConfig = angularJson.projects[defaultOptions.project]?.architect?.build?.options?.assets?.find( + (item: { input: string }) => item.input === `./node_modules/bootstrap-italia/` + ); + expect(assetsConfig).toBeDefined(assetsConfig); + }); + }); + + describe(`ng add design-angular-kit | setup-project for ${standalone ? 'standalone' : 'NgModule'} app - localisation`, () => { + const collectionPath = path.join(__dirname, '../collection.json'); + const runner = new SchematicTestRunner('schematics', collectionPath); + + const defaultOptions: Schema = { + project: 'test-project', // Set your default project name + }; + + it('should add bootstrap italia localisation to angular.json', async () => { + const { tree: appTree } = await createApp(runner, defaultOptions, { standalone, style: 'css' }); + const tree = await runner.runSchematic('ng-add-setup-project', defaultOptions, appTree); + + // Check if the angular.json file exists + const angularJsonPath = '/angular.json'; + expect(tree.files).toContain(angularJsonPath); + // Check if the correct import statement was added + const content = tree.readContent(angularJsonPath); + //"./node_modules/bootstrap-italia/" + expect(content).toContain(`/node_modules/design-angular-kit/assets/i18n`); + const angularJson = JSON.parse(content); + const assetsConfig = angularJson.projects[defaultOptions.project]?.architect?.build?.options?.assets?.find( + (item: { input: string }) => item.input === `./node_modules/design-angular-kit/assets/i18n` + ); + expect(assetsConfig).toBeDefined(assetsConfig); + }); + }); +} diff --git a/projects/design-angular-kit/schematics/ng-add/setup-project.ts b/projects/design-angular-kit/schematics/ng-add/setup-project.ts new file mode 100644 index 00000000..554f6b8f --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/setup-project.ts @@ -0,0 +1,31 @@ +import { chain, Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; + +import { getProjectFromWorkspace } from '@angular/cdk/schematics'; +import { getWorkspace } from '@schematics/angular/utility/workspace'; +import { addAnimations, addAssets, addDesignAngularKit, addHttpClient, addImportToStyleFile, addLocalisation } from './rules/setup-project'; +import { Schema } from './schema'; + +import { ProjectType } from '@schematics/angular/utility/workspace-models'; + +export default function (options: Schema): Rule { + return async (host: Tree, context: SchematicContext) => { + const workspace = await getWorkspace(host); + const project = getProjectFromWorkspace(workspace, options.project); + const isAProject = project.extensions['projectType'] === ProjectType.Application; + + if (isAProject) { + return chain([ + addAnimations(), + addHttpClient(), + addDesignAngularKit(options), + addImportToStyleFile(options), + addAssets(options), + addLocalisation(options), + ]); + } + context.logger.warn( + `Design Angular Kit has been set in your workspace. No additional setup required.\nIf you intend to run the schematics on a specific project, please use -- project option ` + ); + return; + }; +} diff --git a/projects/design-angular-kit/schematics/ng-add/utils.ts b/projects/design-angular-kit/schematics/ng-add/utils.ts new file mode 100644 index 00000000..3e54c0b6 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/utils.ts @@ -0,0 +1,71 @@ +import { Tree } from '@angular-devkit/schematics'; + +interface PackageJson { + dependencies: Record; +} + +interface SemVerObject { + semVer?: string; + major?: string; + minor?: string; + patch?: string; + prerelease?: string; + buildmetadata?: string; +} + +const SEMVER_WITH_PREFIX_REGEX = + /^[\^~]?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; + +function sortObjectByKeys(obj: Record) { + return Object.keys(obj) + .sort() + .reduce( + (result, key) => { + result[key] = obj[key]; + return result; + }, + {} as Record + ); +} + +/** Get a SemVerObject given a semver string */ +export function toSemVerObject(versionString: string) { + const [semVer, major, minor, patch, prerelease, buildmetadata] = versionString.match(SEMVER_WITH_PREFIX_REGEX) ?? []; + return { semVer, major, minor, patch, prerelease, buildmetadata } satisfies SemVerObject; +} + +/** Adds a package to the package.json in the given host tree. */ +export function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree { + if (host.exists('package.json')) { + const sourceText = host.read('package.json')!.toString('utf-8'); + const json = JSON.parse(sourceText) as PackageJson; + + if (!json.dependencies) { + json.dependencies = {}; + } + + if (!json.dependencies[pkg]) { + json.dependencies[pkg] = version; + json.dependencies = sortObjectByKeys(json.dependencies); + } + + host.overwrite('package.json', JSON.stringify(json, null, 2)); + } + + return host; +} + +/** Gets the version of the specified package by looking at the package.json in the given tree. */ +export function getPackageVersionFromPackageJson(tree: Tree, name: string): string | null { + if (!tree.exists('package.json')) { + return null; + } + + const packageJson = JSON.parse(tree.read('package.json')!.toString('utf8')) as PackageJson; + + if (packageJson.dependencies && packageJson.dependencies[name]) { + return packageJson.dependencies[name]; + } + + return null; +} diff --git a/projects/design-angular-kit/schematics/ng-add/versions-helper.ts b/projects/design-angular-kit/schematics/ng-add/versions-helper.ts new file mode 100644 index 00000000..fd3fb268 --- /dev/null +++ b/projects/design-angular-kit/schematics/ng-add/versions-helper.ts @@ -0,0 +1,41 @@ +const ANGULAR_VERSIONS = { + v17: '17', + v18: '18', +}; + +const KIT_VERSION_1_0_0 = '1.0.0'; +const KIT_VERSION_1_1_0 = '1.1.0'; +const DEFAULT = 'DEFAULT'; +const LATEST = 'LATEST'; + +const versionsMap: Record = { + [DEFAULT]: KIT_VERSION_1_0_0, + [ANGULAR_VERSIONS.v17]: KIT_VERSION_1_0_0, + [ANGULAR_VERSIONS.v18]: KIT_VERSION_1_1_0, + [LATEST]: KIT_VERSION_1_1_0, +}; + +// Get Design Angular Kit package version given Angular major version +export function getPackageVersion({ angularMajorVersion }: { angularMajorVersion: string }) { + if (isNaN(+angularMajorVersion)) { + throw new Error('Major version is not a number'); + } + + let version = versionsMap[DEFAULT]; + + switch (angularMajorVersion) { + case ANGULAR_VERSIONS.v17: + version = versionsMap[ANGULAR_VERSIONS.v17]; + break; + case ANGULAR_VERSIONS.v18: + version = versionsMap[ANGULAR_VERSIONS.v18]; + break; + default: { + if (Number(angularMajorVersion) > Number(ANGULAR_VERSIONS.v18)) { + version = versionsMap[LATEST]; + } + } + } + + return version; +} diff --git a/projects/design-angular-kit/src/lib/provide-design-angular-kit.ts b/projects/design-angular-kit/src/lib/provide-design-angular-kit.ts index f8f9dfb1..81389d4d 100644 --- a/projects/design-angular-kit/src/lib/provide-design-angular-kit.ts +++ b/projects/design-angular-kit/src/lib/provide-design-angular-kit.ts @@ -1,10 +1,10 @@ +import { HttpClient, provideHttpClient } from '@angular/common/http'; import { APP_INITIALIZER, EnvironmentProviders, importProvidersFrom, makeEnvironmentProviders, Provider } from '@angular/core'; -import { DesignAngularKitConfig, IT_ASSET_BASE_PATH } from './interfaces/design-angular-kit-config'; -import { loadFonts } from 'bootstrap-italia'; +import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { HttpClient, provideHttpClient } from '@angular/common/http'; import { TranslateHttpLoader } from '@ngx-translate/http-loader'; -import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; +import { loadFonts } from 'bootstrap-italia'; +import { DesignAngularKitConfig, IT_ASSET_BASE_PATH } from './interfaces/design-angular-kit-config'; /** * Configures DesignAngularKit library diff --git a/projects/design-angular-kit/tsconfig.schematics.json b/projects/design-angular-kit/tsconfig.schematics.json new file mode 100644 index 00000000..2b1c343d --- /dev/null +++ b/projects/design-angular-kit/tsconfig.schematics.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "lib": ["es2018", "dom"], + "declaration": true, + "module": "commonjs", + "moduleResolution": "node", + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitThis": true, + "noUnusedParameters": true, + "noUnusedLocals": true, + "rootDir": "schematics", + "outDir": "../../dist/design-angular-kit/schematics", + "skipDefaultLibCheck": true, + "skipLibCheck": true, + "sourceMap": true, + "strictNullChecks": true, + "target": "es6", + "types": ["jasmine", "node"] + }, + "include": ["schematics/**/*"], + "exclude": ["schematics/*/files/**/*"] +} From 15c8caa658923bb21cc60c561843aff73405b623 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 14:26:37 +0100 Subject: [PATCH 04/21] feat: navscroll component --- .../navscroll-list-item.component.ts | 95 ++++++++++++ .../navscroll-list-items.component.ts | 27 ++++ .../navscroll/navscroll.component.html | 101 +++++++++++++ .../navscroll/navscroll.component.scss | 9 ++ .../navscroll/navscroll.component.ts | 143 ++++++++++++++++++ .../navigation/navscroll/navscroll.model.ts | 12 ++ .../navigation/navscroll/navscroll.store.ts | 93 ++++++++++++ .../navigation/navscroll/navscroll.utils.ts | 28 ++++ .../src/lib/design-angular-kit.module.ts | 10 +- projects/design-angular-kit/src/public_api.ts | 3 + src/app/app-routing.module.ts | 7 +- src/app/app.component.html | 10 +- src/app/app.module.ts | 18 ++- src/app/examples/index.ts | 3 + .../navscroll-example.component.ts | 94 ++++++++++++ .../navscroll-example.module.ts | 11 ++ src/app/examples/routes.ts | 4 + ...oll-custom-template-example.component.html | 6 + ...croll-custom-template-example.component.ts | 76 ++++++++++ .../navscroll-default-example.component.html | 1 + .../navscroll-default-example.component.ts | 76 ++++++++++ .../navscroll-examples.component.scss | 0 .../navscroll-examples.component.tpl | 30 ++++ .../navscroll-examples.component.ts | 8 + .../navscroll-index.component.html | 12 ++ .../navscroll-index.component.ts | 14 ++ src/app/navscroll/navscroll-routing.module.ts | 11 ++ src/app/navscroll/navscroll.module.ts | 12 ++ src/assets/table-of-content.json | 4 + 29 files changed, 900 insertions(+), 18 deletions(-) create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-item.component.ts create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-items.component.ts create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.html create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.scss create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.model.ts create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.store.ts create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.utils.ts create mode 100644 src/app/examples/index.ts create mode 100644 src/app/examples/navscroll-example/navscroll-example.component.ts create mode 100644 src/app/examples/navscroll-example/navscroll-example.module.ts create mode 100644 src/app/examples/routes.ts create mode 100644 src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.html create mode 100644 src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.ts create mode 100644 src/app/navscroll/navscroll-default-example/navscroll-default-example.component.html create mode 100644 src/app/navscroll/navscroll-default-example/navscroll-default-example.component.ts create mode 100644 src/app/navscroll/navscroll-examples/navscroll-examples.component.scss create mode 100644 src/app/navscroll/navscroll-examples/navscroll-examples.component.tpl create mode 100644 src/app/navscroll/navscroll-examples/navscroll-examples.component.ts create mode 100644 src/app/navscroll/navscroll-index/navscroll-index.component.html create mode 100644 src/app/navscroll/navscroll-index/navscroll-index.component.ts create mode 100644 src/app/navscroll/navscroll-routing.module.ts create mode 100644 src/app/navscroll/navscroll.module.ts diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-item.component.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-item.component.ts new file mode 100644 index 00000000..f5daf512 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-item.component.ts @@ -0,0 +1,95 @@ +import { AsyncPipe } from '@angular/common'; +import { ChangeDetectionStrategy, Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewChild } from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { + IsActiveMatchOptions, + NavigationEnd, + Router, + Event as RouterEvent, + RouterLink, + RouterLinkActive, + RouterLinkWithHref, + Scroll, +} from '@angular/router'; +import { AsyncSubject, filter, switchMap, tap } from 'rxjs'; +import { ItNavscrollListItemsComponent } from './navscroll-list-items.component'; +import { NavscrollItem } from './navscroll.model'; +import { NavscrollStore } from './navscroll.store'; + +const ROUTER_LINK_ACTIVE_OPTIONS: IsActiveMatchOptions = { + fragment: 'exact', + paths: 'exact', + queryParams: 'exact', + matrixParams: 'exact', +}; + +@Component({ + selector: 'it-navscroll-list-item', + standalone: true, + imports: [RouterLink, RouterLinkActive, RouterLinkWithHref, ItNavscrollListItemsComponent, AsyncPipe], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + {{ item?.title }} + `, +}) +export class ItNavscrollListItemComponent implements OnInit { + @Input() item!: NavscrollItem; + + @Output() readonly checkActive = new EventEmitter(); + + @ViewChild('rtl') + readonly rtl: any; + + readonly routerLinkActiveOptions = ROUTER_LINK_ACTIVE_OPTIONS; + + readonly #initIsActive = new AsyncSubject(); + + readonly active = this.#initIsActive.asObservable().pipe(switchMap(item => this.#store.isActive$(item))); + + readonly #router = inject(Router); + + readonly #store = inject(NavscrollStore); + + readonly #destroyRef = inject(DestroyRef); + + ngOnInit() { + this.#initIsActiveSub(); + this.#router.events + .pipe( + takeUntilDestroyed(this.#destroyRef), + filter((event: RouterEvent) => { + const isNavigationEndEvent = event instanceof NavigationEnd; + const isScrollEvent = event instanceof Scroll && (event as Scroll).routerEvent instanceof NavigationEnd; + return isNavigationEndEvent || isScrollEvent; + }), + tap(() => { + if (this.rtl?.isActive) { + this.#store.setActive(this.item); + } + }) + ) + .subscribe(); + } + + clickHandler(event: Event) { + event.preventDefault(); + this.#store.selectMenuItem(); + this.#router.navigate([], { fragment: this.item.href }); + } + + #initIsActiveSub() { + this.#initIsActive.next(this.item); + this.#initIsActive.complete(); + } +} diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-items.component.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-items.component.ts new file mode 100644 index 00000000..076cb753 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll-list-items.component.ts @@ -0,0 +1,27 @@ +import { JsonPipe, NgTemplateOutlet } from '@angular/common'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; +import { RouterLink, RouterLinkActive, RouterLinkWithHref } from '@angular/router'; +import { ItNavscrollListItemComponent } from './navscroll-list-item.component'; +import { NavscrollItems } from './navscroll.model'; + +@Component({ + selector: 'it-navscroll-list-items', + standalone: true, + imports: [NgTemplateOutlet, RouterLink, RouterLinkActive, RouterLinkWithHref, JsonPipe, ItNavscrollListItemComponent], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + `, +}) +export class ItNavscrollListItemsComponent { + @Input() items!: NavscrollItems; +} diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.html b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.html new file mode 100644 index 00000000..3a604667 --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.html @@ -0,0 +1,101 @@ +
+
+
+
+ +
+
+
+ + +
+
+
+ + + @for (item of items; track item.href) { + + } + + + + @switch (level) { + @case (1) { +

{{ item.title }}

+ } + @case (2) { +

{{ item.title }}

+ } + @case (3) { +

{{ item.title }}

+ } + @case (4) { +
{{ item.title }}
+ } + @default { +
{{ item.title }}
+ } + } +

{{ item.text }}

+ @for (item of item.childs; track item.href) { + + } +
diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.scss b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.scss new file mode 100644 index 00000000..7be7002f --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.scss @@ -0,0 +1,9 @@ +.it-navscroll-sticky { + // data-bs-toggle="sticky" + position: sticky; + top: 0; +} + +.it-navscroll-sticky-mobile { + z-index: 1020; +} diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts new file mode 100644 index 00000000..41a1016c --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts @@ -0,0 +1,143 @@ +import { AsyncPipe, NgClass, NgTemplateOutlet, ViewportScroller } from '@angular/common'; +import { + ChangeDetectionStrategy, + Component, + DestroyRef, + ElementRef, + HostListener, + inject, + Input, + OnInit, + TemplateRef, + ViewChild, +} from '@angular/core'; +import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; +import { RouterLink, RouterLinkActive, RouterLinkWithHref } from '@angular/router'; +import { delay, filter, map, tap, withLatestFrom } from 'rxjs'; +import { ItNavscrollListItemsComponent } from './navscroll-list-items.component'; +import { NavscrollItem } from './navscroll.model'; +import { NavscrollStore } from './navscroll.store'; + +/** + * Navscroll + * @description Show a list of links to anchor of the document. + */ +@Component({ + selector: 'it-navscroll', + standalone: true, + imports: [ + ItNavscrollListItemsComponent, + AsyncPipe, + NgTemplateOutlet, + RouterLink, + RouterLinkActive, + RouterLinkWithHref, + AsyncPipe, + NgClass, + ], + templateUrl: './navscroll.component.html', + styleUrl: './navscroll.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [NavscrollStore], +}) +export class ItNavscrollComponent implements OnInit { + /** + * Header of the Navscroll + */ + @Input() readonly header = ''; + /** + * A list of links + */ + @Input() readonly items!: Array; + /** + * Border position + * @default left + */ + @Input() readonly borderPosition: 'left' | 'right' = 'left'; + /** + * Alignment + * @default top + */ + @Input() readonly alignment: 'top' | 'bottom' = 'top'; + + /** + * Theme + * @default light + */ + @Input() readonly theme: 'light' | 'dark' = 'light'; + + /** + * Custom template for the content section + */ + @Input() + pageSectionsTemplate?: TemplateRef; + + @HostListener('window:scroll', ['$event']) // for window scroll events + onScroll() { + const sectionContainer = this.#elementRef.nativeElement.querySelector('.it-page-sections-container'); + this.#store.updateProgressBar(sectionContainer); + } + + @HostListener('window:resize', ['$event']) + onResize() { + this.#setMobile(); + } + + @ViewChild('toggleButtonRef') + readonly toggleButtonRef!: ElementRef; + + readonly #store = inject(NavscrollStore); + + readonly #scroller = inject(ViewportScroller); + + readonly #destroyRef = inject(DestroyRef); + + readonly #elementRef = inject(ElementRef); + + readonly selectedTitle = this.#store.selected.pipe(map(selected => selected?.title ?? '')); + + readonly progressBarValue = this.#store.progressBar; + + readonly isMobile = this.#store.isMobile; + + constructor() { + this.#store.menuItemSelected + .pipe( + takeUntilDestroyed(), + withLatestFrom(this.isMobile), + tap(v => { + const isMobile = v[1]; + if (isMobile) { + this.toggleButtonRef.nativeElement.click(); + } + }) + ) + .subscribe(); + } + + ngOnInit(): void { + this.#initViewScrollerSubscription(); + this.#store.init(this.items); + this.#setMobile(); + } + + #initViewScrollerSubscription() { + this.#store.selected + .pipe( + takeUntilDestroyed(this.#destroyRef), + filter(selected => Boolean(selected)), + map(v => v as NavscrollItem), + delay(0), //WA + tap({ + next: ({ href }) => { + this.#scroller.scrollToAnchor(href); + }, + }) + ) + .subscribe(); + } + + #setMobile() { + this.#store.setMobile(window); + } +} diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.model.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.model.ts new file mode 100644 index 00000000..4b7b4b6f --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.model.ts @@ -0,0 +1,12 @@ +export interface NavscrollItem { + title: string; + text: string; + href: string; + childs: NavscrollItems; +} + +export type NavscrollItems = Array; + +export interface NavscrollItemActive { + active: boolean; +} diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.store.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.store.ts new file mode 100644 index 00000000..102847af --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.store.ts @@ -0,0 +1,93 @@ +import { BehaviorSubject, distinctUntilChanged, map, Subject } from 'rxjs'; +import { NavscrollItem } from './navscroll.model'; +import { flattenNavscrollItems, search } from './navscroll.utils'; + +interface NavscrollState { + items: Set; + active: Array; + selected?: NavscrollItem; + progressBar: number; + isMobile: boolean; +} + +export class NavscrollStore { + readonly #state = new BehaviorSubject({ + items: new Set(), + active: [], + selected: undefined, + progressBar: 0, + isMobile: false, + }); + + readonly #state$ = this.#state.asObservable(); + + readonly selected = this.#state$.pipe( + map(({ selected }) => selected), + distinctUntilChanged() + ); + + readonly progressBar = this.#state$.pipe( + map(({ progressBar }) => progressBar), + distinctUntilChanged() + ); + + readonly isMobile = this.#state$.pipe( + map(({ isMobile }) => isMobile), + distinctUntilChanged() + ); + + readonly #menuItemSelected = new Subject(); + + readonly menuItemSelected = this.#menuItemSelected.asObservable(); + + init(navscrollItems: Array) { + const flattenItems = flattenNavscrollItems(navscrollItems); + //the first item is selected by default + const selected = ((flattenItems && flattenItems.length && flattenItems[0]) as NavscrollItem) ?? undefined; + + const state = { + items: new Set(flattenItems), + active: selected ? [selected] : [], + selected: selected, + progressBar: 0, + isMobile: false, + }; + + this.#state.next(state); + } + + setActive(item: NavscrollItem) { + const { items } = this.#state.value; + + const active = search(items, item); + const state = this.#state.value; + this.#state.next({ ...state, items, selected: item, active }); + } + + isActive$(item: NavscrollItem) { + return this.#state.asObservable().pipe(map(state => state.active.includes(item))); + } + + updateProgressBar(container: HTMLElement) { + if (!container) { + return; + } + const offset = Math.abs(container.getBoundingClientRect().top); + const height = container.getBoundingClientRect().height; + const scrollAmount = (offset / height) * 100; + const scrollValue = Math.min(100, Math.max(0, scrollAmount)); + const state = this.#state.value; + this.#state.next({ ...state, progressBar: scrollValue }); + } + + selectMenuItem() { + this.#menuItemSelected.next(undefined); + } + + setMobile({ innerWidth }: { innerWidth: number }) { + const isLessThan992px = innerWidth < 992; + const isMobile = isLessThan992px; + const state = this.#state.value; + this.#state.next({ ...state, isMobile }); + } +} diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.utils.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.utils.ts new file mode 100644 index 00000000..bb4fd13f --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.utils.ts @@ -0,0 +1,28 @@ +import { NavscrollItem, NavscrollItems } from './navscroll.model'; + +function searchFn(items: Set, item: NavscrollItem): Array { + //ricerca + const nodes = Array.from(items); + const parent = nodes.find(i => i.childs.includes(item)); + const ancestors = parent?.childs?.length ? searchFn(items, parent) : []; + return [item, ...ancestors]; +} + +function flattenNavscrollItemsFn(items: NavscrollItems): NavscrollItems { + const result: NavscrollItems = []; + + function flatten(items: NavscrollItems): void { + for (const item of items) { + result.push(item); + if (item.childs && item.childs.length > 0) { + flatten(item.childs); + } + } + } + + flatten(items); + return result; +} + +export const search = searchFn; +export const flattenNavscrollItems = flattenNavscrollItemsFn; diff --git a/projects/design-angular-kit/src/lib/design-angular-kit.module.ts b/projects/design-angular-kit/src/lib/design-angular-kit.module.ts index 0b4a697f..da5a5e3b 100644 --- a/projects/design-angular-kit/src/lib/design-angular-kit.module.ts +++ b/projects/design-angular-kit/src/lib/design-angular-kit.module.ts @@ -1,6 +1,4 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; -import { DesignAngularKitConfig } from './interfaces/design-angular-kit-config'; -import { provideDesignAngularKit } from './provide-design-angular-kit'; import { ItAccordionComponent } from './components/core/accordion/accordion.component'; import { ItAlertComponent } from './components/core/alert/alert.component'; import { ItAvatarModule } from './components/core/avatar/avatar.module'; @@ -16,7 +14,6 @@ import { ItDropdownModule } from './components/core/dropdown/dropdown.module'; import { ItForwardDirective } from './components/core/forward/forward.directive'; import { ItLinkComponent } from './components/core/link/link.component'; import { ItListModule } from './components/core/list/list.module'; -import { ItMegamenuComponent } from './components/navigation/megamenu/megamenu.component'; import { ItModalComponent } from './components/core/modal/modal.component'; import { ItNotificationsComponent } from './components/core/notifications/notifications.component'; import { ItPaginationComponent } from './components/core/pagination/pagination.component'; @@ -27,21 +24,25 @@ import { ItSpinnerComponent } from './components/core/spinner/spinner.component' import { ItSteppersModule } from './components/core/steppers/steppers.module'; import { ItTabModule } from './components/core/tab/tab.module'; import { ItTableModule } from './components/core/table/table.module'; +import { ItTimelineModule } from './components/core/timeline/timeline.module'; import { ItTooltipDirective } from './components/core/tooltip/tooltip.directive'; import { ItFormModule } from './components/form/form.module'; import { ItBackButtonComponent } from './components/navigation/back-button/back-button.component'; import { ItBackToTopComponent } from './components/navigation/back-to-top/back-to-top.component'; import { ItBreadcrumbsModule } from './components/navigation/breadcrumbs/breadcrumbs.module'; import { ItHeaderComponent } from './components/navigation/header/header.component'; +import { ItMegamenuComponent } from './components/navigation/megamenu/megamenu.component'; import { ItNavBarModule } from './components/navigation/navbar/navbar.module'; +import { ItNavscrollComponent } from './components/navigation/navscroll/navscroll.component'; import { ItSidebarComponent } from './components/navigation/sidebar/sidebar.component'; import { ItErrorPageComponent } from './components/utils/error-page/error-page.component'; import { ItIconComponent } from './components/utils/icon/icon.component'; import { ItLanguageSwitcherComponent } from './components/utils/language-switcher/language-switcher.component'; +import { DesignAngularKitConfig } from './interfaces/design-angular-kit-config'; import { ItDateAgoPipe } from './pipes/date-ago.pipe'; import { ItDurationPipe } from './pipes/duration.pipe'; import { ItMarkMatchingTextPipe } from './pipes/mark-matching-text.pipe'; -import { ItTimelineModule } from './components/core/timeline/timeline.module'; +import { provideDesignAngularKit } from './provide-design-angular-kit'; /** * Core components @@ -87,6 +88,7 @@ const navigation = [ ItNavBarModule, ItSidebarComponent, ItMegamenuComponent, + ItNavscrollComponent, ]; /** diff --git a/projects/design-angular-kit/src/public_api.ts b/projects/design-angular-kit/src/public_api.ts index 63fbfc95..e5994c59 100644 --- a/projects/design-angular-kit/src/public_api.ts +++ b/projects/design-angular-kit/src/public_api.ts @@ -100,6 +100,9 @@ export * from './lib/components/navigation/navbar/navbar-item/navbar-item.compon export * from './lib/components/navigation/navbar/navbar.module'; export * from './lib/components/navigation/navbar/navbar/navbar.component'; +export * from './lib/components/navigation/navscroll/navscroll.component'; +export * from './lib/components/navigation/navscroll/navscroll.model'; + export * from './lib/components/navigation/sidebar/sidebar.component'; // Utils components diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index ec7d72cb..f2ea5c5c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,7 +1,8 @@ import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { RouterDispatcherComponent } from './router-dispatcher/router-dispatcher.component'; +import { RouterModule, Routes } from '@angular/router'; import { ItErrorPageComponent } from 'design-angular-kit/components/utils/error-page/error-page.component'; +import { EXAMPLES_ROUTES } from './examples/routes'; +import { RouterDispatcherComponent } from './router-dispatcher/router-dispatcher.component'; const routes: Routes = [ { path: '', redirectTo: 'info', pathMatch: 'full' }, @@ -68,9 +69,11 @@ const routes: Routes = [ { path: 'autocomplete', loadChildren: () => import('src/app/autocomplete/autocomplete.module').then(m => m.AutocompleteModule) }, { path: 'sidebar', loadChildren: () => import('src/app/sidebar/sidebar.module').then(m => m.SidebarModule) }, { path: 'timeline', loadChildren: () => import('src/app/timeline/timeline.module').then(m => m.TimelineModule) }, + { path: 'navscroll', loadChildren: () => import('src/app/navscroll/navscroll.module').then(m => m.NavscrollModule) }, { path: 'transfer', loadChildren: () => import('src/app/transfer/transfer.module').then(m => m.TransferModule) }, ], }, + { path: 'esempi', component: RouterDispatcherComponent, children: EXAMPLES_ROUTES }, { path: 'error/not-found', component: ItErrorPageComponent, data: { errorCode: 404 } }, { path: 'error/forbidden', component: ItErrorPageComponent, data: { errorCode: 403 } }, { path: 'error/server-error', component: ItErrorPageComponent, data: { errorCode: 500 } }, diff --git a/src/app/app.component.html b/src/app/app.component.html index 85f39617..de0d8d15 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -79,8 +79,9 @@

data-focus-mouse="false"> v{{ version }} - Changelog + + + Changelog diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4ea33e56..8ecb71f8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,19 +1,21 @@ -import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { NgTemplateOutlet } from '@angular/common'; +import { TranslateStore } from '@ngx-translate/core'; +import { DesignAngularKitModule } from 'design-angular-kit/design-angular-kit.module'; +import { HIGHLIGHT_OPTIONS } from 'ngx-highlightjs'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; -import { TableOfContentComponent } from './table-of-content/table-of-content.component'; -import { TableOfContentItemComponent } from './table-of-content-item/table-of-content-item.component'; -import { RouterDispatcherComponent } from './router-dispatcher/router-dispatcher.component'; +import { EXAMPLES_MODULES } from './examples'; import { LinkSortPipe } from './link-sort.pipe'; -import { HIGHLIGHT_OPTIONS } from 'ngx-highlightjs'; -import { TranslateStore } from '@ngx-translate/core'; -import { DesignAngularKitModule } from 'design-angular-kit/design-angular-kit.module'; +import { RouterDispatcherComponent } from './router-dispatcher/router-dispatcher.component'; +import { TableOfContentItemComponent } from './table-of-content-item/table-of-content-item.component'; +import { TableOfContentComponent } from './table-of-content/table-of-content.component'; @NgModule({ declarations: [AppComponent, TableOfContentComponent, TableOfContentItemComponent, RouterDispatcherComponent, LinkSortPipe], - imports: [BrowserModule, AppRoutingModule, DesignAngularKitModule.forRoot()], + imports: [BrowserModule, AppRoutingModule, DesignAngularKitModule.forRoot(), NgTemplateOutlet, ...EXAMPLES_MODULES], providers: [ TranslateStore, { diff --git a/src/app/examples/index.ts b/src/app/examples/index.ts new file mode 100644 index 00000000..2b113dce --- /dev/null +++ b/src/app/examples/index.ts @@ -0,0 +1,3 @@ +import { NavscrollExampleModule } from './navscroll-example/navscroll-example.module'; + +export const EXAMPLES_MODULES = [NavscrollExampleModule]; diff --git a/src/app/examples/navscroll-example/navscroll-example.component.ts b/src/app/examples/navscroll-example/navscroll-example.component.ts new file mode 100644 index 00000000..df0e7568 --- /dev/null +++ b/src/app/examples/navscroll-example/navscroll-example.component.ts @@ -0,0 +1,94 @@ +import { Component } from '@angular/core'; +import { NavscrollItems } from 'projects/design-angular-kit/src/public_api'; + +const template = ` + + +

Documentazione

+`; + +@Component({ + template, +}) +export class NavscrollExampleComponent { + readonly header = 'Default example'; + readonly items = [ + { + title: 'Prima Sezione', + text: 'Testo prima sezione. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 'primaSezione', + childs: [ + { + title: 'Sottosezione 1.1', + text: 'Testo sottosezione 1.1. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's11', + childs: [ + { + title: 'Sottosezione 1.1.1', + text: 'Testo sottosezione 1.1.1. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's111', + childs: [ + { + title: 'Sottosezione 1.1.1.1', + text: 'Testo sottosezione 1.1.1.1. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's1111', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 1.1.2', + text: 'Testo sottosezione 1.1.2. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's112', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 1.2', + text: 'Testo sottosezione 1.2. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's12', + childs: [], + }, + { + title: 'Sottosezione 1.3', + text: 'Testo sottosezione 1.3. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's13', + childs: [], + }, + ], + }, + { + title: 'Seconda Sezione', + text: 'Testo seconda sezione. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 'secondaSezione', + childs: [ + { + title: 'Sottosezione 2.1', + text: 'Testo sottosezione 2.1. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's21', + childs: [ + { + title: 'Sottosezione 2.1.1', + text: 'Testo sottosezione 2.1.1. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's211', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 2.2', + text: 'Testo sottosezione 2.2. Proin placerat ipsum massa, ac commodo velit tempor quis. In ante augue, sodales ac rhoncus in, ultricies a neque. Morbi non semper felis, at lacinia nibh. Nam quis elit massa. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aliquam laoreet, diam quis blandit porttitor, leo erat semper sem, vel sagittis dolor quam eu magna. Nunc feugiat pretium tempor. Nam eget augue quis tellus viverra malesuada vel ut quam. Cras vehicula rutrum vehicula. Suspendisse efficitur eget purus vitae convallis. Integer euismod pharetra lorem, non ullamcorper lorem euismod vel. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.', + href: 's22', + childs: [], + }, + ], + }, + ] satisfies NavscrollItems; +} diff --git a/src/app/examples/navscroll-example/navscroll-example.module.ts b/src/app/examples/navscroll-example/navscroll-example.module.ts new file mode 100644 index 00000000..1a0e366c --- /dev/null +++ b/src/app/examples/navscroll-example/navscroll-example.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { DesignAngularKitModule } from 'projects/design-angular-kit/src/public_api'; +import { NavscrollExampleComponent } from './navscroll-example.component'; + +@NgModule({ + declarations: [NavscrollExampleComponent], + exports: [NavscrollExampleComponent], + imports: [DesignAngularKitModule, RouterModule], +}) +export class NavscrollExampleModule {} diff --git a/src/app/examples/routes.ts b/src/app/examples/routes.ts new file mode 100644 index 00000000..e2bc08b4 --- /dev/null +++ b/src/app/examples/routes.ts @@ -0,0 +1,4 @@ +import { Routes } from '@angular/router'; +import { NavscrollExampleComponent } from './navscroll-example/navscroll-example.component'; + +export const EXAMPLES_ROUTES = [{ path: 'navscroll', component: NavscrollExampleComponent }] satisfies Routes; diff --git a/src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.html b/src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.html new file mode 100644 index 00000000..1db105d4 --- /dev/null +++ b/src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.html @@ -0,0 +1,6 @@ + +

This is a custom template

+

All the paragraphs will be displayed here

+
+ + diff --git a/src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.ts b/src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.ts new file mode 100644 index 00000000..9acf7b8e --- /dev/null +++ b/src/app/navscroll/navscroll-custom-template-example/navscroll-custom-template-example.component.ts @@ -0,0 +1,76 @@ +import { Component } from '@angular/core'; +import { NavscrollItems } from 'projects/design-angular-kit/src/public_api'; + +@Component({ + selector: 'it-navscroll-custom-template-example', + templateUrl: './navscroll-custom-template-example.component.html', +}) +export class NavscrollCustomTemplateExampleComponent { + readonly header = 'Custom template example'; + readonly items = [ + { + title: 'Prima Sezione', + text: 'Testo prima sezione', + href: '#', + childs: [ + { + title: 'Sottosezione 1.1', + text: 'Testo sottosezione 1.1', + href: '#', + childs: [ + { + title: 'Sottosezione 1.1.1', + text: 'Testo sottosezione 1.1.1', + href: '#', + childs: [ + { + title: 'Sottosezione 1.1.1.1', + text: 'Testo sottosezione 1.1.1.1', + href: '#', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 1.1.2', + text: 'Testo sottosezione 1.1.2', + href: '#', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 1.2', + text: 'Testo sottosezione 1.2', + href: '#', + childs: [], + }, + { + title: 'Sottosezione 1.3', + text: 'Testo sottosezione 1.3', + href: '#', + childs: [], + }, + ], + }, + { + title: 'Seconda Sezione', + text: 'Testo seconda sezione', + href: '#', + childs: [ + { + title: 'Sottosezione 2.1', + text: 'Testo sottosezione 2.1', + href: '#', + childs: [], + }, + { + title: 'Sottosezione 2.2', + text: 'Testo sottosezione 2.2', + href: '#', + childs: [], + }, + ], + }, + ] satisfies NavscrollItems; +} diff --git a/src/app/navscroll/navscroll-default-example/navscroll-default-example.component.html b/src/app/navscroll/navscroll-default-example/navscroll-default-example.component.html new file mode 100644 index 00000000..30bd7798 --- /dev/null +++ b/src/app/navscroll/navscroll-default-example/navscroll-default-example.component.html @@ -0,0 +1 @@ + diff --git a/src/app/navscroll/navscroll-default-example/navscroll-default-example.component.ts b/src/app/navscroll/navscroll-default-example/navscroll-default-example.component.ts new file mode 100644 index 00000000..7612c317 --- /dev/null +++ b/src/app/navscroll/navscroll-default-example/navscroll-default-example.component.ts @@ -0,0 +1,76 @@ +import { Component } from '@angular/core'; +import { NavscrollItems } from 'projects/design-angular-kit/src/public_api'; + +@Component({ + selector: 'it-navscroll-default-example', + templateUrl: './navscroll-default-example.component.html', +}) +export class NavscrollDefaultExampleComponent { + readonly header = 'Default example'; + readonly items = [ + { + title: 'Prima Sezione', + text: 'Testo prima sezione', + href: '#', + childs: [ + { + title: 'Sottosezione 1.1', + text: 'Testo sottosezione 1.1', + href: '#', + childs: [ + { + title: 'Sottosezione 1.1.1', + text: 'Testo sottosezione 1.1.1', + href: '#', + childs: [ + { + title: 'Sottosezione 1.1.1.1', + text: 'Testo sottosezione 1.1.1.1', + href: '#', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 1.1.2', + text: 'Testo sottosezione 1.1.2', + href: '#', + childs: [], + }, + ], + }, + { + title: 'Sottosezione 1.2', + text: 'Testo sottosezione 1.2', + href: '#', + childs: [], + }, + { + title: 'Sottosezione 1.3', + text: 'Testo sottosezione 1.3', + href: '#', + childs: [], + }, + ], + }, + { + title: 'Seconda Sezione', + text: 'Testo seconda sezione', + href: '#', + childs: [ + { + title: 'Sottosezione 2.1', + text: 'Testo sottosezione 2.1', + href: '#', + childs: [], + }, + { + title: 'Sottosezione 2.2', + text: 'Testo sottosezione 2.2', + href: '#', + childs: [], + }, + ], + }, + ] satisfies NavscrollItems; +} diff --git a/src/app/navscroll/navscroll-examples/navscroll-examples.component.scss b/src/app/navscroll/navscroll-examples/navscroll-examples.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/navscroll/navscroll-examples/navscroll-examples.component.tpl b/src/app/navscroll/navscroll-examples/navscroll-examples.component.tpl new file mode 100644 index 00000000..18945d94 --- /dev/null +++ b/src/app/navscroll/navscroll-examples/navscroll-examples.component.tpl @@ -0,0 +1,30 @@ +{% from "../../macro.template.njk" import sanitize as sanitize %} + +{% set htmlColor %} + {% include "../navscroll-default-example/navscroll-default-example.component.html" %} +{% endset %} + +{% set typescriptColor %} + {% include "../navscroll-default-example/navscroll-default-example.component.ts" %} +{% endset %} + +

Esempio di default

+ + + + + +{% set customTemplateHtmlColor %} + {% include "../navscroll-custom-template-example/navscroll-custom-template-example.component.html" %} +{% endset %} + +{% set customTemplateTypescriptColor %} + {% include "../navscroll-custom-template-example/navscroll-custom-template-example.component.ts" %} +{% endset %} + +

Esempio con template personalizzato

+ + + + + diff --git a/src/app/navscroll/navscroll-examples/navscroll-examples.component.ts b/src/app/navscroll/navscroll-examples/navscroll-examples.component.ts new file mode 100644 index 00000000..b405a8e1 --- /dev/null +++ b/src/app/navscroll/navscroll-examples/navscroll-examples.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'it-navscroll-examples', + templateUrl: './navscroll-examples.component.html', + styleUrl: './navscroll-examples.component.scss', +}) +export class NavscrollExamplesComponent {} diff --git a/src/app/navscroll/navscroll-index/navscroll-index.component.html b/src/app/navscroll/navscroll-index/navscroll-index.component.html new file mode 100644 index 00000000..87d8ea6c --- /dev/null +++ b/src/app/navscroll/navscroll-index/navscroll-index.component.html @@ -0,0 +1,12 @@ +

Navscroll

+

Componente che presenta liste di link con ancore a sezioni della pagina in cui è contenuto.

+
+

Esempio funzionante

+ + + + + + + + diff --git a/src/app/navscroll/navscroll-index/navscroll-index.component.ts b/src/app/navscroll/navscroll-index/navscroll-index.component.ts new file mode 100644 index 00000000..1a0d2455 --- /dev/null +++ b/src/app/navscroll/navscroll-index/navscroll-index.component.ts @@ -0,0 +1,14 @@ +import { Component } from '@angular/core'; +import Documentation from '../../../assets/documentation.json'; + +@Component({ + selector: 'it-navscroll-index', + templateUrl: './navscroll-index.component.html', +}) +export class NavscrollIndexComponent { + component: any; + + constructor() { + this.component = (Documentation).components.find(component => component.name === 'ItNavscrollComponent'); + } +} diff --git a/src/app/navscroll/navscroll-routing.module.ts b/src/app/navscroll/navscroll-routing.module.ts new file mode 100644 index 00000000..ce6dd05c --- /dev/null +++ b/src/app/navscroll/navscroll-routing.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { NavscrollIndexComponent } from './navscroll-index/navscroll-index.component'; + +export const ROUTES = [{ path: '', component: NavscrollIndexComponent }] satisfies Routes; + +@NgModule({ + imports: [RouterModule.forChild(ROUTES)], + exports: [RouterModule], +}) +export class NavscrollRoutingModule {} diff --git a/src/app/navscroll/navscroll.module.ts b/src/app/navscroll/navscroll.module.ts new file mode 100644 index 00000000..ae323f9e --- /dev/null +++ b/src/app/navscroll/navscroll.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from '../shared/shared.module'; +import { NavscrollDefaultExampleComponent } from './navscroll-default-example/navscroll-default-example.component'; +import { NavscrollExamplesComponent } from './navscroll-examples/navscroll-examples.component'; +import { NavscrollIndexComponent } from './navscroll-index/navscroll-index.component'; +import { NavscrollRoutingModule } from './navscroll-routing.module'; + +@NgModule({ + imports: [NavscrollRoutingModule, SharedModule], + declarations: [NavscrollIndexComponent, NavscrollExamplesComponent, NavscrollDefaultExampleComponent], +}) +export class NavscrollModule {} diff --git a/src/assets/table-of-content.json b/src/assets/table-of-content.json index cb284d99..e091ac80 100644 --- a/src/assets/table-of-content.json +++ b/src/assets/table-of-content.json @@ -187,6 +187,10 @@ "label": "Timeline", "link": "/componenti/timeline" }, + { + "label": "Navscroll", + "link": "/componenti/navscroll" + }, { "label": "Transfer", "link": "/componenti/transfer" From 251083aef743a5f22916b6ecc30eec0d1c6d2592 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 14:50:01 +0100 Subject: [PATCH 05/21] docs: change placeholder service --- .../card/card-image/card-image.component.html | 8 ++--- .../card-special/card-special.component.html | 2 +- ...usel-featured-image-example.component.html | 15 ++-------- ...el-fullscreen-image-example.component.html | 30 ++++--------------- ...reen-image-standard-example.component.html | 30 ++++--------------- ...arousel-image-above-example.component.html | 30 ++++--------------- ...ousel-special-cards-example.component.html | 30 ++++--------------- .../header-example.component.html | 2 +- .../list/list-image/list-image.component.html | 6 ++-- .../megamenu-cta-bottom.component.html | 2 +- .../megamenu-cta-right.component.html | 2 +- .../megamenu-example.component.html | 2 +- 12 files changed, 39 insertions(+), 120 deletions(-) diff --git a/src/app/card/card-image/card-image.component.html b/src/app/card/card-image/card-image.component.html index e42da187..e185fc9c 100644 --- a/src/app/card/card-image/card-image.component.html +++ b/src/app/card/card-image/card-image.component.html @@ -11,7 +11,7 @@

Card con immagine

descrizione immagine
@@ -36,7 +36,7 @@

Lorem ipsum dolor sit amet, consectetur adipiscing eli
descrizione immagine
@@ -61,7 +61,7 @@

Lorem ipsum dolor sit amet, consectetur adipiscing eli
descrizione immagine
@@ -90,7 +90,7 @@

Lorem ipsum dolor sit amet, consectetur adipiscing eli
descrizione immagine
diff --git a/src/app/card/card-special/card-special.component.html b/src/app/card/card-special/card-special.component.html index 3fd29813..4e8b0e2e 100644 --- a/src/app/card/card-special/card-special.component.html +++ b/src/app/card/card-special/card-special.component.html @@ -11,7 +11,7 @@

Card speciale

descrizione immagine
diff --git a/src/app/carousel/carousel-featured-image-example/carousel-featured-image-example.component.html b/src/app/carousel/carousel-featured-image-example/carousel-featured-image-example.component.html index 3c9f88bb..4580dbb8 100644 --- a/src/app/carousel/carousel-featured-image-example/carousel-featured-image-example.component.html +++ b/src/app/carousel/carousel-featured-image-example/carousel-featured-image-example.component.html @@ -6,10 +6,7 @@

Card con immagine in evidenza

- descrizione immagine + descrizione immagine
@@ -42,10 +39,7 @@
Lorem ipsum dolor sit amet, consectetur adipi
- descrizione immagine + descrizione immagine
@@ -78,10 +72,7 @@
Lorem ipsum dolor sit amet, consectetur adipi
- descrizione immagine + descrizione immagine
diff --git a/src/app/carousel/carousel-fullscreen-image-example/carousel-fullscreen-image-example.component.html b/src/app/carousel/carousel-fullscreen-image-example/carousel-fullscreen-image-example.component.html index 66172a94..cc0ea07d 100644 --- a/src/app/carousel/carousel-fullscreen-image-example/carousel-fullscreen-image-example.component.html +++ b/src/app/carousel/carousel-fullscreen-image-example/carousel-fullscreen-image-example.component.html @@ -7,10 +7,7 @@

Fullscreen di immagini

- descrizione immagine + descrizione immagine
@@ -24,10 +21,7 @@

Fullscreen di immagini

- descrizione immagine + descrizione immagine
@@ -41,10 +35,7 @@

Fullscreen di immagini

- descrizione immagine + descrizione immagine
@@ -58,10 +49,7 @@

Fullscreen di immagini

- descrizione immagine + descrizione immagine
@@ -75,10 +63,7 @@

Fullscreen di immagini

- descrizione immagine + descrizione immagine
@@ -92,10 +77,7 @@

Fullscreen di immagini

- descrizione immagine + descrizione immagine
diff --git a/src/app/carousel/carousel-fullscreen-image-standard-example/carousel-fullscreen-image-standard-example.component.html b/src/app/carousel/carousel-fullscreen-image-standard-example/carousel-fullscreen-image-standard-example.component.html index 082481e8..be3c33e0 100644 --- a/src/app/carousel/carousel-fullscreen-image-standard-example/carousel-fullscreen-image-standard-example.component.html +++ b/src/app/carousel/carousel-fullscreen-image-standard-example/carousel-fullscreen-image-standard-example.component.html @@ -7,10 +7,7 @@

Fullscreen di immagini di dimensioni standard

- descrizione immagine + descrizione immagine
@@ -24,10 +21,7 @@

Fullscreen di immagini di dimensioni standard

- descrizione immagine + descrizione immagine
@@ -41,10 +35,7 @@

Fullscreen di immagini di dimensioni standard

- descrizione immagine + descrizione immagine
@@ -58,10 +49,7 @@

Fullscreen di immagini di dimensioni standard

- descrizione immagine + descrizione immagine
@@ -75,10 +63,7 @@

Fullscreen di immagini di dimensioni standard

- descrizione immagine + descrizione immagine
@@ -92,10 +77,7 @@

Fullscreen di immagini di dimensioni standard

- descrizione immagine + descrizione immagine
diff --git a/src/app/carousel/carousel-image-above-example/carousel-image-above-example.component.html b/src/app/carousel/carousel-image-above-example/carousel-image-above-example.component.html index 21c9c64b..ca84e292 100644 --- a/src/app/carousel/carousel-image-above-example/carousel-image-above-example.component.html +++ b/src/app/carousel/carousel-image-above-example/carousel-image-above-example.component.html @@ -7,10 +7,7 @@

Card con immagine in alto

- descrizione immagine + descrizione immagine
@@ -31,10 +28,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -55,10 +49,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -79,10 +70,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -103,10 +91,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -127,10 +112,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
diff --git a/src/app/carousel/carousel-special-cards-example/carousel-special-cards-example.component.html b/src/app/carousel/carousel-special-cards-example/carousel-special-cards-example.component.html index 1363455e..3c7b4781 100644 --- a/src/app/carousel/carousel-special-cards-example/carousel-special-cards-example.component.html +++ b/src/app/carousel/carousel-special-cards-example/carousel-special-cards-example.component.html @@ -7,10 +7,7 @@

Card speciali

- descrizione immagine + descrizione immagine
@@ -27,10 +24,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -47,10 +41,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -67,10 +58,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -87,10 +75,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
@@ -107,10 +92,7 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit
- descrizione immagine + descrizione immagine
diff --git a/src/app/header/header-example/header-example.component.html b/src/app/header/header-example/header-example.component.html index 3cf8c040..cec13ea1 100644 --- a/src/app/header/header-example/header-example.component.html +++ b/src/app/header/header-example/header-example.component.html @@ -75,7 +75,7 @@

Componente Header

Segnaposto
diff --git a/src/app/list/list-image/list-image.component.html b/src/app/list/list-image/list-image.component.html index 7627b8cc..577980da 100644 --- a/src/app/list/list-image/list-image.component.html +++ b/src/app/list/list-image/list-image.component.html @@ -3,13 +3,13 @@

Lista con Immagini

- + Lista 1 - + Lista 2 - + Lista 3 diff --git a/src/app/megamenu/megamenu-cta-bottom/megamenu-cta-bottom.component.html b/src/app/megamenu/megamenu-cta-bottom/megamenu-cta-bottom.component.html index 7787d48e..483a0b21 100644 --- a/src/app/megamenu/megamenu-cta-bottom/megamenu-cta-bottom.component.html +++ b/src/app/megamenu/megamenu-cta-bottom/megamenu-cta-bottom.component.html @@ -11,7 +11,7 @@

Componente Megamenu con CTA in basso

Segnaposto
diff --git a/src/app/megamenu/megamenu-cta-right/megamenu-cta-right.component.html b/src/app/megamenu/megamenu-cta-right/megamenu-cta-right.component.html index 83b16ef4..62bdf9c2 100644 --- a/src/app/megamenu/megamenu-cta-right/megamenu-cta-right.component.html +++ b/src/app/megamenu/megamenu-cta-right/megamenu-cta-right.component.html @@ -11,7 +11,7 @@

Componente Megamenu con CTA a destra

Segnaposto
diff --git a/src/app/megamenu/megamenu-example/megamenu-example.component.html b/src/app/megamenu/megamenu-example/megamenu-example.component.html index 35f0dfd1..ebb00c09 100644 --- a/src/app/megamenu/megamenu-example/megamenu-example.component.html +++ b/src/app/megamenu/megamenu-example/megamenu-example.component.html @@ -11,7 +11,7 @@

Componente Megamenu completo

Segnaposto
From 20a2129ce4bfc12379896909a76f8ff665f4fd4c Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 16:00:07 +0100 Subject: [PATCH 06/21] chore: upgrade bsi --- package-lock.json | 9 ++++----- package.json | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7e867ee2..0ba32e1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@angular/router": "^18.0.6", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", - "bootstrap-italia": "^2.8.8", + "bootstrap-italia": "^2.10.0", "core-js": "^3.37.1", "fp-ts": "^2.16.8", "io-ts": "^2.2.21", @@ -8390,10 +8390,9 @@ } }, "node_modules/bootstrap-italia": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/bootstrap-italia/-/bootstrap-italia-2.8.8.tgz", - "integrity": "sha512-2NJqPQRnyO2hb5stRNRXk/kQku42vLBKC8CGgDoekD9Fb8UALfeicpZiL0wqDdT/MxFwbQNz3sDLGjJKcXesXA==", - "license": "BSD-3-Clause", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/bootstrap-italia/-/bootstrap-italia-2.10.0.tgz", + "integrity": "sha512-ULYyv1/hC7/0t8jmfbzmCltC5c2ejnQKLhLQ2TirMW2VFOTklFifLB1mj64nr5TAe1kuIx1zRxcwoeNYC3fHdg==", "dependencies": { "@popperjs/core": "^2.11.2", "@splidejs/splide": "^3.6.12", diff --git a/package.json b/package.json index a48ef1fb..b2933f3c 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@angular/router": "^18.0.6", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", - "bootstrap-italia": "^2.8.8", + "bootstrap-italia": "^2.10.0", "core-js": "^3.37.1", "fp-ts": "^2.16.8", "io-ts": "^2.2.21", From 7a5adfa3490d37ef9529f8a2c3ff0048e6c06caa Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 16:15:18 +0100 Subject: [PATCH 07/21] fix: forward example component --- .../forward-example.component.html | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/app/forward/forward-example/forward-example.component.html b/src/app/forward/forward-example/forward-example.component.html index cdef24ae..1d2c5dc5 100644 --- a/src/app/forward/forward-example/forward-example.component.html +++ b/src/app/forward/forward-example/forward-example.component.html @@ -1,18 +1,10 @@

Configurazione forward

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

From 1b91fb278acf6a90abd0a5dd2ff14f28005e2f38 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 16:29:33 +0100 Subject: [PATCH 08/21] fix: dimmer primary color --- .../src/lib/components/core/dimmer/dimmer.component.html | 2 +- .../src/lib/components/core/dimmer/dimmer.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.html b/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.html index 16b66b5c..e59a9909 100644 --- a/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.html +++ b/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.html @@ -1,5 +1,5 @@ @if (active) { -

+
diff --git a/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.ts b/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.ts index d6f5f60a..84dbc20f 100644 --- a/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.ts +++ b/projects/design-angular-kit/src/lib/components/core/dimmer/dimmer.component.ts @@ -11,7 +11,7 @@ export type DimmerColor = '' | 'dimmer-primary'; changeDetection: ChangeDetectionStrategy.OnPush, animations: [ trigger('fade', [ - transition(':enter', [style({ opacity: 0 }), animate('150ms', style({ opacity: 0.92 }))]), + transition(':enter', [style({ opacity: 0 }), animate('150ms', style({ opacity: 0.9 }))]), transition(':leave', [animate('150ms', style({ opacity: 0 }))]), ]), ], From 280434bf900cdaf1fa2b388ed7ba61c0264cb4ee Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Thu, 31 Oct 2024 16:40:33 +0100 Subject: [PATCH 09/21] docs: add button with badge example --- src/app/badge/badge-example/badge-example.component.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/app/badge/badge-example/badge-example.component.html b/src/app/badge/badge-example/badge-example.component.html index becec07a..ec5bcb26 100644 --- a/src/app/badge/badge-example/badge-example.component.html +++ b/src/app/badge/badge-example/badge-example.component.html @@ -36,6 +36,10 @@
Link di esempio link
+
From e068958cb1adf10503289300dc7b3c0cd32fbd49 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Thu, 31 Oct 2024 18:09:39 +0000 Subject: [PATCH 10/21] chore(release): 1.1.1 :tada: [skip ci] ## [1.1.1](https://github.com/italia/design-angular-kit/compare/v1.1.0...v1.1.1) (2024-10-31) ### Bug Fixes * dimmer primary color ([1b91fb2](https://github.com/italia/design-angular-kit/commit/1b91fb278acf6a90abd0a5dd2ff14f28005e2f38)) * forward example component ([7a5adfa](https://github.com/italia/design-angular-kit/commit/7a5adfa3490d37ef9529f8a2c3ff0048e6c06caa)) * groups for radios toggles and checkboxes ([1b65209](https://github.com/italia/design-angular-kit/commit/1b65209414a8fcdba00bb3ae5fd3b5031570f06c)) ### Features * add angular schematics ([7e81151](https://github.com/italia/design-angular-kit/commit/7e81151da547a6e167101749d1aeb0395da03515)) * component transfer ([bb26b09](https://github.com/italia/design-angular-kit/commit/bb26b09f7199cfd34ddaa9c9a8a71229af326f45)) * navscroll component ([15c8caa](https://github.com/italia/design-angular-kit/commit/15c8caa658923bb21cc60c561843aff73405b623)) --- CHANGELOG.md | 16 ++++++++++++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e41f5e..9a70a58c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,19 @@ +## [1.1.1](https://github.com/italia/design-angular-kit/compare/v1.1.0...v1.1.1) (2024-10-31) + + +### Bug Fixes + +* dimmer primary color ([1b91fb2](https://github.com/italia/design-angular-kit/commit/1b91fb278acf6a90abd0a5dd2ff14f28005e2f38)) +* forward example component ([7a5adfa](https://github.com/italia/design-angular-kit/commit/7a5adfa3490d37ef9529f8a2c3ff0048e6c06caa)) +* groups for radios toggles and checkboxes ([1b65209](https://github.com/italia/design-angular-kit/commit/1b65209414a8fcdba00bb3ae5fd3b5031570f06c)) + + +### Features + +* add angular schematics ([7e81151](https://github.com/italia/design-angular-kit/commit/7e81151da547a6e167101749d1aeb0395da03515)) +* component transfer ([bb26b09](https://github.com/italia/design-angular-kit/commit/bb26b09f7199cfd34ddaa9c9a8a71229af326f45)) +* navscroll component ([15c8caa](https://github.com/italia/design-angular-kit/commit/15c8caa658923bb21cc60c561843aff73405b623)) + # [1.1.0](https://github.com/italia/design-angular-kit/compare/v1.0.3...v1.1.0) (2024-07-18) diff --git a/package-lock.json b/package-lock.json index 0ba32e1a..2d365ac7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "design-angular-kit-bundle", - "version": "1.1.0", + "version": "1.1.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "design-angular-kit-bundle", - "version": "1.1.0", + "version": "1.1.1", "license": "BSD-3-Clause", "dependencies": { "@angular-devkit/schematics": "^18.0.7", diff --git a/package.json b/package.json index b2933f3c..75e352ac 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "design-angular-kit-bundle", "description": "Un toolkit Angular conforme alle linee guida di design per i servizi web della PA", "author": "Presidenza del Consiglio dei Ministri", - "version": "1.1.0", + "version": "1.1.1", "license": "BSD-3-Clause", "scripts": { "ng": "ng", From 10939d7ad3ef15750a609896b73ed3669a809cb2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2024 12:58:37 +0100 Subject: [PATCH 11/21] chore: update software version in publiccode Co-authored-by: astagi --- publiccode.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/publiccode.yml b/publiccode.yml index 572b575d..52c8df10 100644 --- a/publiccode.yml +++ b/publiccode.yml @@ -62,8 +62,8 @@ maintenance: name: Design Angular Kit platforms: - web -releaseDate: '2024-07-18' +releaseDate: '2024-10-31' roadmap: 'https://github.com/italia/design-angular-kit/issues' softwareType: library -softwareVersion: v1.1.0 +softwareVersion: v1.1.1 url: 'https://github.com/italia/design-angular-kit' From fceffc5e8467085de760300d303845f73ceb9493 Mon Sep 17 00:00:00 2001 From: Valerio Date: Wed, 6 Nov 2024 15:25:35 +0100 Subject: [PATCH 12/21] fix: readonly input problem in navscroll --- .../navigation/navscroll/navscroll.component.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts index 41a1016c..0e766a45 100644 --- a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts @@ -44,27 +44,27 @@ export class ItNavscrollComponent implements OnInit { /** * Header of the Navscroll */ - @Input() readonly header = ''; + @Input() header = ''; /** * A list of links */ - @Input() readonly items!: Array; + @Input() items!: Array; /** * Border position * @default left */ - @Input() readonly borderPosition: 'left' | 'right' = 'left'; + @Input() borderPosition: 'left' | 'right' = 'left'; /** * Alignment * @default top */ - @Input() readonly alignment: 'top' | 'bottom' = 'top'; + @Input() alignment: 'top' | 'bottom' = 'top'; /** * Theme * @default light */ - @Input() readonly theme: 'light' | 'dark' = 'light'; + @Input() theme: 'light' | 'dark' = 'light'; /** * Custom template for the content section From 20e778fcf6c2a9fa90655bc85d2f238d91518171 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Wed, 6 Nov 2024 14:30:12 +0000 Subject: [PATCH 13/21] chore(release): 1.1.2 :tada: [skip ci] ## [1.1.2](https://github.com/italia/design-angular-kit/compare/v1.1.1...v1.1.2) (2024-11-06) ### Bug Fixes * readonly input problem in navscroll ([fceffc5](https://github.com/italia/design-angular-kit/commit/fceffc5e8467085de760300d303845f73ceb9493)) --- CHANGELOG.md | 7 +++++++ package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a70a58c..45446de8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## [1.1.2](https://github.com/italia/design-angular-kit/compare/v1.1.1...v1.1.2) (2024-11-06) + + +### Bug Fixes + +* readonly input problem in navscroll ([fceffc5](https://github.com/italia/design-angular-kit/commit/fceffc5e8467085de760300d303845f73ceb9493)) + ## [1.1.1](https://github.com/italia/design-angular-kit/compare/v1.1.0...v1.1.1) (2024-10-31) diff --git a/package-lock.json b/package-lock.json index 2d365ac7..f3e35c1d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "design-angular-kit-bundle", - "version": "1.1.1", + "version": "1.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "design-angular-kit-bundle", - "version": "1.1.1", + "version": "1.1.2", "license": "BSD-3-Clause", "dependencies": { "@angular-devkit/schematics": "^18.0.7", diff --git a/package.json b/package.json index 75e352ac..12bd3d88 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "design-angular-kit-bundle", "description": "Un toolkit Angular conforme alle linee guida di design per i servizi web della PA", "author": "Presidenza del Consiglio dei Ministri", - "version": "1.1.1", + "version": "1.1.2", "license": "BSD-3-Clause", "scripts": { "ng": "ng", From 1af2b2d7dc26723aadae3aeb306311ced0d842a9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:24:05 +0100 Subject: [PATCH 14/21] chore: update software version in publiccode Co-authored-by: astagi --- publiccode.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/publiccode.yml b/publiccode.yml index 52c8df10..37c2cbe4 100644 --- a/publiccode.yml +++ b/publiccode.yml @@ -62,8 +62,8 @@ maintenance: name: Design Angular Kit platforms: - web -releaseDate: '2024-10-31' +releaseDate: '2024-11-06' roadmap: 'https://github.com/italia/design-angular-kit/issues' softwareType: library -softwareVersion: v1.1.1 +softwareVersion: v1.1.2 url: 'https://github.com/italia/design-angular-kit' From d9db4c887e47cd3aee25c05c3ff92049f77ee32d Mon Sep 17 00:00:00 2001 From: smartm0use Date: Wed, 4 Dec 2024 12:27:37 +0100 Subject: [PATCH 15/21] chore(docs): remove useless attributes from cards and tooltips Co-authored-by: smartmouse --- src/app/card/card-big/card-big.component.html | 2 +- src/app/card/card-shadow/card-shadow.component.html | 2 +- .../tooltip-placements-example.component.html | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/card/card-big/card-big.component.html b/src/app/card/card-big/card-big.component.html index 7e41d8d1..5564ec76 100644 --- a/src/app/card/card-big/card-big.component.html +++ b/src/app/card/card-big/card-big.component.html @@ -23,7 +23,7 @@

Lorem ipsum dolor sit amet, consectetur adipiscing eli

- +
diff --git a/src/app/card/card-shadow/card-shadow.component.html b/src/app/card/card-shadow/card-shadow.component.html index d4a6cbdc..77d1b75f 100644 --- a/src/app/card/card-shadow/card-shadow.component.html +++ b/src/app/card/card-shadow/card-shadow.component.html @@ -5,7 +5,7 @@

Card con ombreggiatura

- +

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor…

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. diff --git a/src/app/tooltip/tooltip-placements-example/tooltip-placements-example.component.html b/src/app/tooltip/tooltip-placements-example/tooltip-placements-example.component.html index d4a082fd..d1876b58 100644 --- a/src/app/tooltip/tooltip-placements-example/tooltip-placements-example.component.html +++ b/src/app/tooltip/tooltip-placements-example/tooltip-placements-example.component.html @@ -39,7 +39,6 @@

Le quattro direzioni

type="button" class="btn btn-lg btn-danger me-4" itTooltip="Tooltip di esempio" - it-title="Titolo del Tooltip" [tooltipPlacement]="currentPlacement" triggers="click"> Clicca per il tooltip ricollocabile From c31c2b22dc9ae31ce911ffa0d4388919dd016f74 Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Wed, 4 Dec 2024 11:22:03 +0100 Subject: [PATCH 16/21] chore(deps): upgrade bsi --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f3e35c1d..f658b104 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "@angular/router": "^18.0.6", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", - "bootstrap-italia": "^2.10.0", + "bootstrap-italia": "^2.12.1", "core-js": "^3.37.1", "fp-ts": "^2.16.8", "io-ts": "^2.2.21", @@ -8390,9 +8390,9 @@ } }, "node_modules/bootstrap-italia": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/bootstrap-italia/-/bootstrap-italia-2.10.0.tgz", - "integrity": "sha512-ULYyv1/hC7/0t8jmfbzmCltC5c2ejnQKLhLQ2TirMW2VFOTklFifLB1mj64nr5TAe1kuIx1zRxcwoeNYC3fHdg==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/bootstrap-italia/-/bootstrap-italia-2.12.1.tgz", + "integrity": "sha512-D8ZRDs27YkCGC5ocEtHjLJVztTDjPoS2g/Vabl78QZ88ikNMApHPu1s9fQ8GCL1UxnRBfBKbNqvRN+IwRE4GMA==", "dependencies": { "@popperjs/core": "^2.11.2", "@splidejs/splide": "^3.6.12", diff --git a/package.json b/package.json index 12bd3d88..d3decdbb 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@angular/router": "^18.0.6", "@ngx-translate/core": "^15.0.0", "@ngx-translate/http-loader": "^8.0.0", - "bootstrap-italia": "^2.10.0", + "bootstrap-italia": "^2.12.1", "core-js": "^3.37.1", "fp-ts": "^2.16.8", "io-ts": "^2.2.21", From 815f18452e99dfb93c1136a9b0b2ac642d38995f Mon Sep 17 00:00:00 2001 From: Andrea Stagi Date: Wed, 4 Dec 2024 11:32:52 +0100 Subject: [PATCH 17/21] fix: add aria hidden to rating icons --- .../src/lib/components/form/rating/rating.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/design-angular-kit/src/lib/components/form/rating/rating.component.html b/projects/design-angular-kit/src/lib/components/form/rating/rating.component.html index 2614cd5e..acd9de06 100644 --- a/projects/design-angular-kit/src/lib/components/form/rating/rating.component.html +++ b/projects/design-angular-kit/src/lib/components/form/rating/rating.component.html @@ -15,7 +15,7 @@ [attr.aria-hidden]="control.disabled" [formControl]="control" />