Skip to content

Commit

Permalink
Using pinned stimulus-use for date-picker and select
Browse files Browse the repository at this point in the history
  • Loading branch information
aviflombaum committed Jul 22, 2024
1 parent 90dbe4a commit fd101c2
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 63 deletions.
2 changes: 1 addition & 1 deletion app/javascript/controllers/ui/date-picker_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { Controller } from "@hotwired/stimulus";
import IsoDate from "../../utils/iso_date";
import { useClickOutside } from "https://ga.jspm.io/npm:stimulus-use@0.52.2/dist/index.js";
import { useClickOutside } from "stimulus-use";

// All dates are local, not UTC.
export default class UIDatePickerController extends Controller {
Expand Down
127 changes: 66 additions & 61 deletions app/javascript/controllers/ui/select_controller.js
Original file line number Diff line number Diff line change
@@ -1,129 +1,134 @@
import { Controller } from "@hotwired/stimulus"
import { useClickOutside } from "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js";
import { disableBodyScroll, enableBodyScroll, clearAllBodyScrollLocks } from "../../utils/bodyScrollLock.js"
import { Controller } from "@hotwired/stimulus";
import { useClickOutside } from "stimulus-use";
import {
disableBodyScroll,
enableBodyScroll,
clearAllBodyScrollLocks,
} from "../../utils/bodyScrollLock.js";

export default class UISelectController extends Controller {
static targets = ["value", "menu", "wrapper"]
static values = { value: String }
static targets = ["value", "menu", "wrapper"];
static values = { value: String };

connect() {
useClickOutside(this);
this.valueTarget.textContent = this.valueValue || this.valueTarget.textContent || "Select an option"
this.selectedOption = null
this.valueTarget.textContent =
this.valueValue || this.valueTarget.textContent || "Select an option";
this.selectedOption = null;
}

disconnect() {
clearAllBodyScrollLocks()
clearAllBodyScrollLocks();
}

clickOutside(event) {
this.menuTarget.classList.add("hidden")
this.menuTarget.classList.add("hidden");
}

toggle() {
this.menuTarget.classList.toggle("hidden")
this.wrapperTarget.querySelector("button").focus()
this.menuTarget.classList.toggle("hidden");
this.wrapperTarget.querySelector("button").focus();

const optionList = this.menuTarget.children
const currentValue = this.valueTarget.textContent
let childElement = null
const optionList = this.menuTarget.children;
const currentValue = this.valueTarget.textContent;
let childElement = null;

if (!this.menuTarget.classList.contains("hidden")) {
this.adjustScrollPosition()
disableBodyScroll(this.menuTarget)
this.adjustScrollPosition();
disableBodyScroll(this.menuTarget);
} else {
enableBodyScroll(this.menuTarget)
enableBodyScroll(this.menuTarget);
}

Array.from(optionList).forEach(function(child){
if(currentValue == child.textContent) {
child.classList.add("bg-gray-200", "text-gray-900")
childElement = child
Array.from(optionList).forEach(function (child) {
if (currentValue == child.textContent) {
child.classList.add("bg-gray-200", "text-gray-900");
childElement = child;
}
})
});

if(childElement) {
this.selectedOption = childElement
childElement.scrollIntoView({ behavior: 'instant', block: 'nearest', inline: 'start' })
if (childElement) {
this.selectedOption = childElement;
childElement.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
}
}

adjustScrollPosition() {
const menuHeight = this.menuTarget.offsetHeight
const optionsHeight = this.menuTarget.scrollHeight
const menuHeight = this.menuTarget.offsetHeight;
const optionsHeight = this.menuTarget.scrollHeight;
if (optionsHeight > menuHeight) {
this.menuTarget.style.maxHeight = `${menuHeight}px`
this.menuTarget.style.overflowY = "scroll"
this.menuTarget.style.maxHeight = `${menuHeight}px`;
this.menuTarget.style.overflowY = "scroll";
} else {
this.menuTarget.style.maxHeight = "auto"
this.menuTarget.style.overflowY = "auto"
this.menuTarget.style.maxHeight = "auto";
this.menuTarget.style.overflowY = "auto";
}
}

select(event) {
const option = event.target
this.setSelectedOption(option)
this.selectCurrentOption()
const option = event.target;
this.setSelectedOption(option);
this.selectCurrentOption();
}

setValue(value) {
this.valueValue = value
this.valueTarget.textContent = value
this.valueValue = value;
this.valueTarget.textContent = value;
}

key(event) {
if(this.menuTarget.classList.contains("hidden")) return
if (this.menuTarget.classList.contains("hidden")) return;

switch (event.key) {
case "Escape":
this.menuTarget.classList.add("hidden")
break
this.menuTarget.classList.add("hidden");
break;
case "ArrowUp":
this.selectPreviousOption(event)
break
this.selectPreviousOption(event);
break;
case "ArrowDown":
this.selectNextOption(event)
break
this.selectNextOption(event);
break;
case "Enter":
this.selectCurrentOption()
break
this.selectCurrentOption();
break;
}
}

selectPreviousOption(event) {
const selected = this.selectedOption //this.options.querySelector(".selected")
const prevOption = selected ? selected.previousElementSibling : this.options.lastElementChild
this.setSelectedOption(prevOption)
const selected = this.selectedOption; //this.options.querySelector(".selected")
const prevOption = selected ? selected.previousElementSibling : this.options.lastElementChild;
this.setSelectedOption(prevOption);
}

selectNextOption(event) {
const selected = this.selectedOption //this.options.querySelector(".selected")
const nextOption = selected ? selected.nextElementSibling : this.options.firstElementChild
this.setSelectedOption(nextOption)
const selected = this.selectedOption; //this.options.querySelector(".selected")
const nextOption = selected ? selected.nextElementSibling : this.options.firstElementChild;
this.setSelectedOption(nextOption);
}

selectCurrentOption() {
const selected = this.selectedOption
const selected = this.selectedOption;
if (selected) {
this.valueTarget.textContent = selected.textContent
this.menuTarget.classList.add("hidden")
this.valueTarget.textContent = selected.textContent;
this.menuTarget.classList.add("hidden");

this.wrapperTarget.textContent = selected.getAttribute('value')
this.wrapperTarget.dispatchEvent(new Event('change'))
this.wrapperTarget.textContent = selected.getAttribute("value");
this.wrapperTarget.dispatchEvent(new Event("change"));
}
}

setSelectedOption(option) {
if(!option) return
if (!option) return;

// Reset the previously selected option
if (this.selectedOption) {
this.selectedOption.classList.remove("bg-gray-200", "text-gray-900")
this.selectedOption.classList.remove("bg-gray-200", "text-gray-900");
}

// Set the new selected option
option.classList.add("bg-gray-200", "text-gray-900")
this.selectedOption = option
option.scrollIntoView({ behavior: 'instant', block: 'nearest', inline: 'start' })
option.classList.add("bg-gray-200", "text-gray-900");
this.selectedOption = option;
option.scrollIntoView({ behavior: "instant", block: "nearest", inline: "start" });
}
}
2 changes: 1 addition & 1 deletion config/importmap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
pin_all_from "app/javascript/controllers", under: "controllers"
pin "@kanety/stimulus-static-actions", to: "https://ga.jspm.io/npm:@kanety/stimulus-static-actions@1.0.1/dist/index.modern.js", preload: true
pin "highlight.js", to: "https://ga.jspm.io/npm:highlight.js@11.8.0/es/index.js", preload: true
pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.51.3/dist/index.js", preload: true
pin "stimulus-use", to: "https://ga.jspm.io/npm:stimulus-use@0.52.2/dist/index.js", preload: true
pin "stimulus-dropdown", to: "https://ga.jspm.io/npm:stimulus-dropdown@2.1.0/dist/stimulus-dropdown.mjs", preload: true
pin "hotkeys-js", to: "https://ga.jspm.io/npm:hotkeys-js@3.10.4/dist/hotkeys.esm.js", preload: true
pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.8/lib/index.js", preload: true

0 comments on commit fd101c2

Please sign in to comment.