mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
feat(portal): add scrolling to the desktop (#2410)
* feat(portal): add scrolling to the desktop * chore: comments * chore: fix
This commit is contained in:
@@ -4,7 +4,7 @@ import { IonicModule } from '@ionic/angular'
|
||||
import { NgDompurifyModule } from '@tinkoff/ng-dompurify'
|
||||
|
||||
import { MarkdownPipeModule } from '../../pipes/markdown/markdown.module'
|
||||
import { SafeLinksModule } from '../../directives/safe-links/safe-links.module'
|
||||
import { SafeLinksDirective } from '../../directives/safe-links.directive'
|
||||
import { TextSpinnerComponentModule } from '../text-spinner/text-spinner.component.module'
|
||||
import { MarkdownComponent } from './markdown.component'
|
||||
|
||||
@@ -15,7 +15,7 @@ import { MarkdownComponent } from './markdown.component'
|
||||
IonicModule,
|
||||
MarkdownPipeModule,
|
||||
TextSpinnerComponentModule,
|
||||
SafeLinksModule,
|
||||
SafeLinksDirective,
|
||||
NgDompurifyModule,
|
||||
],
|
||||
exports: [MarkdownComponent],
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
Directive,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
inject,
|
||||
NgZone,
|
||||
} from '@angular/core'
|
||||
import { DOCUMENT } from '@angular/common'
|
||||
import { ANIMATION_FRAME } from '@ng-web-apis/common'
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
|
||||
import { tuiZonefree } from '@taiga-ui/cdk'
|
||||
import { filter } from 'rxjs'
|
||||
|
||||
const SIZE = 100
|
||||
const SPEED = 15
|
||||
|
||||
@Directive({
|
||||
selector: '[dragScroller]',
|
||||
standalone: true,
|
||||
})
|
||||
export class DragScrollerDirective {
|
||||
private readonly element: HTMLElement = inject(ElementRef).nativeElement
|
||||
private dragging = false
|
||||
private x = 0
|
||||
private y = 0
|
||||
|
||||
private readonly sub = inject(ANIMATION_FRAME)
|
||||
.pipe(
|
||||
filter(() => this.dragging),
|
||||
tuiZonefree(inject(NgZone)),
|
||||
takeUntilDestroyed(),
|
||||
)
|
||||
.subscribe(() => {
|
||||
this.element.scrollTop += this.y * SPEED
|
||||
this.element.scrollLeft += this.x * SPEED
|
||||
})
|
||||
|
||||
@HostListener('document:pointerdown.silent', ['true'])
|
||||
@HostListener('document:pointerup.silent', ['false'])
|
||||
onPointer(dragging: boolean) {
|
||||
this.dragging = dragging
|
||||
this.x = 0
|
||||
this.y = 0
|
||||
}
|
||||
|
||||
@HostListener('pointermove.silent', ['$event'])
|
||||
onPointerMove(event: PointerEvent) {
|
||||
if (!this.dragging) {
|
||||
return
|
||||
}
|
||||
|
||||
const { clientX, clientY } = event
|
||||
const { top, left, right, bottom } = this.element.getBoundingClientRect()
|
||||
const x = Math.min(clientX - left, SIZE) - Math.min(right - clientX, SIZE)
|
||||
const y = Math.min(clientY - top, SIZE) - Math.min(bottom - clientY, SIZE)
|
||||
|
||||
this.x = x / SIZE
|
||||
this.y = y / SIZE
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import {
|
||||
Directive,
|
||||
OnInit,
|
||||
Optional,
|
||||
ElementRef,
|
||||
Inject,
|
||||
InjectionToken,
|
||||
@@ -8,10 +10,14 @@ import {
|
||||
} from '@angular/core'
|
||||
import { ResizeObserverService } from '@ng-web-apis/resize-observer'
|
||||
import { distinctUntilChanged, map, Observable } from 'rxjs'
|
||||
import { tuiZonefree } from '@taiga-ui/cdk'
|
||||
import { tuiZonefree, TuiDestroyService } from '@taiga-ui/cdk'
|
||||
import { IonCol } from '@ionic/angular'
|
||||
import { takeUntil } from 'rxjs'
|
||||
|
||||
export type Step = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
||||
|
||||
const SIZE: readonly Step[] = ['xl', 'lg', 'md', 'sm', 'xs']
|
||||
|
||||
/**
|
||||
* Not exported:
|
||||
* https://github.com/ionic-team/ionic-framework/blob/main/core/src/utils/media.ts
|
||||
@@ -41,6 +47,7 @@ export const BREAKPOINTS = new InjectionToken<readonly [number, Step][]>(
|
||||
selector: '[responsiveColViewport]',
|
||||
exportAs: 'viewport',
|
||||
providers: [ResizeObserverService],
|
||||
standalone: true,
|
||||
})
|
||||
export class ResponsiveColViewportDirective extends Observable<Step> {
|
||||
@Input()
|
||||
@@ -65,3 +72,50 @@ export class ResponsiveColViewportDirective extends Observable<Step> {
|
||||
tuiZonefree(this.zone),
|
||||
)
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-col[responsiveCol]',
|
||||
providers: [TuiDestroyService],
|
||||
standalone: true,
|
||||
})
|
||||
export class ResponsiveColDirective implements OnInit {
|
||||
readonly size: Record<Step, string | undefined> = {
|
||||
xs: '12',
|
||||
sm: '6',
|
||||
md: '4',
|
||||
lg: '3',
|
||||
xl: '2',
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Optional()
|
||||
viewport$: ResponsiveColViewportDirective | null,
|
||||
destroy$: TuiDestroyService,
|
||||
private readonly col: IonCol,
|
||||
) {
|
||||
viewport$?.pipe(takeUntil(destroy$)).subscribe(size => {
|
||||
const max = this.size[size] || this.findMax(size)
|
||||
|
||||
this.col.sizeLg = max
|
||||
this.col.sizeMd = max
|
||||
this.col.sizeSm = max
|
||||
this.col.sizeXl = max
|
||||
this.col.sizeXs = max
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.size.lg = this.col.sizeLg
|
||||
this.size.md = this.col.sizeMd
|
||||
this.size.sm = this.col.sizeSm
|
||||
this.size.xl = this.col.sizeXl
|
||||
this.size.xs = this.col.sizeXs
|
||||
}
|
||||
|
||||
private findMax(current: Step): string | undefined {
|
||||
const start = SIZE.indexOf(current) - 1
|
||||
const max = SIZE.find((size, i) => i > start && this.size[size]) || current
|
||||
|
||||
return this.size[max]
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import { Directive, OnInit, Optional } from '@angular/core'
|
||||
import { TuiDestroyService } from '@taiga-ui/cdk'
|
||||
import {
|
||||
ResponsiveColViewportDirective,
|
||||
Step,
|
||||
} from './responsive-col-viewport.directive'
|
||||
import { IonCol } from '@ionic/angular'
|
||||
import { takeUntil } from 'rxjs'
|
||||
|
||||
const SIZE: readonly Step[] = ['xl', 'lg', 'md', 'sm', 'xs']
|
||||
|
||||
@Directive({
|
||||
selector: 'ion-col[responsiveCol]',
|
||||
providers: [TuiDestroyService],
|
||||
})
|
||||
export class ResponsiveColDirective implements OnInit {
|
||||
readonly size: Record<Step, string | undefined> = {
|
||||
xs: '12',
|
||||
sm: '6',
|
||||
md: '4',
|
||||
lg: '3',
|
||||
xl: '2',
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Optional()
|
||||
viewport$: ResponsiveColViewportDirective | null,
|
||||
destroy$: TuiDestroyService,
|
||||
private readonly col: IonCol,
|
||||
) {
|
||||
viewport$?.pipe(takeUntil(destroy$)).subscribe(size => {
|
||||
const max = this.size[size] || this.findMax(size)
|
||||
|
||||
this.col.sizeLg = max
|
||||
this.col.sizeMd = max
|
||||
this.col.sizeSm = max
|
||||
this.col.sizeXl = max
|
||||
this.col.sizeXs = max
|
||||
})
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.size.lg = this.col.sizeLg
|
||||
this.size.md = this.col.sizeMd
|
||||
this.size.sm = this.col.sizeSm
|
||||
this.size.xl = this.col.sizeXl
|
||||
this.size.xs = this.col.sizeXs
|
||||
}
|
||||
|
||||
private findMax(current: Step): string | undefined {
|
||||
const start = SIZE.indexOf(current) - 1
|
||||
const max = SIZE.find((size, i) => i > start && this.size[size]) || current
|
||||
|
||||
return this.size[max]
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
|
||||
import { ResponsiveColViewportDirective } from './responsive-col-viewport.directive'
|
||||
import { ResponsiveColDirective } from './responsive-col.directive'
|
||||
|
||||
@NgModule({
|
||||
declarations: [ResponsiveColDirective, ResponsiveColViewportDirective],
|
||||
exports: [ResponsiveColDirective, ResponsiveColViewportDirective],
|
||||
})
|
||||
export class ResponsiveColModule {}
|
||||
@@ -4,6 +4,7 @@ import { DOCUMENT } from '@angular/common'
|
||||
// TODO: Refactor to use `MutationObserver` so it works with dynamic content
|
||||
@Directive({
|
||||
selector: '[safeLinks]',
|
||||
standalone: true,
|
||||
})
|
||||
export class SafeLinksDirective implements AfterViewInit {
|
||||
constructor(
|
||||
@@ -1,8 +0,0 @@
|
||||
import { NgModule } from '@angular/core'
|
||||
import { SafeLinksDirective } from './safe-links.directive'
|
||||
|
||||
@NgModule({
|
||||
declarations: [SafeLinksDirective],
|
||||
exports: [SafeLinksDirective],
|
||||
})
|
||||
export class SafeLinksModule {}
|
||||
@@ -18,11 +18,9 @@ export * from './components/text-spinner/text-spinner.component.module'
|
||||
export * from './components/ticker/ticker.component'
|
||||
export * from './components/ticker/ticker.module'
|
||||
|
||||
export * from './directives/responsive-col/responsive-col.directive'
|
||||
export * from './directives/responsive-col/responsive-col.module'
|
||||
export * from './directives/responsive-col/responsive-col-viewport.directive'
|
||||
export * from './directives/safe-links/safe-links.directive'
|
||||
export * from './directives/safe-links/safe-links.module'
|
||||
export * from './directives/drag-scroller.directive'
|
||||
export * from './directives/responsive-col.directive'
|
||||
export * from './directives/safe-links.directive'
|
||||
|
||||
export * from './mocks/get-setup-status'
|
||||
|
||||
|
||||
Reference in New Issue
Block a user