Skip to content

Instantly share code, notes, and snippets.

@nfreear
Created June 8, 2022 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nfreear/d61ec03df08618a2c8abc40a301c770d to your computer and use it in GitHub Desktop.
Save nfreear/d61ec03df08618a2c8abc40a301c770d to your computer and use it in GitHub Desktop.
Date-picker / Calendar custom element/ Web component
<!doctype html> <title> Date picker calendar </title>
<style>
body { font: 1rem/1.7 sans-serif; margin: 1rem auto; max-width: 36rem; }
table { border: 1px solid #ddd; line-height: 2; X-width: 99%; }
th, td { border: 1px solid #ddd; min-width: 3.5rem; text-align: center; }
th { background: #eee; color: #444; }
button, summary { cursor: pointer; font: inherit; padding: .2rem .5rem; }
summary { border: 1px solid #ccc; display: inline-block; }
.details-inner {
border: 1px solid #ccc;
padding: .5rem;
position: absolute;
}
my-date-picker * {
border-radius: .2rem;
outline-offset: .3rem;
transition: all 500ms;
}
[aria-current], [aria-selected] {
cursor: help;
}
[ aria-current ] {
background: #eee;
color: #a00;
font-weight: bold;
}
[ aria-current ]:after {
content: ' [c]';
font-size: x-small;
}
[ aria-selected = true ] {
background: #ddd;
color: #080;
font-weight: bold;
}
[ aria-selected = true ]:after {
content: ' [s]';
font-size: x-small;
}
[ role = gridcell ][ tabindex ] {
cursor: pointer;
}
[ role = gridcell ][ tabindex ]:hover,
[ role = gridcell ][ tabindex ]:focus {
background: #eee;
border-color: black;
}
</style>
<h1> Date picker calendar </h1>
<my-date-picker selected="2022-06-09" today="2022-06-08">
<details>
<summary>📅 calendar</summary>
<div class="details-inner">
<button class="prev">&larr; Previous</button>
<button class="next">Next &rarr;</button>
<table>
<caption> <time datetime="2022-06">June 2022</time> </caption>
<thead>
<tr>
<th>Sun</th> <th>Mon</th> <th>Tue</th> <th>Wed</th> <th>Thu</th> <th>Fri</th> <th>Sat</th>
</tr>
</thead>
<tbody role="grid">
<tr>
<td>29</td> <td>30</td> <td>31</td> <td>1</td> <td>2</td> <td>3</td> <td>4</td>
</tr>
<tr>
<td>5</td> <td>6</td> <td>7</td> <td>8</td> <td>9</td> <td>10</td> <td>11</td>
</tr>
<tr>
<td>12</td> <td>13</td> <td>14</td> <td>15</td> <td>16</td> <td>17</td> <td>18</td>
</tr>
<tr>
<td>19</td> <td>20</td> <td>21</td> <td>22</td> <td>23</td> <td>24</td> <td>25</td>
</tr>
<tr>
<td>26</td> <td>27</td> <td>28</td> <td>29</td> <td>30</td> <td>1</td> <td>2</td>
</tr>
</tbody>
</table>
</div>
</details>
</my-date-picker>
<script type="module">
const PICKER = document.querySelector('my-date-picker');
const MONTH = PICKER.querySelector('time').getAttribute('datetime');
const GRID = PICKER.querySelector('table tbody');
const ROWS = GRID.querySelectorAll('tr');
const CELLS = GRID.querySelectorAll('td');
const DT_TODAY = new Date(PICKER.getAttribute('today'));
const DT_SELECTED = new Date(PICKER.getAttribute('selected'));
ROWS.forEach((row, week) => {
row.setAttribute('data-week', week);
const cells = row.querySelectorAll('td');
cells.forEach((cell, idx) => {
cell.setAttribute('role', 'gridcell');
cell.setAttribute('tabindex', -1);
cell.setAttribute('data-col', idx);
cell.setAttribute('data-coord', `${idx},${week}`);
});
});
function reset() {
CELLS.forEach(cell => {
// cell.setAttribute('role', 'gridcell');
cell.setAttribute('aria-selected', 'false');
cell.setAttribute('tabindex', -1);
});
}
const CELL_TODAY = [...CELLS].find(el => parseInt(el.textContent) === DT_TODAY.getDate());
const CELL_SELECTED = [...CELLS].find(el => parseInt(el.textContent) === DT_SELECTED.getDate());
CELL_TODAY.setAttribute('aria-current', 'date');
CELL_TODAY.title = 'current';
CELL_SELECTED.setAttribute('aria-selected', 'true');
CELL_SELECTED.setAttribute('tabindex', 0);
GRID.addEventListener('click', ev => {
const DATE = ev.target.textContent;
reset();
ev.target.setAttribute('aria-selected', true);
ev.target.setAttribute('tabindex', 0);
console.debug('click:', DATE, ev);
});
const HORIZ = {
Left: -1,
Right: 1,
Up: 0,
Down: 0
};
const VERT = {
Left: 0,
Right: 0,
Up: -1,
Down: 1
};
GRID.addEventListener('keyup', ev => {
const COL = parseInt(ev.target.dataset.col);
const WEEK = parseInt(ev.target.parentElement.dataset.week);
const BEFORE = ev.target.textContent;
const IS_ARROW = /Arrow/.test(ev.key);
const DIR = ev.key.replace(/Arrow/, '');
if (IS_ARROW) {
ev.preventDefault();
const COORDS = `${COL + HORIZ[DIR]},${WEEK + VERT[DIR]}`;
const CELL = GRID.querySelector(`[data-coord="${COORDS}"]`);
if (CELL) {
reset();
CELL.setAttribute('aria-selected', true);
CELL.setAttribute('tabindex', 0);
CELL.focus();
}
// AFTER = BEFORE + step;
console.debug(`keyup: "${COORDS}"`, COL, WEEK, IS_ARROW, DIR, ev);
}
});
console.debug('My date picker:', DT_TODAY, MONTH, PICKER);
</script>
<pre>
NDF, 08-June-2022.
</pre>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment