@@ -13,9 +13,9 @@ import {
1313 effect ,
1414 inject ,
1515 input ,
16+ isDevMode ,
1617 output ,
1718 signal ,
18- untracked ,
1919} from '@angular/core'
2020import { takeUntilDestroyed } from '@angular/core/rxjs-interop'
2121import type { SplitAreaComponent } from '../split-area/split-area.component'
@@ -31,6 +31,7 @@ import {
3131 numberAttributeWithFallback ,
3232 sum ,
3333 toRecord ,
34+ assertUnreachable ,
3435} from '../utils'
3536import { DOCUMENT , NgStyle , NgTemplateOutlet } from '@angular/common'
3637import { SplitGutterInteractionEvent , SplitAreaSize } from '../models'
@@ -115,56 +116,11 @@ export class SplitComponent {
115116
116117 readonly dragProgress$ = this . dragProgressSubject . asObservable ( )
117118
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 ( ) )
168124 private readonly hostClasses = computed ( ( ) =>
169125 createClassesString ( {
170126 [ `as-${ this . direction ( ) } ` ] : true ,
@@ -179,6 +135,11 @@ export class SplitComponent {
179135 * @internal
180136 */
181137 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 ( ) )
182143
183144 @HostBinding ( 'class' ) protected get hostClassesBinding ( ) {
184145 return this . hostClasses ( )
@@ -189,45 +150,17 @@ export class SplitComponent {
189150 }
190151
191152 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+ }
212160
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+ }
231164
232165 // Responsible for updating grid template style. Must be this way and not based on HostBinding
233166 // as change detection for host binding is bound to the parent component and this style
@@ -450,7 +383,7 @@ export class SplitComponent {
450383 }
451384
452385 private createAreaSizes ( ) {
453- return this . visibleAreas ( ) . map ( ( area ) => area . _internalSize ( ) )
386+ return this . _visibleAreas ( ) . map ( ( area ) => area . _internalSize ( ) )
454387 }
455388
456389 private createDragStartContext (
@@ -460,7 +393,7 @@ export class SplitComponent {
460393 ) : DragStartContext {
461394 const splitBoundingRect = this . elementRef . nativeElement . getBoundingClientRect ( )
462395 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 ( )
464397 // Use the internal size and split size to calculate the pixel size from wildcard and percent areas
465398 const areaPixelSizesWithWildcard = this . _areas ( ) . map ( ( area ) => {
466399 if ( this . unit ( ) === 'pixel' ) {
@@ -598,4 +531,92 @@ export class SplitComponent {
598531
599532 this . dragProgressSubject . next ( this . createDragInteractionEvent ( this . draggedGutterIndex ( ) ) )
600533 }
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+ }
601622}
0 commit comments