missive / emoji-mart

🏪 One component to pick them all
https://missiveapp.com/open/emoji-mart
MIT License
8.58k stars 825 forks source link

How to use .update method ? #919

Open midnightgamer opened 3 months ago

midnightgamer commented 3 months ago

``I am using emoji-mart in my application for making Emoji Picker as Popup and I wanted picker to have focus every time i open I was getting .update method but was not able to found any document for the same

My Picker Util, which makes a popup

import { Picker } from 'emoji-mart';

type Props = {
  triggerElement: HTMLElement;
  position?: 'center' | 'baseline';
  actionOnEmojiSelect: (emoji: string) => void;
};

class EmojiPicker {
  private static instance: EmojiPicker;
  private picker: Picker | null = null;
  private isOpen: boolean = false;
  private triggerElement: HTMLElement | null = null;
  private parentElement: HTMLElement | null = null;
  private actionOnEmojiSelect: (data: any) => void;

  private constructor(triggerElement) {
    this.triggerElement = triggerElement;
    this.parentElement = document.getElementById('emojiPickerContainer');
    this.init(triggerElement);
  }

  init(triggerElement) {
    this.triggerElement = triggerElement;
    this.hidePicker();
    this.picker = new Picker({
      parent: this.parentElement,
      onEmojiSelect: e => {
        if (this.actionOnEmojiSelect) {
          this.actionOnEmojiSelect({ emoji: e.native });
        }
        this.closePicker();
      },
      onClickOutside: e => {
        const isTrigger = this.triggerElement?.dataset.trigger === 'emojiPicker';
        const position = this.triggerElement?.dataset.triggerPosition;

        if (isTrigger && (e.target === this.triggerElement || this.triggerElement?.contains(e.target))) {
          return this.getTriggerPositionAndSetToContainer(
            this.triggerElement,
            this.parentElement,
            position ? position : 'center'
          );
        }

        this.closePicker();
      }
    });
  }

  public static getInstance(triggerElement): EmojiPicker {
    if (!EmojiPicker.instance) {
      EmojiPicker.instance = new EmojiPicker(triggerElement);
    }
    EmojiPicker.instance.triggerElement = triggerElement;
    return EmojiPicker.instance;
  }

  public openPicker = (props: Props) => {
    console.log('🚀 ~ EmojiPicker ~ openPicker:', this.picker);

    this.isOpen = true;
    this.showPicker(props);
    this.autoFocus();
    this.actionOnEmojiSelect = props.actionOnEmojiSelect;
  };

  public closePicker = () => {
    this.isOpen = false;
    this.hidePicker();
    this.actionOnEmojiSelect = () => {
      return;
    };
  };

  public togglePicker = (props: Props) => {
    if (this.isOpen) {
      this.closePicker();
    } else {
      this.openPicker(props);
    }
  };

  private hidePicker(): void {
    if (this.parentElement) {
      this.parentElement.style.display = 'none';
    }
  }

  private showPicker(props: Props): void {
    if (this.parentElement) {
      this.getTriggerPositionAndSetToContainer(this.triggerElement, this.parentElement, props.position);
    }
  }

  private autoFocus = () => {
    this.picker?.update({ autoFocus: true });
  };

  private getTriggerPositionAndSetToContainer = (triggerElement, emojiPickerContainer, vertical = 'center') => {
    const triggerElementRect = triggerElement.getBoundingClientRect();
    const emojiPickerRect = { width: 352, height: 435 };
    const viewportHeight = window.innerHeight || document.documentElement.clientHeight;

    let top;

    if (vertical === 'baseline') {
      // Align picker baseline with trigger baseline
      top = triggerElementRect.bottom - emojiPickerRect.height - 40;
    } else {
      // Center align vertically
      top = Math.max(0, triggerElementRect.top + (triggerElementRect.height - emojiPickerRect.height) / 2);
    }

    // Check if there's enough space below, if not, adjust the top position
    if (top + emojiPickerRect.height > viewportHeight) {
      top = viewportHeight - emojiPickerRect.height;
    }

    // Check if there's enough space above, if not, adjust the top position
    if (top < 0) {
      top = 0;
    }

    // Calculate left position to place the picker to the left of the trigger
    const left = triggerElementRect.left - emojiPickerRect.width;

    // Set position and display style
    emojiPickerContainer.style.left = `${left}px`;
    emojiPickerContainer.style.top = `${top}px`;
    emojiPickerContainer.style.position = 'absolute';
    emojiPickerContainer.style.display = 'block';
  };
}

export default EmojiPicker;