| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -43,13 +43,13 @@ export class SplitAreaComponent { | |||
| 43 | 43 | // As size is an input and we can change the size without the outside | |
| 44 | 44 | // listening to the change we need an intermediate writeable signal | |
| 45 | 45 | computed((): SplitAreaSize => { | |
| 46 | - if (!this.visible()) { | ||
| 46 | + const visibleIndex = this.split._visibleAreas().findIndex((area) => area === this) | ||
| 47 | + | ||
| 48 | + if (visibleIndex === -1) { | ||
| 47 | 49 | return 0 | |
| 48 | 50 | } | |
| 49 | 51 | ||
| 50 | - const size = this.size() | ||
| 51 | - // auto acts the same as * in all calculations | ||
| 52 | - return size === 'auto' ? '*' : size | ||
| 52 | + return this.split._alignedVisibleAreasSizes()[visibleIndex] | ||
| 53 | 53 | }), | |
| 54 | 54 | ) | |
| 55 | 55 | /** | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -13,9 +13,9 @@ import { | |||
| 13 | 13 | effect, | |
| 14 | 14 | inject, | |
| 15 | 15 | input, | |
| 16 | + isDevMode, | ||
| 16 | 17 | output, | |
| 17 | 18 | signal, | |
| 18 | - untracked, | ||
| 19 | 19 | } from '@angular/core' | |
| 20 | 20 | import { takeUntilDestroyed } from '@angular/core/rxjs-interop' | |
| 21 | 21 | import type { SplitAreaComponent } from '../split-area/split-area.component' | |
@@ -31,6 +31,7 @@ import { | |||
| 31 | 31 | numberAttributeWithFallback, | |
| 32 | 32 | sum, | |
| 33 | 33 | toRecord, | |
| 34 | + assertUnreachable, | ||
| 34 | 35 | } from '../utils' | |
| 35 | 36 | import { DOCUMENT, NgStyle, NgTemplateOutlet } from '@angular/common' | |
| 36 | 37 | import { SplitGutterInteractionEvent, SplitAreaSize } from '../models' | |
@@ -115,56 +116,11 @@ export class SplitComponent { | |||
| 115 | 116 | ||
| 116 | 117 | readonly dragProgress$ = this.dragProgressSubject.asObservable() | |
| 117 | 118 | ||
| 118 | - private readonly visibleAreas = computed(() => this._areas().filter((area) => area.visible())) | ||
| 119 | - private readonly gridTemplateColumnsStyle = computed(() => { | ||
| 120 | - const columns: string[] = [] | ||
| 121 | - const sumNonWildcardSizes = sum(this.visibleAreas(), (area) => { | ||
| 122 | - const size = area._internalSize() | ||
| 123 | - return size === '*' ? 0 : size | ||
| 124 | - }) | ||
| 125 | - const visibleAreasCount = this.visibleAreas().length | ||
| 126 | - | ||
| 127 | - let visitedVisibleAreas = 0 | ||
| 128 | - | ||
| 129 | - this._areas().forEach((area, index, areas) => { | ||
| 130 | - const unit = this.unit() | ||
| 131 | - const areaSize = area._internalSize() | ||
| 132 | - | ||
| 133 | - // Add area size column | ||
| 134 | - if (!area.visible()) { | ||
| 135 | - columns.push(unit === 'percent' || areaSize === '*' ? '0fr' : '0px') | ||
| 136 | - } else { | ||
| 137 | - if (unit === 'pixel') { | ||
| 138 | - const columnValue = areaSize === '*' ? '1fr' : `${areaSize}px` | ||
| 139 | - columns.push(columnValue) | ||
| 140 | - } else { | ||
| 141 | - const percentSize = areaSize === '*' ? 100 - sumNonWildcardSizes : areaSize | ||
| 142 | - const columnValue = `${percentSize}fr` | ||
| 143 | - columns.push(columnValue) | ||
| 144 | - } | ||
| 145 | - | ||
| 146 | - visitedVisibleAreas++ | ||
| 147 | - } | ||
| 148 | - | ||
| 149 | - const isLastArea = index === areas.length - 1 | ||
| 150 | - | ||
| 151 | - if (isLastArea) { | ||
| 152 | - return | ||
| 153 | - } | ||
| 154 | - | ||
| 155 | - const remainingVisibleAreas = visibleAreasCount - visitedVisibleAreas | ||
| 156 | - | ||
| 157 | - // Only add gutter with size if this area is visible and there are more visible areas after this one | ||
| 158 | - // to avoid ghost gutters | ||
| 159 | - if (area.visible() && remainingVisibleAreas > 0) { | ||
| 160 | - columns.push(`${this.gutterSize()}px`) | ||
| 161 | - } else { | ||
| 162 | - columns.push('0px') | ||
| 163 | - } | ||
| 164 | - }) | ||
| 165 | - | ||
| 166 | - return this.direction() === 'horizontal' ? `1fr / ${columns.join(' ')}` : `${columns.join(' ')} / 1fr` | ||
| 167 | - }) | ||
| 119 | + /** | ||
| 120 | + * @internal | ||
| 121 | + */ | ||
| 122 | + readonly _visibleAreas = computed(() => this._areas().filter((area) => area.visible())) | ||
| 123 | + private readonly gridTemplateColumnsStyle = computed(() => this.createGridTemplateColumnsStyle()) | ||
| 168 | 124 | private readonly hostClasses = computed(() => | |
| 169 | 125 | createClassesString({ | |
| 170 | 126 | [`as-${this.direction()}`]: true, | |
@@ -179,6 +135,11 @@ export class SplitComponent { | |||
| 179 | 135 | * @internal | |
| 180 | 136 | */ | |
| 181 | 137 | readonly _isDragging = computed(() => this.draggedGutterIndex() !== undefined) | |
| 138 | + /** | ||
| 139 | + * @internal | ||
| 140 | + * Should only be used by {@link SplitAreaComponent._internalSize} | ||
| 141 | + */ | ||
| 142 | + readonly _alignedVisibleAreasSizes = computed(() => this.createAlignedVisibleAreasSize()) | ||
| 182 | 143 | ||
| 183 | 144 | @HostBinding('class') protected get hostClassesBinding() { | |
| 184 | 145 | return this.hostClasses() | |
@@ -189,45 +150,17 @@ export class SplitComponent { | |||
| 189 | 150 | } | |
| 190 | 151 | ||
| 191 | 152 | constructor() { | |
| 192 | - effect( | ||
| 193 | - () => { | ||
| 194 | - const visibleAreas = this.visibleAreas() | ||
| 195 | - const unit = this.unit() | ||
| 196 | - const isInAutoMode = visibleAreas.every((area) => area.size() === 'auto') | ||
| 197 | - | ||
| 198 | - untracked(() => { | ||
| 199 | - // Special mode when no size input was declared which is a valid mode | ||
| 200 | - if (unit === 'percent' && visibleAreas.length > 1 && isInAutoMode) { | ||
| 201 | - visibleAreas.forEach((area) => area._internalSize.set(100 / visibleAreas.length)) | ||
| 202 | - return | ||
| 203 | - } | ||
| 204 | - | ||
| 205 | - visibleAreas.forEach((area) => area._internalSize.reset()) | ||
| 206 | - | ||
| 207 | - const isValid = areAreasValid(visibleAreas, unit) | ||
| 208 | - | ||
| 209 | - if (isValid) { | ||
| 210 | - return | ||
| 211 | - } | ||
| 153 | + if (isDevMode()) { | ||
| 154 | + // Logs warnings to console when the provided areas sizes are invalid | ||
| 155 | + effect(() => { | ||
| 156 | + // Special mode when no size input was declared which is a valid mode | ||
| 157 | + if (this.unit() === 'percent' && this._visibleAreas().every((area) => area.size() === 'auto')) { | ||
| 158 | + return | ||
| 159 | + } | ||
| 212 | 160 | ||
| 213 | - if (unit === 'percent') { | ||
| 214 | - // Distribute sizes equally | ||
| 215 | - const defaultSize = 100 / visibleAreas.length | ||
| 216 | - visibleAreas.forEach((area) => area._internalSize.set(defaultSize)) | ||
| 217 | - } else if (unit === 'pixel') { | ||
| 218 | - const wildcardAreas = visibleAreas.filter((area) => area._internalSize() === '*') | ||
| 219 | - | ||
| 220 | - // Make sure only one wildcard area | ||
| 221 | - if (wildcardAreas.length === 0) { | ||
| 222 | - visibleAreas[0]._internalSize.set('*') | ||
| 223 | - } else if (wildcardAreas.length > 1) { | ||
| 224 | - wildcardAreas.filter((_, i) => i !== 0).forEach((area) => area._internalSize.set(100)) | ||
| 225 | - } | ||
| 226 | - } | ||
| 227 | - }) | ||
| 228 | - }, | ||
| 229 | - { allowSignalWrites: true }, | ||
| 230 | - ) | ||
| 161 | + areAreasValid(this._visibleAreas(), this.unit(), true) | ||
| 162 | + }) | ||
| 163 | + } | ||
| 231 | 164 | ||
| 232 | 165 | // Responsible for updating grid template style. Must be this way and not based on HostBinding | |
| 233 | 166 | // as change detection for host binding is bound to the parent component and this style | |
@@ -450,7 +383,7 @@ export class SplitComponent { | |||
| 450 | 383 | } | |
| 451 | 384 | ||
| 452 | 385 | private createAreaSizes() { | |
| 453 | - return this.visibleAreas().map((area) => area._internalSize()) | ||
| 386 | + return this._visibleAreas().map((area) => area._internalSize()) | ||
| 454 | 387 | } | |
| 455 | 388 | ||
| 456 | 389 | private createDragStartContext( | |
@@ -460,7 +393,7 @@ export class SplitComponent { | |||
| 460 | 393 | ): DragStartContext { | |
| 461 | 394 | const splitBoundingRect = this.elementRef.nativeElement.getBoundingClientRect() | |
| 462 | 395 | const splitSize = this.direction() === 'horizontal' ? splitBoundingRect.width : splitBoundingRect.height | |
| 463 | - const totalAreasPixelSize = splitSize - (this.visibleAreas().length - 1) * this.gutterSize() | ||
| 396 | + const totalAreasPixelSize = splitSize - (this._visibleAreas().length - 1) * this.gutterSize() | ||
| 464 | 397 | // Use the internal size and split size to calculate the pixel size from wildcard and percent areas | |
| 465 | 398 | const areaPixelSizesWithWildcard = this._areas().map((area) => { | |
| 466 | 399 | if (this.unit() === 'pixel') { | |
@@ -598,4 +531,92 @@ export class SplitComponent { | |||
| 598 | 531 | ||
| 599 | 532 | this.dragProgressSubject.next(this.createDragInteractionEvent(this.draggedGutterIndex())) | |
| 600 | 533 | } | |
| 534 | + | ||
| 535 | + private createGridTemplateColumnsStyle(): string { | ||
| 536 | + const columns: string[] = [] | ||
| 537 | + const sumNonWildcardSizes = sum(this._visibleAreas(), (area) => { | ||
| 538 | + const size = area._internalSize() | ||
| 539 | + return size === '*' ? 0 : size | ||
| 540 | + }) | ||
| 541 | + const visibleAreasCount = this._visibleAreas().length | ||
| 542 | + | ||
| 543 | + let visitedVisibleAreas = 0 | ||
| 544 | + | ||
| 545 | + this._areas().forEach((area, index, areas) => { | ||
| 546 | + const unit = this.unit() | ||
| 547 | + const areaSize = area._internalSize() | ||
| 548 | + | ||
| 549 | + // Add area size column | ||
| 550 | + if (!area.visible()) { | ||
| 551 | + columns.push(unit === 'percent' || areaSize === '*' ? '0fr' : '0px') | ||
| 552 | + } else { | ||
| 553 | + if (unit === 'pixel') { | ||
| 554 | + const columnValue = areaSize === '*' ? '1fr' : `${areaSize}px` | ||
| 555 | + columns.push(columnValue) | ||
| 556 | + } else { | ||
| 557 | + const percentSize = areaSize === '*' ? 100 - sumNonWildcardSizes : areaSize | ||
| 558 | + const columnValue = `${percentSize}fr` | ||
| 559 | + columns.push(columnValue) | ||
| 560 | + } | ||
| 561 | + | ||
| 562 | + visitedVisibleAreas++ | ||
| 563 | + } | ||
| 564 | + | ||
| 565 | + const isLastArea = index === areas.length - 1 | ||
| 566 | + | ||
| 567 | + if (isLastArea) { | ||
| 568 | + return | ||
| 569 | + } | ||
| 570 | + | ||
| 571 | + const remainingVisibleAreas = visibleAreasCount - visitedVisibleAreas | ||
| 572 | + | ||
| 573 | + // Only add gutter with size if this area is visible and there are more visible areas after this one | ||
| 574 | + // to avoid ghost gutters | ||
| 575 | + if (area.visible() && remainingVisibleAreas > 0) { | ||
| 576 | + columns.push(`${this.gutterSize()}px`) | ||
| 577 | + } else { | ||
| 578 | + columns.push('0px') | ||
| 579 | + } | ||
| 580 | + }) | ||
| 581 | + | ||
| 582 | + return this.direction() === 'horizontal' ? `1fr / ${columns.join(' ')}` : `${columns.join(' ')} / 1fr` | ||
| 583 | + } | ||
| 584 | + | ||
| 585 | + private createAlignedVisibleAreasSize(): SplitAreaSize[] { | ||
| 586 | + const visibleAreasSizes = this._visibleAreas().map((area): SplitAreaSize => { | ||
| 587 | + const size = area.size() | ||
| 588 | + return size === 'auto' ? '*' : size | ||
| 589 | + }) | ||
| 590 | + const isValid = areAreasValid(this._visibleAreas(), this.unit(), false) | ||
| 591 | + | ||
| 592 | + if (isValid) { | ||
| 593 | + return visibleAreasSizes | ||
| 594 | + } | ||
| 595 | + | ||
| 596 | + const unit = this.unit() | ||
| 597 | + | ||
| 598 | + if (unit === 'percent') { | ||
| 599 | + // Distribute sizes equally | ||
| 600 | + const defaultPercentSize = 100 / visibleAreasSizes.length | ||
| 601 | + return visibleAreasSizes.map(() => defaultPercentSize) | ||
| 602 | + } | ||
| 603 | + | ||
| 604 | + if (unit === 'pixel') { | ||
| 605 | + // Make sure only one wildcard area | ||
| 606 | + const wildcardAreas = visibleAreasSizes.filter((areaSize) => areaSize === '*') | ||
| 607 | + | ||
| 608 | + if (wildcardAreas.length === 0) { | ||
| 609 | + return ['*', ...visibleAreasSizes.slice(1)] | ||
| 610 | + } else { | ||
| 611 | + const firstWildcardIndex = visibleAreasSizes.findIndex((areaSize) => areaSize === '*') | ||
| 612 | + const defaultPxSize = 100 | ||
| 613 | + | ||
| 614 | + return visibleAreasSizes.map((areaSize, index) => | ||
| 615 | + index === firstWildcardIndex || areaSize !== '*' ? areaSize : defaultPxSize, | ||
| 616 | + ) | ||
| 617 | + } | ||
| 618 | + } | ||
| 619 | + | ||
| 620 | + return assertUnreachable(unit, 'SplitUnit') | ||
| 621 | + } | ||
| 601 | 622 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -131,3 +131,7 @@ export function leaveNgZone<T>() { | |||
| 131 | 131 | } | |
| 132 | 132 | ||
| 133 | 133 | export const numberAttributeWithFallback = (fallback: number) => (value: unknown) => numberAttribute(value, fallback) | |
| 134 | + | ||
| 135 | + export const assertUnreachable = (value: never, name: string) => { | ||
| 136 | + throw new Error(`as-split: unknown value "${value}" for "${name}"`) | ||
| 137 | + } | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,17 +1,21 @@ | |||
| 1 | - import { isDevMode } from '@angular/core' | ||
| 2 | - import { SplitUnit } from './models' | ||
| 1 | + import { SplitAreaSize, SplitUnit } from './models' | ||
| 3 | 2 | import { SplitAreaComponent } from './split-area/split-area.component' | |
| 4 | 3 | import { sum } from './utils' | |
| 5 | 4 | ||
| 6 | - export function areAreasValid(areas: readonly SplitAreaComponent[], unit: SplitUnit): boolean { | ||
| 5 | + export function areAreasValid(areas: readonly SplitAreaComponent[], unit: SplitUnit, logWarnings: boolean): boolean { | ||
| 7 | 6 | if (areas.length === 0) { | |
| 8 | 7 | return true | |
| 9 | 8 | } | |
| 10 | 9 | ||
| 11 | - const wildcardAreas = areas.filter((area) => area._internalSize() === '*') | ||
| 10 | + const areaSizes = areas.map((area): SplitAreaSize => { | ||
| 11 | + const size = area.size() | ||
| 12 | + return size === 'auto' ? '*' : size | ||
| 13 | + }) | ||
| 14 | + | ||
| 15 | + const wildcardAreas = areaSizes.filter((areaSize) => areaSize === '*') | ||
| 12 | 16 | ||
| 13 | 17 | if (wildcardAreas.length > 1) { | |
| 14 | - if (isDevMode()) { | ||
| 18 | + if (logWarnings) { | ||
| 15 | 19 | console.warn('as-split: Maximum one * area is allowed') | |
| 16 | 20 | } | |
| 17 | 21 | ||
@@ -23,33 +27,30 @@ export function areAreasValid(areas: readonly SplitAreaComponent[], unit: SplitU | |||
| 23 | 27 | return true | |
| 24 | 28 | } | |
| 25 | 29 | ||
| 26 | - if (isDevMode()) { | ||
| 30 | + if (logWarnings) { | ||
| 27 | 31 | console.warn('as-split: Pixel mode must have exactly one * area') | |
| 28 | 32 | } | |
| 29 | 33 | ||
| 30 | 34 | return false | |
| 31 | 35 | } | |
| 32 | 36 | ||
| 33 | - const sumPercent = sum(areas, (area) => { | ||
| 34 | - const size = area._internalSize() | ||
| 35 | - return size === '*' ? 0 : size | ||
| 36 | - }) | ||
| 37 | + const sumPercent = sum(areaSizes, (areaSize) => (areaSize === '*' ? 0 : areaSize)) | ||
| 37 | 38 | ||
| 38 | 39 | // As percent calculation isn't perfect we allow for a small margin of error | |
| 39 | 40 | if (wildcardAreas.length === 1) { | |
| 40 | 41 | if (sumPercent <= 100.1) { | |
| 41 | 42 | return true | |
| 42 | 43 | } | |
| 43 | 44 | ||
| 44 | - if (isDevMode()) { | ||
| 45 | + if (logWarnings) { | ||
| 45 | 46 | console.warn(`as-split: Percent areas must total 100%`) | |
| 46 | 47 | } | |
| 47 | 48 | ||
| 48 | 49 | return false | |
| 49 | 50 | } | |
| 50 | 51 | ||
| 51 | 52 | if (sumPercent < 99.9 || sumPercent > 100.1) { | |
| 52 | - if (isDevMode()) { | ||
| 53 | + if (logWarnings) { | ||
| 53 | 54 | console.warn('as-split: Percent areas must total 100%') | |
| 54 | 55 | } | |
| 55 | 56 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments