-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
718 additions
and
3 deletions.
There are no files selected for viewing
54 changes: 54 additions & 0 deletions
54
angular/demo/src/app/samples/floatingUI/floatingUI.route.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import {AgnosUIAngularModule, createFloatingUI, floatingUI, toAngularSignal} from '@agnos-ui/angular'; | ||
import {ChangeDetectionStrategy, Component} from '@angular/core'; | ||
|
||
const scrollToMiddle = (element: HTMLElement) => { | ||
element.scrollTo({left: 326, top: 420}); | ||
}; | ||
|
||
@Component({ | ||
standalone: true, | ||
imports: [AgnosUIAngularModule], | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
template: `<div class="position-relative overflow-auto border border-primary-subtle demo-floatingui" [auUse]="scrollToMiddle"> | ||
<button [auUse]="floatingUI.directives.referenceDirective" type="button" class="btn btn-primary" (click)="displayPopover = !displayPopover"> | ||
Toggle popover | ||
</button> | ||
@if (displayPopover) { | ||
<div | ||
[auUse]="floatingUI.directives.floatingDirective" | ||
[attr.data-popper-placement]="floatingUIState().placement" | ||
class="popover bs-popover-auto position-absolute" | ||
[class.invisible]="floatingUIState().middlewareData?.hide?.referenceHidden" | ||
role="tooltip" | ||
> | ||
<div class="popover-arrow position-absolute" [auUse]="floatingUI.directives.arrowDirective"></div> | ||
<div class="popover-body text-center">This is a sample popover</div> | ||
</div> | ||
} | ||
</div>`, | ||
|
||
styles: "@import '@agnos-ui/common/samples/floatingui/floatingui.scss';", | ||
}) | ||
export default class FloatingUIComponent { | ||
displayPopover = true; | ||
|
||
floatingUI = createFloatingUI({ | ||
props: { | ||
arrowOptions: { | ||
padding: 6, | ||
}, | ||
computePositionOptions: { | ||
middleware: [ | ||
floatingUI.offset(10), | ||
floatingUI.autoPlacement(), | ||
floatingUI.shift({ | ||
padding: 5, | ||
}), | ||
floatingUI.hide(), | ||
], | ||
}, | ||
}, | ||
}); | ||
floatingUIState = toAngularSignal(this.floatingUI.state$); | ||
scrollToMiddle = scrollToMiddle; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
div.demo-floatingui { | ||
width: 500px; | ||
height: 200px; | ||
|
||
button { | ||
margin: 500px; | ||
width: 150px; | ||
} | ||
.popover { | ||
width: 250px; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
import {computed, derived} from '@amadeus-it-group/tansu'; | ||
import type {ArrowOptions, AutoUpdateOptions, ComputePositionConfig, ComputePositionReturn, Derivable} from '@floating-ui/dom'; | ||
import {arrow, autoUpdate, computePosition} from '@floating-ui/dom'; | ||
import {createStoreDirective, directiveSubscribe, mergeDirectives} from './directiveUtils'; | ||
import {stateStores, writablesForProps, type PropsConfig} from './stores'; | ||
|
||
import * as floatingUI from '@floating-ui/dom'; | ||
import {promiseStoreToValueStore} from './promiseStoreUtils'; | ||
export {floatingUI}; | ||
|
||
export interface FloatingUIProps { | ||
/** | ||
* Options to use when calling computePosition from Floating UI | ||
*/ | ||
computePositionOptions: ComputePositionConfig; | ||
|
||
/** | ||
* Options to use when calling autoUpdate from Floating UI | ||
*/ | ||
autoUpdateOptions: AutoUpdateOptions; | ||
|
||
/** | ||
* Options to use when calling the arrow middleware from Floating UI | ||
*/ | ||
arrowOptions: Omit<ArrowOptions, 'element'> | Derivable<Omit<ArrowOptions, 'element'>>; | ||
} | ||
|
||
const defaultConfig: FloatingUIProps = { | ||
computePositionOptions: {}, | ||
autoUpdateOptions: {}, | ||
arrowOptions: {}, | ||
}; | ||
|
||
export const createFloatingUI = (propsConfig?: PropsConfig<FloatingUIProps>) => { | ||
const [{autoUpdateOptions$, computePositionOptions$: computePositionInputOptions$, arrowOptions$: arrowInputOptions$}, patch] = writablesForProps( | ||
defaultConfig, | ||
propsConfig, | ||
); | ||
|
||
const {directive: floatingDirective, element$: floatingElement$} = createStoreDirective(); | ||
const {directive: referenceDirective, element$: referenceElement$} = createStoreDirective(); | ||
const {directive: arrowDirective, element$: arrowElement$} = createStoreDirective(); | ||
|
||
const arrowOptions$ = computed((): null | ArrowOptions | Derivable<ArrowOptions> => { | ||
const arrowElement = arrowElement$(); | ||
if (!arrowElement) { | ||
return null; | ||
} | ||
const arrowInputOptions = arrowInputOptions$(); | ||
return typeof arrowInputOptions === 'function' | ||
? (state) => ({...arrowInputOptions(state), element: arrowElement}) | ||
: {...arrowInputOptions, element: arrowElement}; | ||
}); | ||
|
||
const computePositionOptions$ = computed(() => { | ||
let options = computePositionInputOptions$(); | ||
const arrowOptions = arrowOptions$(); | ||
if (arrowOptions) { | ||
options = { | ||
...options, | ||
middleware: [...(options.middleware ?? []), arrow(arrowOptions)], | ||
}; | ||
} | ||
return options; | ||
}); | ||
|
||
const promisePosition$ = derived( | ||
[floatingElement$, referenceElement$, computePositionOptions$, autoUpdateOptions$], | ||
([floatingElement, referenceElement, computePositionOptions, autoUpdateOptions], set) => { | ||
if (floatingElement && referenceElement) { | ||
const clean = autoUpdate( | ||
referenceElement, | ||
floatingElement, | ||
() => { | ||
set(computePosition(referenceElement, floatingElement, computePositionOptions)); | ||
}, | ||
autoUpdateOptions, | ||
); | ||
return () => { | ||
set(null); | ||
clean(); | ||
}; | ||
} | ||
return undefined; | ||
}, | ||
null as null | Promise<ComputePositionReturn>, | ||
); | ||
const position$ = promiseStoreToValueStore(promisePosition$, null); | ||
|
||
const placement$ = computed(() => position$()?.placement); | ||
const middlewareData$ = computed(() => position$()?.middlewareData); | ||
const x$ = computed(() => position$()?.x); | ||
const y$ = computed(() => position$()?.y); | ||
const strategy$ = computed(() => position$()?.strategy); | ||
const arrowX$ = computed(() => middlewareData$()?.arrow?.x); | ||
const arrowY$ = computed(() => middlewareData$()?.arrow?.y); | ||
|
||
const floatingStyleApplyAction$ = computed(() => { | ||
const floatingElement = floatingElement$(); | ||
if (floatingElement) { | ||
floatingElement.style.left = `${x$() ?? 0}px`; | ||
floatingElement.style.top = `${y$() ?? 0}px`; | ||
} | ||
}); | ||
|
||
const arrowStyleApplyAction$ = computed(() => { | ||
const arrowElement = arrowElement$(); | ||
if (arrowElement) { | ||
const arrowX = arrowX$(); | ||
const arrowY = arrowY$(); | ||
arrowElement.style.left = arrowX != null ? `${arrowX}px` : ''; | ||
arrowElement.style.top = arrowY != null ? `${arrowY}px` : ''; | ||
} | ||
}); | ||
|
||
return { | ||
patch, | ||
...stateStores({ | ||
x$, | ||
y$, | ||
strategy$, | ||
placement$, | ||
middlewareData$, | ||
}), | ||
directives: { | ||
referenceDirective, | ||
floatingDirective: mergeDirectives(floatingDirective, directiveSubscribe(floatingStyleApplyAction$)), | ||
arrowDirective: mergeDirectives(arrowDirective, directiveSubscribe(arrowStyleApplyAction$)), | ||
}, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.