Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit052675d

Browse files
committed
feat(tooltip): reference input for positioning the tooltip on reference element, refactor with signals
1 parentbf44bd0 commit052675d

File tree

3 files changed

+82
-79
lines changed

3 files changed

+82
-79
lines changed

‎projects/coreui-angular/src/lib/popover/popover.directive.spec.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import{ChangeDetectorRef,ElementRef,Renderer2,ViewContainerRef}from'@angular/core';
2+
import{TestBed}from'@angular/core/testing';
23
import{IntersectionService,ListenersService}from'../services';
34
import{PopoverDirective}from'./popover.directive';
4-
import{TestBed}from'@angular/core/testing';
55

66
describe('PopoverDirective',()=>{
77
letdocument:Document;
@@ -11,11 +11,11 @@ describe('PopoverDirective', () => {
1111
letchangeDetectorRef:ChangeDetectorRef;
1212

1313
it('should create an instance',()=>{
14-
constlistenersService=newListenersService(renderer);
1514
TestBed.configureTestingModule({
16-
providers:[IntersectionService]
15+
providers:[IntersectionService,Renderer2,ListenersService],
1716
});
1817
constintersectionService=TestBed.inject(IntersectionService);
18+
constlistenersService=TestBed.inject(ListenersService);
1919
TestBed.runInInjectionContext(()=>{
2020
constdirective=newPopoverDirective(
2121
document,
@@ -24,7 +24,7 @@ describe('PopoverDirective', () => {
2424
viewContainerRef,
2525
listenersService,
2626
changeDetectorRef,
27-
intersectionService
27+
intersectionService,
2828
);
2929
expect(directive).toBeTruthy();
3030
});

‎projects/coreui-angular/src/lib/tooltip/tooltip.directive.spec.ts‎

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ describe('TooltipDirective', () => {
1111
letchangeDetectorRef:ChangeDetectorRef;
1212

1313
it('should create an instance',()=>{
14-
constlistenersService=newListenersService(renderer);
1514
TestBed.configureTestingModule({
16-
providers:[IntersectionService]
15+
providers:[IntersectionService,Renderer2,ListenersService],
1716
});
1817
constintersectionService=TestBed.inject(IntersectionService);
18+
constlistenersService=TestBed.inject(ListenersService);
1919
TestBed.runInInjectionContext(()=>{
2020
constdirective=newTooltipDirective(
2121
document,
@@ -24,10 +24,9 @@ describe('TooltipDirective', () => {
2424
viewContainerRef,
2525
listenersService,
2626
changeDetectorRef,
27-
intersectionService
27+
intersectionService,
2828
);
2929
expect(directive).toBeTruthy();
3030
});
31-
3231
});
3332
});

‎projects/coreui-angular/src/lib/tooltip/tooltip.directive.ts‎

Lines changed: 75 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
AfterViewInit,
33
ChangeDetectorRef,
44
ComponentRef,
5+
computed,
56
DestroyRef,
67
Directive,
78
effect,
@@ -10,75 +11,95 @@ import {
1011
inject,
1112
Inject,
1213
input,
13-
Input,
14-
OnChanges,
14+
model,
1515
OnDestroy,
1616
OnInit,
1717
Renderer2,
18-
SimpleChanges,
1918
TemplateRef,
20-
ViewContainerRef
19+
ViewContainerRef,
2120
}from'@angular/core';
22-
import{takeUntilDestroyed}from'@angular/core/rxjs-interop';
2321
import{DOCUMENT}from'@angular/common';
24-
import{debounceTime,filter,finalize}from'rxjs/operators';
2522
import{createPopper,Instance,Options}from'@popperjs/core';
2623

2724
import{Triggers}from'../coreui.types';
2825
import{TooltipComponent}from'./tooltip/tooltip.component';
29-
import{IListenersConfig,ListenersService}from'../services/listeners.service';
30-
import{IntersectionService}from'../services';
26+
import{IListenersConfig,IntersectionService,ListenersService}from'../services';
27+
import{debounceTime,filter,finalize}from'rxjs/operators';
28+
import{takeUntilDestroyed}from'@angular/core/rxjs-interop';
29+
import{ElementRefDirective}from'../shared';
3130

3231
@Directive({
3332
selector:'[cTooltip]',
3433
exportAs:'cTooltip',
3534
providers:[ListenersService,IntersectionService],
36-
standalone:true
35+
standalone:true,
3736
})
38-
exportclassTooltipDirectiveimplementsOnChanges,OnDestroy,OnInit,AfterViewInit{
39-
37+
exportclassTooltipDirectiveimplementsOnDestroy,OnInit,AfterViewInit{
4038
/**
4139
* Content of tooltip
4240
*@type {string | TemplateRef}
4341
*/
44-
readonlycontent=input<string|TemplateRef<any>>('',{alias:'cTooltip'});
42+
readonlycontent=input<string|TemplateRef<any>|undefined>(undefined,{alias:'cTooltip'});
43+
44+
contentEffect=effect(()=>{
45+
if(this.content()){
46+
this.destroyTooltipElement();
47+
}
48+
});
4549

4650
/**
4751
* Optional popper Options object, takes precedence over cPopoverPlacement prop
4852
*@type Partial<Options>
4953
*/
50-
@Input('cTooltipOptions')
51-
setpopperOptions(value:Partial<Options>){
52-
this._popperOptions={ ...this._popperOptions,placement:this.placement, ...value};
53-
};
54+
readonlypopperOptions=input<Partial<Options>>({},{alias:'cTooltipOptions'});
5455

55-
getpopperOptions():Partial<Options>{
56-
return{placement:this.placement, ...this._popperOptions};
57-
}
56+
popperOptionsEffect=effect(()=>{
57+
this._popperOptions={
58+
...this._popperOptions,
59+
placement:this.placement(),
60+
...this.popperOptions(),
61+
};
62+
});
63+
64+
popperOptionsComputed=computed(()=>{
65+
return{placement:this.placement(), ...this._popperOptions};
66+
});
5867

5968
/**
6069
* Describes the placement of your component after Popper.js has applied all the modifiers that may have flipped or altered the originally provided placement property.
70+
*@type: 'top' | 'bottom' | 'left' | 'right'
71+
*@default: 'top'
72+
*/
73+
readonlyplacement=input<'top'|'bottom'|'left'|'right'>('top',{
74+
alias:'cTooltipPlacement',
75+
});
76+
77+
/**
78+
* ElementRefDirective for positioning the tooltip on reference element
79+
*@type: ElementRefDirective
80+
*@default: undefined
6181
*/
62-
@Input('cTooltipPlacement')placement:'top'|'bottom'|'left'|'right'='top';
82+
readonlyreference=input<ElementRefDirective|undefined>(undefined,{
83+
alias:'cTooltipRef',
84+
});
85+
86+
readonlyreferenceRef=computed(()=>this.reference()?.elementRef??this.hostElement);
87+
6388
/**
6489
* Sets which event handlers you’d like provided to your toggle prop. You can specify one trigger or an array of them.
65-
*@type {'hover' |'focus' | 'click'}
90+
*@type: 'Triggers |Triggers[]
6691
*/
67-
@Input('cTooltipTrigger')trigger:Triggers|Triggers[]='hover';
92+
readonlytrigger=input<Triggers|Triggers[]>('hover',{alias:'cTooltipTrigger'});
6893

6994
/**
7095
* Toggle the visibility of tooltip component.
96+
*@type boolean
7197
*/
72-
@Input('cTooltipVisible')
73-
setvisible(value:boolean){
74-
this._visible=value;
75-
}
98+
readonlyvisible=model(false,{alias:'cTooltipVisible'});
7699

77-
getvisible(){
78-
returnthis._visible;
79-
}
80-
81-
private_visible=false;
100+
visibleEffect=effect(()=>{
101+
this.visible() ?this.addTooltipElement() :this.removeTooltipElement();
102+
});
82103

83104
@HostBinding('attr.aria-describedby')getariaDescribedBy():string|null{
84105
returnthis.tooltipId ?this.tooltipId :null;
@@ -94,10 +115,10 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
94115
{
95116
name:'offset',
96117
options:{
97-
offset:[0,5]
98-
}
99-
}
100-
]
118+
offset:[0,5],
119+
},
120+
},
121+
],
101122
};
102123

103124
readonly #destroyRef=inject(DestroyRef);
@@ -109,24 +130,13 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
109130
privateviewContainerRef:ViewContainerRef,
110131
privatelistenersService:ListenersService,
111132
privatechangeDetectorRef:ChangeDetectorRef,
112-
privateintersectionService:IntersectionService
133+
privateintersectionService:IntersectionService,
113134
){}
114135

115-
contentEffect=effect(()=>{
116-
this.destroyTooltipElement();
117-
this.content() ?this.addTooltipElement() :this.removeTooltipElement();
118-
});
119-
120136
ngAfterViewInit():void{
121137
this.intersectionServiceSubscribe();
122138
}
123139

124-
ngOnChanges(changes:SimpleChanges):void{
125-
if(changes['visible']){
126-
changes['visible'].currentValue ?this.addTooltipElement() :this.removeTooltipElement();
127-
}
128-
}
129-
130140
ngOnDestroy():void{
131141
this.clearListeners();
132142
this.destroyTooltipElement();
@@ -139,19 +149,16 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
139149
privatesetListeners():void{
140150
constconfig:IListenersConfig={
141151
hostElement:this.hostElement,
142-
trigger:this.trigger,
152+
trigger:this.trigger(),
143153
callbackToggle:()=>{
144-
this.visible=!this.visible;
145-
this.visible ?this.addTooltipElement() :this.removeTooltipElement();
154+
this.visible.set(!this.visible());
146155
},
147156
callbackOff:()=>{
148-
this.visible=false;
149-
this.removeTooltipElement();
157+
this.visible.set(false);
150158
},
151159
callbackOn:()=>{
152-
this.visible=true;
153-
this.addTooltipElement();
154-
}
160+
this.visible.set(true);
161+
},
155162
};
156163
this.listenersService.setListeners(config);
157164
}
@@ -161,19 +168,18 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
161168
}
162169

163170
privateintersectionServiceSubscribe():void{
164-
this.intersectionService.createIntersectionObserver(this.hostElement);
171+
this.intersectionService.createIntersectionObserver(this.referenceRef());
165172
this.intersectionService.intersecting$
166173
.pipe(
167-
filter(next=>next.hostElement===this.hostElement),
174+
filter((next)=>next.hostElement===this.referenceRef()),
168175
debounceTime(100),
169176
finalize(()=>{
170-
this.intersectionService.unobserve(this.hostElement);
177+
this.intersectionService.unobserve(this.referenceRef());
171178
}),
172-
takeUntilDestroyed(this.#destroyRef)
179+
takeUntilDestroyed(this.#destroyRef),
173180
)
174-
.subscribe(next=>{
175-
this.visible=next.isIntersecting ?this.visible :false;
176-
!this.visible&&this.removeTooltipElement();
181+
.subscribe((next)=>{
182+
this.visible.set(next.isIntersecting ?this.visible() :false);
177183
});
178184
}
179185

@@ -205,6 +211,7 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
205211

206212
privateaddTooltipElement():void{
207213
if(!this.content()){
214+
this.destroyTooltipElement();
208215
return;
209216
}
210217

@@ -214,7 +221,7 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
214221

215222
this.tooltipId=this.getUID('tooltip');
216223
this.tooltipRef.instance.id=this.tooltipId;
217-
this.tooltipRef.instance.content=this.content();
224+
this.tooltipRef.instance.content=this.content()??'';
218225

219226
this.tooltip=this.tooltipRef.location.nativeElement;
220227
this.renderer.addClass(this.tooltip,'d-none');
@@ -225,24 +232,21 @@ export class TooltipDirective implements OnChanges, OnDestroy, OnInit, AfterView
225232
this.viewContainerRef.insert(this.tooltipRef.hostView);
226233
this.renderer.appendChild(this.document.body,this.tooltip);
227234

228-
this.popperInstance=createPopper(
229-
this.hostElement.nativeElement,
230-
this.tooltip,
231-
{ ...this.popperOptions}
232-
);
233-
if(!this.visible){
235+
this.popperInstance=createPopper(this.referenceRef().nativeElement,this.tooltip,{
236+
...this.popperOptionsComputed(),
237+
});
238+
if(!this.visible()){
234239
this.removeTooltipElement();
235240
return;
236241
}
237242
this.renderer.removeClass(this.tooltip,'d-none');
238243
this.changeDetectorRef.markForCheck();
239244

240245
setTimeout(()=>{
241-
this.tooltipRef&&(this.tooltipRef.instance.visible=this.visible);
246+
this.tooltipRef&&(this.tooltipRef.instance.visible=this.visible());
242247
this.popperInstance?.forceUpdate();
243248
this.changeDetectorRef?.markForCheck();
244249
},100);
245-
246250
}
247251

248252
privateremoveTooltipElement():void{

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp