Skip to content

Instantly share code, notes, and snippets.

@art-solopov
Last active February 26, 2022 13:39
Show Gist options
  • Save art-solopov/bb2bfd530e4f2b3b6c2256727b933cb0 to your computer and use it in GitHub Desktop.
Save art-solopov/bb2bfd530e4f2b3b6c2256727b933cb0 to your computer and use it in GitHub Desktop.
A very simple calendar widget for Stimulus.JS
import moment from 'moment'
import { Controller } from 'stimulus'
const TOGGLE_BUTTON_TEMPLATE = `
<button data-target="cal-grid.toggleButton" data-action="cal-grid#toggleWidget"></button>
`
const DROPDOWN_TEMPLATE = `
<div class="cal-grid--dropdown" data-target="cal-grid.dropdown">
<div>
<button data-action="cal-grid#prevMonth">&lt;</button>
<span data-target="cal-grid.currMonth"></span>
<button data-action="cal-grid#nextMonth">&gt;</button>
</div>
<div class="cal-grid--grid" data-target="cal-grid.grid">
</div>
</div>
`
const CELL_TEMPLATE = `
<a href="#" class="cal-grid--cell" role="button" data-action="click->cal-grid#setDate"></a>
`
class CalGridController extends Controller {
static targets = ['input', 'toggleButton', 'dropdown', 'grid', 'currMonth']
connect () {
this.element.classList.add('cal-grid')
let tmpEl = document.createElement('div')
tmpEl.innerHTML = TOGGLE_BUTTON_TEMPLATE
this.element.appendChild(tmpEl.firstElementChild)
tmpEl.innerHTML = DROPDOWN_TEMPLATE
this.element.appendChild(tmpEl.firstElementChild)
this.hideWidget()
this.data.set('month', moment().date(1).format('YYYY-MM-DD'))
this.makeCalendar()
}
toggleWidget () {
if (this.element.dataset.widgetState == 'opened') { this.hideWidget(); }
else { this.showWidget() }
}
showWidget () {
this.toggleButtonTarget.innerText = 'Close'
this.element.dataset.widgetState = 'opened'
this.dropdownTarget.classList.add('show')
}
hideWidget () {
this.toggleButtonTarget.innerText = 'Open'
this.element.dataset.widgetState = 'closed'
this.dropdownTarget.classList.remove('show')
}
prevMonth () {
this.currMoment = this.currMoment.add(-1, 'month')
this.makeCalendar()
}
nextMonth () {
this.currMoment = this.currMoment.add(1, 'month')
this.makeCalendar()
}
makeCalendar() {
let d = this.currMoment
this.currMonthTarget.innerText = d.format('MMM YYYY')
this.gridTarget.innerHTML = ''
this.gridTarget.style.setProperty('--month-offset', d.isoWeekday() - 1)
for(let i = 1; i <= d.daysInMonth(); i++) {
let tmpEl = document.createElement('div')
tmpEl.innerHTML = CELL_TEMPLATE
let btn = tmpEl.firstElementChild
btn.dataset.date = d.date(i).format('YYYY-MM-DD')
btn.innerText = i
this.gridTarget.appendChild(btn)
}
}
setDate(e) {
e.preventDefault();
let date = e.target.dataset.date
this.inputTarget.value = date
}
get currMoment () {
return moment(this.data.get('month'))
}
set currMoment (m) {
if (moment.isMoment(m)) this.data.set('month', m.format('YYYY-MM-DD'))
else this.currMoment = moment(m)
}
}
export default CalGridController
.form-field {
padding: 0.5em;
width: 30em;
display: flex;
flex-flow: row nowrap;
& > input {
flex: 0 1 100%;
}
}
.cal-grid {
position: relative;
&--dropdown {
position: absolute;
top: 100%;
right: 0;
display: none;
padding: 0.25em;
border: 1px dotted #333;
&.show {
display: block;
}
}
&--grid {
display: grid;
grid-template-columns: repeat(7, 4em);
&::before {
content: '\00A0';
grid-area: 1 / 1 / 1 / span var(--month-offset)
}
}
&--cell {
text-align: right;
padding: 0.5em;
}
}
@braindeaf
Copy link

oh excellent, I think I'll be trying this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment