@stimulus-plumbers/controllers 0.4.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -8
- package/dist/controllers.manifest.json +125 -12
- package/dist/stimulus-plumbers-controllers.es.js +681 -312
- package/dist/stimulus-plumbers-controllers.umd.js +1 -1
- package/package.json +1 -1
- package/src/accessibility/aria.js +6 -0
- package/src/accessibility/focus.js +4 -20
- package/src/accessibility/keyboard.js +134 -28
- package/src/controllers/calendar_decade_controller.js +99 -0
- package/src/controllers/calendar_decade_selector_controller.js +14 -0
- package/src/controllers/calendar_month_controller.js +69 -17
- package/src/controllers/calendar_month_selector_controller.js +14 -0
- package/src/controllers/calendar_year_controller.js +104 -0
- package/src/controllers/calendar_year_selector_controller.js +14 -0
- package/src/controllers/combobox_date_controller.js +36 -85
- package/src/controllers/combobox_dropdown_controller.js +12 -25
- package/src/controllers/input_combobox_controller.js +0 -1
- package/src/controllers/modal_controller.js +4 -1
- package/src/controllers/popover_controller.js +5 -0
- package/src/controllers/timeline_controller.js +71 -0
- package/src/index.js +6 -1
- package/src/plumbers/calendar-selector.js +85 -0
- package/src/plumbers/calendar.js +25 -13
- package/src/plumbers/index.js +5 -0
- package/src/plumbers/visibility.js +3 -3
- package/src/controllers/calendar_observer_controller.js +0 -27
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Plumber from './plumber';
|
|
2
|
+
import { tryParseDate } from './plumber/date';
|
|
3
|
+
|
|
4
|
+
export class CalendarDaySelector extends Plumber {
|
|
5
|
+
constructor(controller, options = {}) {
|
|
6
|
+
super(controller, options);
|
|
7
|
+
this.handle = this.handle.bind(this);
|
|
8
|
+
this.onSelect = options.onSelect || null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
attach() {
|
|
12
|
+
this.element.addEventListener('click', this.handle);
|
|
13
|
+
}
|
|
14
|
+
detach() {
|
|
15
|
+
this.element.removeEventListener('click', this.handle);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
handle(event) {
|
|
19
|
+
if (!(event.target instanceof HTMLElement)) return;
|
|
20
|
+
event.preventDefault();
|
|
21
|
+
const input = event.target instanceof HTMLTimeElement ? event.target.parentElement : event.target;
|
|
22
|
+
if (input.disabled || input.getAttribute('aria-disabled') === 'true') return;
|
|
23
|
+
const time = event.target instanceof HTMLTimeElement ? event.target : event.target.querySelector('time');
|
|
24
|
+
if (!time) return;
|
|
25
|
+
const date = tryParseDate(time.dateTime);
|
|
26
|
+
if (!date) return;
|
|
27
|
+
this.dispatch('selecting', { target: input });
|
|
28
|
+
const iso = date.toISOString();
|
|
29
|
+
if (this.onSelect) {
|
|
30
|
+
this.awaitCallback(this.onSelect, iso);
|
|
31
|
+
} else {
|
|
32
|
+
this.dispatch('selected', { detail: { epoch: date.getTime(), iso } });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class CalendarMonthSelector extends Plumber {
|
|
38
|
+
constructor(controller, options = {}) {
|
|
39
|
+
super(controller, options);
|
|
40
|
+
this.handle = this.handle.bind(this);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
attach() {
|
|
44
|
+
this.element.addEventListener('click', this.handle);
|
|
45
|
+
}
|
|
46
|
+
detach() {
|
|
47
|
+
this.element.removeEventListener('click', this.handle);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
handle(event) {
|
|
51
|
+
const btn = event.target.closest('button[data-month]');
|
|
52
|
+
if (!btn || btn.disabled || btn.getAttribute('aria-disabled') === 'true') return;
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
const month = parseInt(btn.dataset.month, 10);
|
|
55
|
+
if (isNaN(month)) return;
|
|
56
|
+
this.dispatch('selected', { detail: { month } });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class CalendarYearSelector extends Plumber {
|
|
61
|
+
constructor(controller, options = {}) {
|
|
62
|
+
super(controller, options);
|
|
63
|
+
this.handle = this.handle.bind(this);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
attach() {
|
|
67
|
+
this.element.addEventListener('click', this.handle);
|
|
68
|
+
}
|
|
69
|
+
detach() {
|
|
70
|
+
this.element.removeEventListener('click', this.handle);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
handle(event) {
|
|
74
|
+
const btn = event.target.closest('button[data-year]');
|
|
75
|
+
if (!btn || btn.disabled || btn.getAttribute('aria-disabled') === 'true') return;
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
const year = parseInt(btn.dataset.year, 10);
|
|
78
|
+
if (isNaN(year)) return;
|
|
79
|
+
this.dispatch('selected', { detail: { year } });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export const attachCalendarDaySelector = (controller, options) => new CalendarDaySelector(controller, options);
|
|
84
|
+
export const attachCalendarMonthSelector = (controller, options) => new CalendarMonthSelector(controller, options);
|
|
85
|
+
export const attachCalendarYearSelector = (controller, options) => new CalendarYearSelector(controller, options);
|
package/src/plumbers/calendar.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import Plumber from './plumber';
|
|
2
2
|
import { isValidDate, tryParseDate } from './plumber/date';
|
|
3
3
|
|
|
4
|
-
const
|
|
4
|
+
const DAYS_PER_ROW = 7;
|
|
5
|
+
const YEAR_SIZE = 12;
|
|
6
|
+
const DECADE_SIZE = 10;
|
|
5
7
|
|
|
6
8
|
const defaultOptions = {
|
|
7
9
|
locales: ['default'],
|
|
@@ -131,8 +133,8 @@ export class Calendar extends Plumber {
|
|
|
131
133
|
const current = new Date(currentYear, currentMonth, i);
|
|
132
134
|
daysOfMonth.push(parseDate(current));
|
|
133
135
|
}
|
|
134
|
-
const mod = daysOfMonth.length %
|
|
135
|
-
const trailing = mod === 0 ? 0 :
|
|
136
|
+
const mod = daysOfMonth.length % DAYS_PER_ROW;
|
|
137
|
+
const trailing = mod === 0 ? 0 : DAYS_PER_ROW - mod;
|
|
136
138
|
for (let i = 1; i <= trailing; i++) {
|
|
137
139
|
const next = new Date(currentYear, currentMonth + 1, i);
|
|
138
140
|
daysOfMonth.push(parseDate(next));
|
|
@@ -150,14 +152,21 @@ export class Calendar extends Plumber {
|
|
|
150
152
|
const numericFormatter = new Intl.DateTimeFormat(this.localesValue, { month: 'numeric' });
|
|
151
153
|
|
|
152
154
|
const monthsOfYear = [];
|
|
153
|
-
for (let i = 0; i <
|
|
154
|
-
const
|
|
155
|
+
for (let i = 0; i < YEAR_SIZE; i++) {
|
|
156
|
+
const date = new Date(this.year, i);
|
|
157
|
+
const monthStart = new Date(this.year, i, 1);
|
|
158
|
+
const monthEnd = new Date(this.year, i + 1, 0);
|
|
159
|
+
const shortName = shortFormatter.format(date);
|
|
160
|
+
const longName = longFormatter.format(date);
|
|
161
|
+
const rangeDisabled = (this.since && monthEnd < this.since) || (this.till && monthStart > this.till);
|
|
162
|
+
const listDisabled = this.disabledMonths.some((str) => i == str || shortName === str || longName === str);
|
|
155
163
|
monthsOfYear.push({
|
|
156
|
-
date:
|
|
157
|
-
value:
|
|
158
|
-
long:
|
|
159
|
-
short:
|
|
160
|
-
numeric: numericFormatter.format(
|
|
164
|
+
date: date,
|
|
165
|
+
value: date.getMonth(),
|
|
166
|
+
long: longName,
|
|
167
|
+
short: shortName,
|
|
168
|
+
numeric: numericFormatter.format(date),
|
|
169
|
+
disabled: rangeDisabled || listDisabled,
|
|
161
170
|
});
|
|
162
171
|
}
|
|
163
172
|
return monthsOfYear;
|
|
@@ -169,13 +178,16 @@ export class Calendar extends Plumber {
|
|
|
169
178
|
* @returns {Array<Object>} Array of year objects
|
|
170
179
|
*/
|
|
171
180
|
buildYearsOfDecade() {
|
|
172
|
-
const decadeStart = Math.floor(this.year /
|
|
181
|
+
const decadeStart = Math.floor(this.year / DECADE_SIZE) * DECADE_SIZE;
|
|
173
182
|
const yearsOfDecade = [];
|
|
174
|
-
for (let i = decadeStart - 1; i <= decadeStart +
|
|
183
|
+
for (let i = decadeStart - 1; i <= decadeStart + DECADE_SIZE; i++) {
|
|
184
|
+
const bufferYear = i < decadeStart || i > decadeStart + DECADE_SIZE - 1;
|
|
185
|
+
const rangeDisabled = (this.since && i < this.since.getFullYear()) || (this.till && i > this.till.getFullYear());
|
|
186
|
+
const listDisabled = this.disabledYears.some((str) => i == str);
|
|
175
187
|
yearsOfDecade.push({
|
|
176
188
|
value: i,
|
|
177
189
|
current: i === this.year,
|
|
178
|
-
|
|
190
|
+
disabled: bufferYear || rangeDisabled || listDisabled,
|
|
179
191
|
});
|
|
180
192
|
}
|
|
181
193
|
return yearsOfDecade;
|
package/src/plumbers/index.js
CHANGED
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
export { initCalendar } from './calendar';
|
|
6
|
+
export {
|
|
7
|
+
attachCalendarDaySelector,
|
|
8
|
+
attachCalendarMonthSelector,
|
|
9
|
+
attachCalendarYearSelector,
|
|
10
|
+
} from './calendar-selector';
|
|
6
11
|
export { attachContentLoader } from './content_loader';
|
|
7
12
|
export { attachDismisser } from './dismisser';
|
|
8
13
|
export { attachFlipper } from './flipper';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Plumber from './plumber';
|
|
2
2
|
import { visibilityConfig } from './plumber/config';
|
|
3
|
+
import { setExpanded, setHidden } from '../accessibility/aria';
|
|
3
4
|
|
|
4
5
|
const defaultOptions = {
|
|
5
6
|
visibility: 'visibility',
|
|
@@ -62,8 +63,7 @@ export class Visibility extends Plumber {
|
|
|
62
63
|
if (visible) target.classList.remove(hiddenClass);
|
|
63
64
|
else target.classList.add(hiddenClass);
|
|
64
65
|
} else {
|
|
65
|
-
|
|
66
|
-
else target.setAttribute('hidden', true);
|
|
66
|
+
setHidden(target, !visible);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
@@ -72,7 +72,7 @@ export class Visibility extends Plumber {
|
|
|
72
72
|
* @param {boolean} isExpanded - True to mark as expanded, false to mark as collapsed
|
|
73
73
|
*/
|
|
74
74
|
activate(isExpanded) {
|
|
75
|
-
if (this.activator) this.activator
|
|
75
|
+
if (this.activator) setExpanded(this.activator, isExpanded);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Controller } from '@hotwired/stimulus';
|
|
2
|
-
import { tryParseDate } from '../plumbers/plumber/date';
|
|
3
|
-
|
|
4
|
-
export default class extends Controller {
|
|
5
|
-
onSelect(event) {
|
|
6
|
-
if (!(event.target instanceof HTMLElement)) return;
|
|
7
|
-
|
|
8
|
-
event.preventDefault();
|
|
9
|
-
const input = event.target instanceof HTMLTimeElement ? event.target.parentElement : event.target;
|
|
10
|
-
if (input.disabled || input.getAttribute('aria-disabled') === 'true') return;
|
|
11
|
-
|
|
12
|
-
this.dispatch('selecting', { target: input });
|
|
13
|
-
const time = event.target instanceof HTMLTimeElement ? event.target : event.target.querySelector('time');
|
|
14
|
-
if (!time) return console.error(`unable to locate time element within ${input}`);
|
|
15
|
-
|
|
16
|
-
const date = tryParseDate(time.dateTime);
|
|
17
|
-
if (!date) return console.error(`unable to parse ${time.dateTime} found within the time element`);
|
|
18
|
-
|
|
19
|
-
this.select(date.toISOString());
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
select(iso) {
|
|
23
|
-
const date = tryParseDate(iso);
|
|
24
|
-
if (!date) return;
|
|
25
|
-
this.dispatch('selected', { detail: { epoch: date.getTime(), iso } });
|
|
26
|
-
}
|
|
27
|
-
}
|