import { Component , forwardRef , OnDestroy , OnInit , OnChanges , SimpleChanges , Renderer2 , EventEmitter , Input , Output , HostBinding } from '@angular/core' ; import { ControlValueAccessor , NG_VALUE_ACCESSOR } from '@angular/forms' ; import { coerceBooleanProperty } from '@angular/cdk/coercion' ; import { BreakpointObserver , Breakpoints } from '@angular/cdk/layout' ; import { MatBottomSheet , MatBottomSheetRef } from "@angular/material/bottom-sheet" ; import { Subject } from 'rxjs' ; import { map , takeUntil } from 'rxjs/operators' ; import { OptionsInterface } from '../options.interface' ; import { NmCascadeBottomSheetComponent } from '../cascade-bottom-sheet/cascade-bottom-sheet.component' ; let NEXT_ID = 0 ; @ Component ( { selector : 'nm-cascade-dropdownlist' , templateUrl : './cascade-list.component.html' , styleUrls : [ './cascade-list.component.scss' ] , providers : [ { provide : NG_VALUE_ACCESSOR , useExisting : forwardRef ( ( ) => NmCascadeListComponent ) , multi : true } ] , } ) export class NmCascadeListComponent implements OnInit , OnDestroy , OnChanges , ControlValueAccessor { @ HostBinding ( 'attr.id' ) @ Input ( ) public id = `nm-cascade-list-${ NEXT_ID ++ }` ; @ Input ( ) disabled = false ; @ Input ( ) placeholder = '请选择' ; @ Input ( ) appearance : 'legacy' | 'standard' | 'fill' | 'outline' = 'legacy' ; @ Input ( ) get touchUi ( ) : boolean { return this . _touchUi ; } set touchUi ( value ) { this . _touchUi = coerceBooleanProperty ( value ) ; } private _touchUi = false ; bottomSheetRef : MatBottomSheetRef ; @ Input ( ) options : Array < OptionsInterface > ; @ Input ( ) clearable = false ; @ Input ( ) fullLevels = true ; @ Input ( ) changeOnSelect = false ; @ Input ( ) model : Array < string > ; @ Output ( ) modelChange : EventEmitter < any > = new EventEmitter < any > ( ) ; private ngUnsubscribe : Subject < any > ; steps : Array < any > = [ ] ; menuVisible = false ; inputHover = false ; currentLabels : Array < OptionsInterface > = [ ] ; globalListenFunc : Function ; inputValue : string ; constructor ( private renderer , private breakpointObserver , private bottomSheet ) { this . ngUnsubscribe = new Subject ( ) ; } private controlChange : Function = ( ) => { } ; private controlTouch : Function = ( ) => { } ; private setInputValue ( ) { if ( this . fullLevels ) { this . currentLabels . map ( ( value , i ) => { this . inputValue += `${ value . label }${ i < this . currentLabels . length - 1 ? ' / ' : '' }` ; } ) ; } else { this . inputValue = this . currentLabels [ this . currentLabels . length - 1 ] . label ; } } ngOnInit ( ) { } ngOnChanges ( changes ) { if ( changes . options ) { this . options = changes . options . currentValue ; this . init ( ) ; } } init ( ) { this . clearValue ( ) ; if ( this . model && this . model . length ) { const getLabel = ( options , val ) => { const item = options . filter ( ( item ) => item . value === val ) [ 0 ] ; return { children : item . children , val : item } ; } ; let options = this . options ; const val = this . model . map ( v => { const { children , val } = getLabel ( options , v ) ; options = children ; return val ; } ) ; this . currentLabels = val . filter ( v => ! ! v ) ; this . setInputValue ( ) ; this . initSteps ( 0 , this . options ) ; } this . breakpointObserver . observe ( Breakpoints . Handset ) . pipe ( takeUntil ( this . ngUnsubscribe ) , map ( match => match . matches ) ) . subscribe ( matches => { this . touchUi !== matches && ! matches && this . bottomSheetRef && this . bottomSheetRef . dismiss ( ) ; } ) ; } writeValue ( value ) { this . model = value ; } registerOnChange ( fn ) { this . controlChange = fn ; } registerOnTouched ( fn ) { this . controlTouch = fn ; } close ( ) { this . menuVisible = false ; this . globalListenFunc && this . globalListenFunc ( ) ; } clickHandle ( event ) { event . stopPropagation ( ) ; if ( this . disabled ) { return ; } const element = event . target as HTMLElement ; const isSelfTrigger = [ 'SPAN' , 'I' , 'INPUT' ] . find ( v => v === element . tagName ) ; if ( ! isSelfTrigger ) { return ; } if ( this . touchUi ) { this . menuVisible = false ; this . openBottomSheet ( ) ; } else { this . menuVisible = ! this . menuVisible ; if ( this . menuVisible ) { this . globalListenFunc = this . renderer . listen ( 'document' , 'click' , ( ) => { this . menuVisible = false ; } ) ; } else { this . globalListenFunc && this . globalListenFunc ( ) ; } } } changeLabels ( ) { this . inputValue = '' ; const nextValue = [ ] ; this . steps . forEach ( ( items ) => { const steps = items . filter ( ( item ) => item . active ) ; nextValue . push ( steps [ 0 ] ) ; } ) ; this . currentLabels = nextValue ; this . setInputValue ( ) ; const next = nextValue . map ( ( item ) => item . value ) ; this . model = next ; this . modelChange . emit ( next ) ; this . controlChange ( next ) ; } clearValue ( event ? ) { event && event . stopPropagation ( ) ; this . currentLabels = [ ] ; if ( ( this . model && ! this . model . length ) || ! this . model ) { const step1 = this . options . map ( ( option ) => ( { ... option , active : false } ) ) ; this . steps = [ step1 ] ; } this . menuVisible = false ; this . inputValue = '' ; event && this . modelChange . emit ( [ ] ) ; } initSteps ( index , options ) { const step = [ ] ; options . map ( ( item ) => { if ( this . model [ index ] === item . value ) { options = item . children ; step . push ( ( { ... item , active : true } ) ) ; } else { step . push ( item ) ; } } ) ; this . steps . push ( step ) ; index += 1 ; index < this . model . length && this . initSteps ( index , options ) ; } selectHandle ( event , step , index ) { event . stopPropagation ( ) ; if ( this . steps [ step ] [ index ] . disabled ) { return ; } this . steps [ step ] = this . steps [ step ] . map ( ( item , i ) => ( { ... item , active : i === index } ) ) ; this . steps . length = step + 1 ; const next = this . steps [ step ] [ index ] . children ; if ( next && Array . isArray ( next ) && next . length ) { this . changeOnSelect && this . changeLabels ( ) ; const nativeNext = next . map ( ( item ) => ( { ... item , active : false } ) ) ; return this . steps . push ( nativeNext ) ; } this . changeLabels ( ) ; this . menuVisible = false ; this . touchUi && this . bottomSheetRef . dismiss ( ) ; } showClearIcon ( ) { return ! ! ( this . clearable && this . inputHover && this . currentLabels . length ) ; } openBottomSheet ( ) { this . bottomSheetRef = this . bottomSheet . open ( NmCascadeBottomSheetComponent , { data : this } ) ; } ngOnDestroy ( ) { this . globalListenFunc && this . globalListenFunc ( ) ; this . ngUnsubscribe . next ( ) ; this . ngUnsubscribe . complete ( ) ; } }