Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions addon/components/pix-floating.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { hash } from '@ember/helper';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { modifier as eModifier } from 'ember-modifier';

import { anchorTo } from '../modifiers/floating-ui';

const ref = eModifier((element, positional) => {
const fn = positional[0];
fn(element);
});

/**
* A component that provides no DOM and yields two modifiers for creating
* creating floating uis, such as menus, popovers, tooltips, etc.
* This component currently uses [Floating UI](https://floating-ui.com/)
*
* Example usage:
* ```gjs
* import { PixFloating } from '@1024pix/pix-ui';
*
* <template>
* <PixFloating as |reference floating|>
* <button {{reference}}> ... </button>
* <menu {{floating}}> ... </menu>
* </PixFloating>
* </template>
* ```
*/
export default class PixFloating extends Component {
@tracked reference = undefined;
@tracked data = undefined;

setData = (data) => (this.data = data);

setReference = (element) => (this.reference = element);

<template>
{{#let
(modifier
anchorTo
flipOptions=@flipOptions
hideOptions=@hideOptions
middleware=@middleware
offsetOptions=@offsetOptions
placement=@placement
shiftOptions=@shiftOptions
strategy=@strategy
setData=this.setData
)
as |prewiredAnchorTo|
}}
{{#let (if this.reference (modifier prewiredAnchorTo this.reference)) as |floating|}}
{{yield
(modifier ref this.setReference)
floating
(hash setReference=this.setReference data=this.data)
}}
{{/let}}
{{/let}}
</template>
}
13 changes: 9 additions & 4 deletions addon/components/pix-multi-select.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import onClickOutside from 'ember-click-outside/modifiers/on-click-outside';
import { PopperJS } from 'ember-popperjs';
import { gt, or } from 'ember-truth-helpers';

import onArrowDownUpAction from '../modifiers/on-arrow-down-up-action';
import onEnterAction from '../modifiers/on-enter-action';
import onEscapeAction from '../modifiers/on-escape-action';
import PixCheckbox from './pix-checkbox';
import PixFloating from './pix-floating';
import PixIcon from './pix-icon';
import PixLabel from './pix-label';

Expand Down Expand Up @@ -47,7 +47,7 @@ import PixLabel from './pix-label';
* @property {boolean} [screenReaderOnly] - Masque le libellé visuellement, tout en le laissant lisible par les lecteurs d'écran.
* @property {boolean} [inlineLabel] - Place le libellé sur la même ligne que le champ.
* @property {string} [className] - Classes CSS ajoutées au bouton d'ouverture.
* @property {string} [placement] - Position de la liste par rapport au champ, au sens de Popper. Par défaut : `bottom-start`.
* @property {string} [placement] - Position de la liste par rapport au champ, au sens de Floating UI. Par défaut : `bottom-start`.
* @property {boolean} [isComputeWidthDisabled] - Désactive l'alignement automatique de la largeur du champ sur celle de la liste.
*/

Expand Down Expand Up @@ -234,7 +234,12 @@ export default class PixMultiSelect extends Component {
</PixLabel>

<div>
<PopperJS @placement={{or @placement "bottom-start"}} as |reference popover|>
<PixFloating
@placement={{or @placement "bottom-start"}}
@strategy="absolute"
@offsetOptions={{4}}
as |reference popover|
>
<button
{{reference}}
id={{this.multiSelectId}}
Expand Down Expand Up @@ -306,7 +311,7 @@ export default class PixMultiSelect extends Component {
>{{this.emptySearchMessage}}</li>
{{/if}}
</ul>
</PopperJS>
</PixFloating>
</div>
</div>
</template>
Expand Down
13 changes: 9 additions & 4 deletions addon/components/pix-select.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import { service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import onClickOutside from 'ember-click-outside/modifiers/on-click-outside';
import { PopperJS } from 'ember-popperjs';
import { or } from 'ember-truth-helpers';

import onArrowDownUpAction from '../modifiers/on-arrow-down-up-action';
import onEscapeAction from '../modifiers/on-escape-action';
import PixFloating from './pix-floating';
import PixIcon from './pix-icon';
import PixLabel from './pix-label';
import PixSelectList from './pix-select-list';
Expand Down Expand Up @@ -53,7 +53,7 @@ import PixSelectList from './pix-select-list';
* @property {boolean} [isFullWidth] - Étend le champ à toute la largeur disponible.
* @property {string} [errorMessage] - Message d'erreur affiché sous le champ. Sa présence applique le style d'erreur.
* @property {string} [className] - Classes CSS ajoutées au bouton d'ouverture.
* @property {string} [placement] - Position de la liste par rapport au champ, au sens de Popper. Par défaut : `bottom-start`.
* @property {string} [placement] - Position de la liste par rapport au champ, au sens de Floating UI. Par défaut : `bottom-start`.
* @property {boolean} [isComputeWidthDisabled] - Désactive l'alignement automatique de la largeur du champ sur celle de la liste.
*/

Expand Down Expand Up @@ -233,7 +233,12 @@ export default class PixSelect extends Component {
{{/if}}

<div class="pix-select__button-container">
<PopperJS @placement={{or @placement "bottom-start"}} as |reference popover|>
<PixFloating
@placement={{or @placement "bottom-start"}}
@strategy="absolute"
@offsetOptions={{4}}
as |reference popover|
>
<button
{{reference}}
type="button"
Expand Down Expand Up @@ -298,7 +303,7 @@ export default class PixSelect extends Component {
@emptySearchMessage={{@texts.emptySearchMessage}}
/>
</div>
</PopperJS>
</PixFloating>
{{#if @errorMessage}}
<p class="pix-select__error-message">{{@errorMessage}}</p>
{{/if}}
Expand Down
11 changes: 8 additions & 3 deletions addon/components/pix-structure-switcher.gjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { guidFor } from '@ember/object/internals';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import onClickOutside from 'ember-click-outside/modifiers/on-click-outside';
import { PopperJS } from 'ember-popperjs';

import onArrowDownUpAction from '../modifiers/on-arrow-down-up-action';
import onEscapeAction from '../modifiers/on-escape-action';
import PixButton from './pix-button';
import PixFloating from './pix-floating';
import PixSelectList from './pix-select-list';

export default class PixStructureSwitcher extends Component {
Expand Down Expand Up @@ -71,7 +71,12 @@ export default class PixStructureSwitcher extends Component {
{{on "keydown" this.lockTab}}
...attributes
>
<PopperJS @placement="right-end" as |reference popover|>
<PixFloating
@placement="right-end"
@strategy="absolute"
@offsetOptions={{20}}
as |reference popover|
>
<PixButton
{{reference}}
@size="small"
Expand Down Expand Up @@ -101,7 +106,7 @@ export default class PixStructureSwitcher extends Component {
/>
</div>
{{/if}}
</PopperJS>
</PixFloating>
</div>
</template>
}
94 changes: 94 additions & 0 deletions addon/modifiers/floating-ui.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { assert } from '@ember/debug';
import { autoUpdate, computePosition, flip, hide, offset, shift } from '@floating-ui/dom';
import { modifier as eModifier } from 'ember-modifier';

export const anchorTo = eModifier(
(
floatingElement,
[_referenceElement],
{
strategy = 'fixed',
offsetOptions = 0,
placement = 'bottom',
flipOptions,
shiftOptions,
middleware = [],
setData,
},
) => {
const referenceElement =
typeof _referenceElement === 'string'
? document.querySelector(_referenceElement)
: _referenceElement;

assert(
'no reference element defined',
referenceElement instanceof HTMLElement || referenceElement instanceof SVGElement,
);

assert(
'no floating element defined',
floatingElement instanceof HTMLElement || _referenceElement instanceof SVGElement,
);

assert(
'reference and floating elements cannot be the same element',
floatingElement !== _referenceElement,
);

assert('@middleware must be an array of one or more objects', Array.isArray(middleware));

Object.assign(floatingElement.style, {
position: strategy,
top: '0',
left: '0',
});

const update = async () => {
const { middlewareData, x, y } = await computePosition(referenceElement, floatingElement, {
middleware: [
offset(offsetOptions),
flip(flipOptions),
shift(shiftOptions),
...middleware,
hide({ strategy: 'referenceHidden' }),
hide({ strategy: 'escaped' }),
exposeMetadata(),
],
placement,
strategy,
});

const referenceHidden = middlewareData.hide?.referenceHidden;

Object.assign(floatingElement.style, {
top: `${y}px`,
left: `${x}px`,
margin: 0,
visibility: referenceHidden ? 'hidden' : 'visible',
});

void setData?.(middlewareData['metadata']);
};

void update();

const cleanup = autoUpdate(referenceElement, floatingElement, update);

/**
* in the function-modifier manager, teardown of the previous modifier
* occurs before setup of the next
* https://github.com/ember-modifier/ember-modifier/blob/main/ember-modifier/src/-private/function-based/modifier-manager.ts#L58
*/
return cleanup;
},
);

function exposeMetadata() {
return {
name: 'metadata',
fn: (data) => {
return { data };
},
};
}
13 changes: 7 additions & 6 deletions addon/styles/_pix-multi-select.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@use "pix-design-tokens/typography";
@use "component-state/form";
@use "pix-design-tokens/shadows";
@use 'pix-design-tokens/typography';
@use 'component-state/form';
@use 'pix-design-tokens/shadows';

.pix-multi-select {
position: relative;
Expand Down Expand Up @@ -87,21 +87,22 @@
min-width: 100%;
max-height: 12.5rem;
margin: 0;
margin-top: var(--pix-spacing-1x);
padding: 0;
overflow-y: auto;
list-style-type: none;
background-color: var(--pix-neutral-0);
border-top: none;
border-radius: 0 0 var(--pix-spacing-1x) var(--pix-spacing-1x);
transition: all 0.1s ease-in-out;
transition:
opacity 0.1s ease-in-out,
visibility 0.1s ease-in-out;

&__item-label {
padding: var(--pix-spacing-2x) var(--pix-spacing-6x);
}

&--hidden {
visibility: hidden;
visibility: hidden !important;
opacity: 0;
}

Expand Down
17 changes: 8 additions & 9 deletions addon/styles/_pix-select.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@use "pix-design-tokens/typography";
@use "component-state/form";
@use "pix-design-tokens/shadows";
@use 'pix-design-tokens/typography';
@use 'component-state/form';
@use 'pix-design-tokens/shadows';

.pix-select {
position: relative;
Expand Down Expand Up @@ -28,15 +28,16 @@
width: 100%;
min-width: fit-content;
max-height: 12rem;
margin-top: var(--pix-spacing-1x);
padding: 0;
overflow-y: auto;
white-space: nowrap;
list-style-type: none;
background-color: var(--pix-neutral-0);
border-top: none;
border-radius: 0 0 var(--pix-spacing-1x) var(--pix-spacing-1x);
transition: all 0.1s ease-in-out;
transition:
opacity 0.1s ease-in-out,
visibility 0.1s ease-in-out;

&::-webkit-scrollbar {
width: 0.5rem;
Expand All @@ -59,13 +60,11 @@
}

&--closed {
visibility: hidden;
visibility: hidden !important;
opacity: 0;
}
}



&__search {
display: flex;
margin: var(--pix-spacing-2x) var(--pix-spacing-3x);
Expand Down Expand Up @@ -146,6 +145,6 @@

&__icon {
margin: auto var(--pix-spacing-1x);
color: var(--pix-neutral-100)
color: var(--pix-neutral-100);
}
}
Loading