stimeo-ui 0.16.0 → 0.18.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +136 -0
- data/README.md +27 -11
- data/dist/cable/index.js +384 -449
- data/dist/controllers/accordion_controller.js +174 -55
- data/dist/controllers/alert_dialog_controller.js +903 -235
- data/dist/controllers/announcer_controller.js +238 -205
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +414 -145
- data/dist/controllers/avatar_controller.js +185 -107
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +282 -58
- data/dist/controllers/calendar_controller.js +228 -260
- data/dist/controllers/carousel_controller.js +536 -436
- data/dist/controllers/character_counter_controller.js +493 -207
- data/dist/controllers/checkbox_controller.js +38 -74
- data/dist/controllers/clipboard_controller.js +261 -149
- data/dist/controllers/collapsible_controller.js +304 -120
- data/dist/controllers/color_picker_controller.js +178 -146
- data/dist/controllers/combobox_controller.js +522 -156
- data/dist/controllers/command_palette_controller.js +1156 -395
- data/dist/controllers/conditional_fields_controller.js +300 -171
- data/dist/controllers/confirm_controller.js +980 -262
- data/dist/controllers/context_menu_controller.js +346 -153
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +281 -158
- data/dist/controllers/currency_input_controller.js +575 -225
- data/dist/controllers/data_grid_controller.js +248 -214
- data/dist/controllers/date_range_picker_controller.js +338 -235
- data/dist/controllers/dialog_controller.js +899 -237
- data/dist/controllers/direct_upload_controller.js +97 -167
- data/dist/controllers/dirty_form_controller.js +72 -69
- data/dist/controllers/dismissible_controller.js +196 -24
- data/dist/controllers/drawer_controller.js +974 -355
- data/dist/controllers/dropdown_controller.js +339 -96
- data/dist/controllers/editable_controller.js +315 -88
- data/dist/controllers/empty_state_controller.js +79 -58
- data/dist/controllers/file_dropzone_controller.js +322 -211
- data/dist/controllers/filter_controller.js +77 -42
- data/dist/controllers/flash_controller.js +238 -347
- data/dist/controllers/focus_controller.js +679 -240
- data/dist/controllers/form_field_controller.js +156 -132
- data/dist/controllers/form_validation_controller.js +31 -97
- data/dist/controllers/frame_loading_controller.js +275 -217
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +327 -130
- data/dist/controllers/idle_controller.js +271 -101
- data/dist/controllers/input_mask_controller.js +483 -97
- data/dist/controllers/intersection_controller.js +150 -106
- data/dist/controllers/lazy_frame_controller.js +24 -68
- data/dist/controllers/listbox_controller.js +523 -240
- data/dist/controllers/local_time_controller.js +102 -77
- data/dist/controllers/masonry_controller.js +80 -96
- data/dist/controllers/menu_controller.js +302 -188
- data/dist/controllers/menubar_controller.js +232 -345
- data/dist/controllers/meter_controller.js +417 -87
- data/dist/controllers/multi_select_controller.js +785 -393
- data/dist/controllers/navigation_menu_controller.js +190 -250
- data/dist/controllers/nested_form_controller.js +396 -192
- data/dist/controllers/network_status_controller.js +268 -62
- data/dist/controllers/number_input_controller.js +610 -303
- data/dist/controllers/optimistic_controller.js +45 -87
- data/dist/controllers/otp_controller.js +658 -255
- data/dist/controllers/overflow_indicator_controller.js +122 -113
- data/dist/controllers/overflow_menu_controller.js +470 -295
- data/dist/controllers/pagination_controller.js +332 -123
- data/dist/controllers/password_reveal_controller.js +392 -121
- data/dist/controllers/password_strength_controller.js +111 -158
- data/dist/controllers/persist_controller.js +165 -111
- data/dist/controllers/pointer_drag_controller.js +434 -188
- data/dist/controllers/popover_controller.js +269 -113
- data/dist/controllers/portal_controller.js +215 -76
- data/dist/controllers/preview_guard_controller.js +243 -116
- data/dist/controllers/progress_controller.js +391 -78
- data/dist/controllers/radio_group_controller.js +278 -211
- data/dist/controllers/range_slider_controller.js +220 -113
- data/dist/controllers/rating_controller.js +458 -211
- data/dist/controllers/read_more_controller.js +138 -63
- data/dist/controllers/reading_progress_controller.js +166 -137
- data/dist/controllers/relative_time_controller.js +211 -112
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +103 -38
- data/dist/controllers/resizable_controller.js +299 -150
- data/dist/controllers/roving_controller.js +49 -88
- data/dist/controllers/scroll_area_controller.js +490 -246
- data/dist/controllers/scroll_restore_controller.js +14 -55
- data/dist/controllers/scroll_visibility_controller.js +432 -149
- data/dist/controllers/scrollspy_controller.js +193 -248
- data/dist/controllers/separator_controller.js +285 -142
- data/dist/controllers/sidebar_controller.js +1069 -404
- data/dist/controllers/skeleton_controller.js +259 -101
- data/dist/controllers/slider_controller.js +187 -86
- data/dist/controllers/smart_sticky_header_controller.js +173 -66
- data/dist/controllers/sortable_controller.js +26 -125
- data/dist/controllers/spinner_controller.js +288 -173
- data/dist/controllers/step_indicator_controller.js +105 -46
- data/dist/controllers/stepper_controller.js +120 -73
- data/dist/controllers/stick_to_bottom_controller.js +275 -140
- data/dist/controllers/sticky_observer_controller.js +220 -49
- data/dist/controllers/submit_once_controller.js +376 -227
- data/dist/controllers/switch_controller.js +201 -84
- data/dist/controllers/tabs_controller.js +41 -30
- data/dist/controllers/tags_input_controller.js +293 -173
- data/dist/controllers/textarea_autosize_controller.js +108 -74
- data/dist/controllers/theme_controller.js +140 -116
- data/dist/controllers/time_picker_controller.js +331 -127
- data/dist/controllers/toast_controller.js +244 -324
- data/dist/controllers/toggle_group_controller.js +252 -191
- data/dist/controllers/toolbar_controller.js +219 -120
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +283 -369
- data/dist/index.js +8573 -10410
- data/dist/positioning/index.js +154 -47
- data/lib/generators/stimeo/install/install_generator.rb +7 -2
- data/lib/generators/stimeo/install/templates/stimeo.js +19 -6
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,24 +1,48 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var FOCUS_EVENTS = /* @__PURE__ */ new Set(["blur", "focus", "focusin", "focusout"]);
|
|
4
|
+
var POINTER_EVENTS = /* @__PURE__ */ new Set(["mouseenter", "mouseleave", "pointerenter", "pointerleave"]);
|
|
5
|
+
function stateReasonFor(event) {
|
|
6
|
+
if (!event) return "api";
|
|
7
|
+
if (FOCUS_EVENTS.has(event.type)) return "focus";
|
|
8
|
+
if (POINTER_EVENTS.has(event.type)) return "pointer";
|
|
9
|
+
return "user";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function actionSource(source) {
|
|
13
|
+
if (source instanceof HTMLElement) {
|
|
14
|
+
return { event: null, host: source, origin: source, reason: "api" };
|
|
15
|
+
}
|
|
16
|
+
const event = source ?? null;
|
|
17
|
+
return {
|
|
18
|
+
event,
|
|
19
|
+
host: event?.currentTarget instanceof Element ? event.currentTarget : null,
|
|
20
|
+
origin: event?.target instanceof Element ? event.target : null,
|
|
21
|
+
reason: stateReasonFor(event)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
4
24
|
|
|
5
|
-
// src/utils/logical_scroll.ts
|
|
6
25
|
function isRtl(element) {
|
|
7
26
|
return window.getComputedStyle(element).direction === "rtl";
|
|
8
27
|
}
|
|
9
28
|
|
|
10
|
-
// src/utils/arrow_step.ts
|
|
11
29
|
function logicalArrowKey(key, element) {
|
|
12
30
|
if (key !== "ArrowRight" && key !== "ArrowLeft") return key;
|
|
13
31
|
if (!isRtl(element)) return key;
|
|
14
32
|
return key === "ArrowRight" ? "ArrowLeft" : "ArrowRight";
|
|
15
33
|
}
|
|
34
|
+
function gridDirectionElement(grid, root) {
|
|
35
|
+
const table = grid.closest("table");
|
|
36
|
+
return table !== null && root.contains(table) ? table : grid;
|
|
37
|
+
}
|
|
16
38
|
function isReservedArrowChord(event, allow = []) {
|
|
17
39
|
if (!event.key.startsWith("Arrow")) return false;
|
|
18
40
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
19
41
|
}
|
|
42
|
+
function hasModifierChord(event) {
|
|
43
|
+
return event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
|
44
|
+
}
|
|
20
45
|
|
|
21
|
-
// src/utils/intl_format.ts
|
|
22
46
|
var formatters = /* @__PURE__ */ new WeakMap();
|
|
23
47
|
function intlFormatter(factory, locale, options, fallbackLocale) {
|
|
24
48
|
const cache = cacheFor(factory);
|
|
@@ -47,7 +71,6 @@ function construct(factory, locale, options, fallbackLocale) {
|
|
|
47
71
|
}
|
|
48
72
|
}
|
|
49
73
|
|
|
50
|
-
// src/utils/dates.ts
|
|
51
74
|
function toISODateString(date) {
|
|
52
75
|
const year = date.getFullYear();
|
|
53
76
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
@@ -80,12 +103,35 @@ function parseISOMonthString(monthStr) {
|
|
|
80
103
|
function toISOMonthString(date) {
|
|
81
104
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
|
|
82
105
|
}
|
|
106
|
+
function monthGridDays(year, month, weekStart) {
|
|
107
|
+
const first = new Date(year, month - 1, 1);
|
|
108
|
+
const offset = (first.getDay() - weekStart + 7) % 7;
|
|
109
|
+
const current = new Date(first);
|
|
110
|
+
current.setDate(first.getDate() - offset);
|
|
111
|
+
const days = [];
|
|
112
|
+
for (let i = 0; i < 42; i++) {
|
|
113
|
+
days.push(new Date(current));
|
|
114
|
+
current.setDate(current.getDate() + 1);
|
|
115
|
+
}
|
|
116
|
+
return days;
|
|
117
|
+
}
|
|
118
|
+
function dateInMonthClamped(date, year, month) {
|
|
119
|
+
const target = new Date(year, month - 1, 1);
|
|
120
|
+
const lastDay = new Date(target.getFullYear(), target.getMonth() + 1, 0).getDate();
|
|
121
|
+
target.setDate(Math.min(date.getDate(), lastDay));
|
|
122
|
+
return target;
|
|
123
|
+
}
|
|
124
|
+
function shiftMonthClamped(date, delta) {
|
|
125
|
+
return dateInMonthClamped(date, date.getFullYear(), date.getMonth() + 1 + delta);
|
|
126
|
+
}
|
|
127
|
+
function shiftYearClamped(date, delta) {
|
|
128
|
+
return dateInMonthClamped(date, date.getFullYear() + delta, date.getMonth() + 1);
|
|
129
|
+
}
|
|
83
130
|
var MONTH_LABEL_OPTIONS = { month: "long", year: "numeric" };
|
|
84
131
|
function monthLabelFormatter(locale) {
|
|
85
132
|
return intlFormatter(Intl.DateTimeFormat, locale, MONTH_LABEL_OPTIONS, "en");
|
|
86
133
|
}
|
|
87
134
|
|
|
88
|
-
// src/utils/field_mirror.ts
|
|
89
135
|
function writeField(field, value) {
|
|
90
136
|
if (field.value === value) return false;
|
|
91
137
|
field.value = value;
|
|
@@ -95,32 +141,29 @@ function commitField(target) {
|
|
|
95
141
|
target.dispatchEvent(new Event("change", { bubbles: true }));
|
|
96
142
|
}
|
|
97
143
|
|
|
98
|
-
// src/utils/locale.ts
|
|
99
144
|
function resolveLocale(element, declared = "") {
|
|
100
145
|
return declared || element.closest("[lang]")?.getAttribute("lang") || void 0;
|
|
101
146
|
}
|
|
102
147
|
|
|
103
|
-
// src/utils/microtask_coalescer.ts
|
|
104
148
|
var MicrotaskCoalescer = class {
|
|
105
149
|
#run;
|
|
106
150
|
#queued = false;
|
|
107
151
|
#active = false;
|
|
108
152
|
#generation = 0;
|
|
109
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
110
153
|
constructor(run) {
|
|
111
154
|
this.#run = run;
|
|
112
155
|
}
|
|
113
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
114
156
|
activate() {
|
|
115
157
|
this.#active = true;
|
|
116
158
|
}
|
|
117
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
118
159
|
cancel() {
|
|
119
160
|
this.#active = false;
|
|
120
161
|
this.#queued = false;
|
|
121
162
|
this.#generation += 1;
|
|
122
163
|
}
|
|
123
|
-
|
|
164
|
+
get pending() {
|
|
165
|
+
return this.#queued;
|
|
166
|
+
}
|
|
124
167
|
schedule() {
|
|
125
168
|
if (!this.#active || this.#queued) return;
|
|
126
169
|
this.#queued = true;
|
|
@@ -133,15 +176,95 @@ var MicrotaskCoalescer = class {
|
|
|
133
176
|
}
|
|
134
177
|
};
|
|
135
178
|
|
|
136
|
-
|
|
179
|
+
var ListenerSet = class {
|
|
180
|
+
#abort = new AbortController();
|
|
181
|
+
add(target, type, handler, options) {
|
|
182
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
183
|
+
}
|
|
184
|
+
dispose() {
|
|
185
|
+
this.#abort.abort();
|
|
186
|
+
this.#abort = new AbortController();
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
var MorphRenderWatcher = class {
|
|
191
|
+
#listeners = new ListenerSet();
|
|
192
|
+
#pass;
|
|
193
|
+
#element = null;
|
|
194
|
+
#onMorph = () => this.#pass.schedule();
|
|
195
|
+
constructor(run) {
|
|
196
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
197
|
+
}
|
|
198
|
+
observe(element) {
|
|
199
|
+
if (this.#element === element) return;
|
|
200
|
+
this.disconnect();
|
|
201
|
+
this.#element = element;
|
|
202
|
+
this.#pass.activate();
|
|
203
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
204
|
+
}
|
|
205
|
+
schedule() {
|
|
206
|
+
this.#pass.schedule();
|
|
207
|
+
}
|
|
208
|
+
get pending() {
|
|
209
|
+
return this.#pass.pending;
|
|
210
|
+
}
|
|
211
|
+
disconnect() {
|
|
212
|
+
this.#listeners.dispose();
|
|
213
|
+
this.#pass.cancel();
|
|
214
|
+
this.#element = null;
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
function matchesNumberBounds(value, bounds) {
|
|
219
|
+
if (!Number.isFinite(value)) {
|
|
220
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
221
|
+
if (direction === null) return false;
|
|
222
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
223
|
+
}
|
|
224
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
225
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
226
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
227
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
228
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function readNumber(raw, fallback, bounds) {
|
|
233
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
var NumberValueReader = class {
|
|
237
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
238
|
+
read(owner, name, raw, fallback, bounds) {
|
|
239
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
240
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
241
|
+
this.#lastRejected.delete(name);
|
|
242
|
+
return resolved;
|
|
243
|
+
}
|
|
244
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
245
|
+
const literal = owner.element.getAttribute(attribute);
|
|
246
|
+
if (literal === null) {
|
|
247
|
+
this.#lastRejected.delete(name);
|
|
248
|
+
return resolved;
|
|
249
|
+
}
|
|
250
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
251
|
+
this.#lastRejected.set(name, literal);
|
|
252
|
+
console.warn(
|
|
253
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
return resolved;
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
|
|
137
260
|
function targetSelector(identifier, name) {
|
|
138
261
|
return `[data-${identifier}-target~="${name}"]`;
|
|
139
262
|
}
|
|
140
263
|
|
|
141
|
-
// src/controllers/calendar_controller.ts
|
|
142
264
|
var OWNED_DISABLED = "owns-disabled";
|
|
143
|
-
var CalendarController = class extends Controller {
|
|
144
|
-
|
|
265
|
+
var CalendarController = class _CalendarController extends Controller {
|
|
266
|
+
#numbers = new NumberValueReader();
|
|
267
|
+
#fieldWrites = /* @__PURE__ */ new WeakMap();
|
|
145
268
|
get #ownedDisabled() {
|
|
146
269
|
return `data-${this.identifier}-${OWNED_DISABLED}`;
|
|
147
270
|
}
|
|
@@ -152,82 +275,31 @@ var CalendarController = class extends Controller {
|
|
|
152
275
|
min: { type: String, default: "" },
|
|
153
276
|
max: { type: String, default: "" },
|
|
154
277
|
weekStart: { type: Number, default: 0 },
|
|
155
|
-
// 0 = Sunday, 1 = Monday, etc.
|
|
156
278
|
locale: { type: String, default: "" }
|
|
157
279
|
};
|
|
158
|
-
static
|
|
280
|
+
static valueConstraints = {
|
|
281
|
+
weekStart: { finite: true, integer: true, min: 0, max: 6 }
|
|
282
|
+
};
|
|
283
|
+
static actions = ["next", "onKeydown", "prev", "select"];
|
|
159
284
|
static events = ["monthchange", "reconcile", "select"];
|
|
160
|
-
/** The date currently receiving focus in the grid (local time). */
|
|
161
285
|
focusedDate = /* @__PURE__ */ new Date();
|
|
162
|
-
/**
|
|
163
|
-
* Whether `connect()` has run and `disconnect()` has not since. The paints
|
|
164
|
-
* before and during `connect()` describe the grid, so they report nothing.
|
|
165
|
-
*/
|
|
166
286
|
#connected = false;
|
|
167
|
-
/**
|
|
168
|
-
* The selection the last paint published — a `YYYY-MM-DD` day, or `""` — which
|
|
169
|
-
* the next move of the selection is measured from. A pick records its own day
|
|
170
|
-
* here before painting it.
|
|
171
|
-
*/
|
|
172
287
|
#published = "";
|
|
173
|
-
/**
|
|
174
|
-
* How many times a pick or a change the page made has moved the selection, so
|
|
175
|
-
* a report still pending for a selection a listener has since replaced can tell.
|
|
176
|
-
*/
|
|
177
288
|
#selectionMoves = 0;
|
|
178
|
-
/**
|
|
179
|
-
* The month the last paint showed (`YYYY-MM`), or `""` before the first paint.
|
|
180
|
-
* Only a connected grid reports a paint that moves it: settling on the initial
|
|
181
|
-
* month — before `connect()` or in it, from the attribute or derived here — is
|
|
182
|
-
* the grid describing itself, not a navigation, so it must not reach a
|
|
183
|
-
* listener that refetches inventory or pushes history.
|
|
184
|
-
*
|
|
185
|
-
* While `month` is empty this is also the month the next paint shows, so the
|
|
186
|
-
* grid stays where it is; the first `connect()` of this instance sets it to the
|
|
187
|
-
* month the grid opens on.
|
|
188
|
-
*/
|
|
189
289
|
#paintedMonth = "";
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
* the month a grid with no `month` opens on. A later one keeps the month on
|
|
193
|
-
* screen, and so do the paints Stimulus runs before it.
|
|
194
|
-
*/
|
|
290
|
+
#monthMoves = 0;
|
|
291
|
+
#paintedDates = /* @__PURE__ */ new WeakMap();
|
|
195
292
|
#opened = false;
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* The `month` and `selected` Values are shared with the page — application
|
|
199
|
-
* code and a Turbo morph write them too — so the field mirrors take their
|
|
200
|
-
* "did the user commit this" answer from the route, not from the Value.
|
|
201
|
-
*/
|
|
202
|
-
#movedByUser = false;
|
|
203
|
-
/**
|
|
204
|
-
* Repaints the month on screen after `min`, `max` or `weekStart` changed.
|
|
205
|
-
*
|
|
206
|
-
* A morph that swaps several of them repaints once, and the delivery Stimulus
|
|
207
|
-
* makes before `connect()` is refused, because the grid is painted on
|
|
208
|
-
* connect. None of these Values decides the month on screen; `min` and `max`
|
|
209
|
-
* decide whether the requested day is published.
|
|
210
|
-
*/
|
|
211
|
-
#repaint = new MicrotaskCoalescer(() => {
|
|
293
|
+
#ownMove = false;
|
|
294
|
+
#repaint = new MorphRenderWatcher(() => {
|
|
212
295
|
this.#repaintKeepingFocus();
|
|
213
296
|
});
|
|
214
|
-
/**
|
|
215
|
-
* The cell DOM focus was on, its day and the month on screen, recorded by the
|
|
216
|
-
* first repaint of a batch for the pass that settles focus after it.
|
|
217
|
-
*/
|
|
218
297
|
#focusOrigin = null;
|
|
219
|
-
/**
|
|
220
|
-
* Settles DOM focus once, after the last repaint of a batch. The callbacks of
|
|
221
|
-
* one mutation repaint one by one and each reads every Value as it stands, so
|
|
222
|
-
* a `selected` or `locale` callback can paint a month before that month's own
|
|
223
|
-
* callback has moved the tab stop into it. Outside a connection no pass is
|
|
224
|
-
* scheduled, so a grid that is not connected moves no focus.
|
|
225
|
-
*/
|
|
226
298
|
#settleFocus = new MicrotaskCoalescer(() => {
|
|
227
299
|
this.#restoreFocus();
|
|
228
300
|
});
|
|
229
301
|
connect() {
|
|
230
|
-
this.#repaint.
|
|
302
|
+
this.#repaint.observe(this.element);
|
|
231
303
|
this.#settleFocus.activate();
|
|
232
304
|
this.#focusOrigin = null;
|
|
233
305
|
if (!this.monthValue && !this.#opened) {
|
|
@@ -238,85 +310,55 @@ var CalendarController = class extends Controller {
|
|
|
238
310
|
this.#render();
|
|
239
311
|
this.#connected = true;
|
|
240
312
|
}
|
|
241
|
-
/**
|
|
242
|
-
* Drops a pending repaint and a pending focus move, and stops reporting, so
|
|
243
|
-
* nothing reaches for a grid that is not connected.
|
|
244
|
-
*/
|
|
245
313
|
disconnect() {
|
|
246
314
|
this.#connected = false;
|
|
247
|
-
this.#repaint.
|
|
315
|
+
this.#repaint.disconnect();
|
|
248
316
|
this.#settleFocus.cancel();
|
|
249
317
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Paints the month `month` names, whether application code, a morph or the
|
|
252
|
-
* grid's own navigation wrote it; focus in the grid follows to the tab stop.
|
|
253
|
-
*/
|
|
254
318
|
monthValueChanged() {
|
|
255
319
|
if (!this.monthValue) return;
|
|
256
320
|
this.#syncFocusedDateWithMonth();
|
|
257
321
|
this.#repaintKeepingFocus();
|
|
258
322
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Repaints the label when application code changes `locale` at runtime.
|
|
261
|
-
*
|
|
262
|
-
* Stimulus runs this for the initial Value too, before `connect()`. The paint
|
|
263
|
-
* it triggers is idempotent — the month comes from `month` (or, while that is
|
|
264
|
-
* still empty, from `focusedDate`) — and a grid that has not connected
|
|
265
|
-
* reports nothing, so that paint never reports a month.
|
|
266
|
-
*/
|
|
267
323
|
localeValueChanged() {
|
|
268
324
|
this.#repaintKeepingFocus();
|
|
269
325
|
}
|
|
270
|
-
/**
|
|
271
|
-
* Repaints `aria-selected` and the field when the requested day changes, and
|
|
272
|
-
* moves the tab stop to the day the grid now publishes. A request `min` /
|
|
273
|
-
* `max` withhold is not published, so the tab stop stays where it was.
|
|
274
|
-
*/
|
|
275
326
|
selectedValueChanged() {
|
|
276
327
|
const selection = parseISODateString(this.#selection);
|
|
277
328
|
if (selection) this.focusedDate = selection;
|
|
278
329
|
this.#repaintKeepingFocus();
|
|
279
330
|
}
|
|
280
|
-
/** Repaints when `min` changes after connect, so the disabled days and the selection follow. */
|
|
281
331
|
minValueChanged() {
|
|
282
332
|
this.#repaint.schedule();
|
|
283
333
|
}
|
|
284
|
-
/** Repaints when `max` changes after connect, so the disabled days and the selection follow. */
|
|
285
334
|
maxValueChanged() {
|
|
286
335
|
this.#repaint.schedule();
|
|
287
336
|
}
|
|
288
|
-
/**
|
|
289
|
-
* Lays the grid out again when `weekStart` changes after connect, so every
|
|
290
|
-
* date moves to the column of its weekday.
|
|
291
|
-
*
|
|
292
|
-
* Only the day cells are painted: column headers are author markup, so the
|
|
293
|
-
* author updates them together with the Value.
|
|
294
|
-
*/
|
|
295
337
|
weekStartValueChanged() {
|
|
296
338
|
this.#repaint.schedule();
|
|
297
339
|
}
|
|
298
|
-
/** Navigates to the previous month. */
|
|
299
340
|
prev(event) {
|
|
300
341
|
if (event) event.preventDefault();
|
|
301
342
|
this.#shiftMonth(-1);
|
|
302
343
|
}
|
|
303
|
-
/** Navigates to the next month. */
|
|
304
344
|
next(event) {
|
|
305
345
|
if (event) event.preventDefault();
|
|
306
346
|
this.#shiftMonth(1);
|
|
307
347
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
);
|
|
313
|
-
if (!
|
|
314
|
-
this
|
|
348
|
+
select(source) {
|
|
349
|
+
const { origin, reason } = actionSource(source);
|
|
350
|
+
if (origin && origin.closest(`[data-controller~="${this.identifier}"]`) !== this.element)
|
|
351
|
+
return;
|
|
352
|
+
const day = origin?.closest(targetSelector(this.identifier, "day"));
|
|
353
|
+
if (!day || !this.dayTargets.includes(day)) return;
|
|
354
|
+
this.#selectDay(day, reason);
|
|
315
355
|
}
|
|
316
|
-
/** Handles grid cell keyboard navigation and triggers selection. */
|
|
317
356
|
onKeydown(event) {
|
|
318
357
|
if (event.defaultPrevented) return;
|
|
319
358
|
if (isReservedArrowChord(event)) return;
|
|
359
|
+
if ((event.key === "Home" || event.key === "End") && hasModifierChord(event)) return;
|
|
360
|
+
if ((event.key === "PageUp" || event.key === "PageDown") && (event.ctrlKey || event.altKey || event.metaKey))
|
|
361
|
+
return;
|
|
320
362
|
const dayElement = event.target?.closest(
|
|
321
363
|
targetSelector(this.identifier, "day")
|
|
322
364
|
);
|
|
@@ -325,7 +367,8 @@ var CalendarController = class extends Controller {
|
|
|
325
367
|
if (!date) return;
|
|
326
368
|
let handled = true;
|
|
327
369
|
let nextDate = new Date(date);
|
|
328
|
-
|
|
370
|
+
const directionElement = this.hasGridTarget ? gridDirectionElement(this.gridTarget, this.element) : this.element;
|
|
371
|
+
switch (logicalArrowKey(event.key, directionElement)) {
|
|
329
372
|
case "ArrowLeft":
|
|
330
373
|
nextDate.setDate(nextDate.getDate() - 1);
|
|
331
374
|
break;
|
|
@@ -340,16 +383,16 @@ var CalendarController = class extends Controller {
|
|
|
340
383
|
break;
|
|
341
384
|
case "PageUp":
|
|
342
385
|
if (event.shiftKey) {
|
|
343
|
-
nextDate =
|
|
386
|
+
nextDate = shiftYearClamped(date, -1);
|
|
344
387
|
} else {
|
|
345
|
-
nextDate =
|
|
388
|
+
nextDate = shiftMonthClamped(date, -1);
|
|
346
389
|
}
|
|
347
390
|
break;
|
|
348
391
|
case "PageDown":
|
|
349
392
|
if (event.shiftKey) {
|
|
350
|
-
nextDate =
|
|
393
|
+
nextDate = shiftYearClamped(date, 1);
|
|
351
394
|
} else {
|
|
352
|
-
nextDate =
|
|
395
|
+
nextDate = shiftMonthClamped(date, 1);
|
|
353
396
|
}
|
|
354
397
|
break;
|
|
355
398
|
case "Home":
|
|
@@ -371,7 +414,7 @@ var CalendarController = class extends Controller {
|
|
|
371
414
|
case "Enter":
|
|
372
415
|
case " ":
|
|
373
416
|
event.preventDefault();
|
|
374
|
-
this
|
|
417
|
+
this.#selectDay(dayElement, stateReasonFor(event));
|
|
375
418
|
return;
|
|
376
419
|
default:
|
|
377
420
|
handled = false;
|
|
@@ -382,43 +425,13 @@ var CalendarController = class extends Controller {
|
|
|
382
425
|
this.#focusAndNavigateToDate(nextDate);
|
|
383
426
|
}
|
|
384
427
|
}
|
|
385
|
-
/**
|
|
386
|
-
* First day of the month the grid paints: the `month` Value when it parses —
|
|
387
|
-
* while it is empty, the month on screen — otherwise the focused date's month.
|
|
388
|
-
*
|
|
389
|
-
* A malformed `month` falls back instead of stopping the paint: every cell
|
|
390
|
-
* keeps its `aria-selected` and the grid keeps its tab stop, so a Value typo
|
|
391
|
-
* still leaves the grid reachable by Tab and the author can see what they
|
|
392
|
-
* typed. Every consumer of "which month is on screen" reads it from here, so
|
|
393
|
-
* the paint and the `monthchange` report cannot name different months.
|
|
394
|
-
*/
|
|
395
428
|
#paintedMonthStart() {
|
|
396
429
|
const monthInfo = parseISOMonthString(this.#monthRequest);
|
|
397
430
|
return monthInfo ? new Date(monthInfo.year, monthInfo.month - 1, 1) : new Date(this.focusedDate.getFullYear(), this.focusedDate.getMonth(), 1);
|
|
398
431
|
}
|
|
399
|
-
/**
|
|
400
|
-
* The month the grid is asked to paint: `month`, or while that is empty the
|
|
401
|
-
* month on screen, which `connect()` opens on the month of the published
|
|
402
|
-
* selection, else on the current one.
|
|
403
|
-
*/
|
|
404
432
|
get #monthRequest() {
|
|
405
433
|
return this.monthValue || this.#paintedMonth;
|
|
406
434
|
}
|
|
407
|
-
/**
|
|
408
|
-
* Renders the grid days and updates the month/year label, mirrors both
|
|
409
|
-
* fields, then reports each field the user's move moved and — once connected
|
|
410
|
-
* — a painted month that differs from the last one painted, and a published
|
|
411
|
-
* selection that a change the page made moved.
|
|
412
|
-
*
|
|
413
|
-
* The report belongs to the paint, not to the `month` Value: the Value is only
|
|
414
|
-
* one of the things that decide the month on screen. While a malformed `month`
|
|
415
|
-
* falls back, changing `selected` and selecting a neighbouring month's cell
|
|
416
|
-
* move the painted month too, and both repaint from here. Reading the report
|
|
417
|
-
* off the paint keeps the event naming the month the grid shows, in the
|
|
418
|
-
* `YYYY-MM` the detail contract promises, whichever route repainted.
|
|
419
|
-
*
|
|
420
|
-
* @stimeoRenderRoot
|
|
421
|
-
*/
|
|
422
435
|
#render() {
|
|
423
436
|
const monthStart = this.#paintedMonthStart();
|
|
424
437
|
const year = monthStart.getFullYear();
|
|
@@ -428,7 +441,7 @@ var CalendarController = class extends Controller {
|
|
|
428
441
|
const locale = resolveLocale(this.element, this.localeValue);
|
|
429
442
|
this.labelTarget.textContent = monthLabelFormatter(locale).format(monthStart);
|
|
430
443
|
}
|
|
431
|
-
const days =
|
|
444
|
+
const days = monthGridDays(year, month, this.#safeWeekStart);
|
|
432
445
|
const dayElements = this.dayTargets;
|
|
433
446
|
const consumerDisabled = /* @__PURE__ */ new Map();
|
|
434
447
|
for (const el of dayElements) {
|
|
@@ -447,6 +460,7 @@ var CalendarController = class extends Controller {
|
|
|
447
460
|
const kept = consumerDisabled.get(dateStr);
|
|
448
461
|
if (kept !== void 0) el.setAttribute("aria-disabled", kept);
|
|
449
462
|
}
|
|
463
|
+
this.#paintedDates.set(el, dateStr);
|
|
450
464
|
el.setAttribute("data-date", dateStr);
|
|
451
465
|
el.textContent = String(date.getDate());
|
|
452
466
|
const isOutside = date.getFullYear() !== year || date.getMonth() !== month - 1;
|
|
@@ -472,25 +486,20 @@ var CalendarController = class extends Controller {
|
|
|
472
486
|
const painted = toISOMonthString(monthStart);
|
|
473
487
|
const previous = this.#paintedMonth;
|
|
474
488
|
this.#paintedMonth = painted;
|
|
489
|
+
if (previous !== painted) this.#monthMoves += 1;
|
|
490
|
+
const monthMove = this.#monthMoves;
|
|
475
491
|
const reconciled = this.#settleSelection(selection);
|
|
476
492
|
const move = this.#selectionMoves;
|
|
477
|
-
for (const [field, value] of this.#mirrorFields(painted, selection, reconciled)) {
|
|
478
|
-
if (field.value === value) commitField(field);
|
|
493
|
+
for (const [field, value, write] of this.#mirrorFields(painted, selection, reconciled)) {
|
|
494
|
+
if (field.value === value && this.#fieldWrites.get(field) === write) commitField(field);
|
|
479
495
|
}
|
|
480
|
-
if (this.#connected && previous !== painted &&
|
|
496
|
+
if (this.#connected && previous !== painted && monthMove === this.#monthMoves) {
|
|
481
497
|
this.dispatch("monthchange", { detail: { month: painted } });
|
|
482
498
|
}
|
|
483
499
|
if (reconciled && move === this.#selectionMoves) {
|
|
484
500
|
this.dispatch("reconcile", { detail: { date: selection } });
|
|
485
501
|
}
|
|
486
502
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Records the selection a paint publishes and says whether it moved from the
|
|
489
|
-
* one published before, which a connected grid reports as `reconcile`. A pick
|
|
490
|
-
* records its own day before its paint, so that paint finds no move, and
|
|
491
|
-
* nothing counts before the grid connects. A move counts as a move of the
|
|
492
|
-
* selection.
|
|
493
|
-
*/
|
|
494
503
|
#settleSelection(selection) {
|
|
495
504
|
const published = this.#published;
|
|
496
505
|
this.#published = selection;
|
|
@@ -498,18 +507,13 @@ var CalendarController = class extends Controller {
|
|
|
498
507
|
this.#selectionMoves += 1;
|
|
499
508
|
return true;
|
|
500
509
|
}
|
|
501
|
-
/**
|
|
502
|
-
* Repaints the grid for a Value callback or a pick. The first repaint of a
|
|
503
|
-
* batch records where DOM focus stood on a day, for the settle pass; focus
|
|
504
|
-
* outside the grid is left alone.
|
|
505
|
-
*/
|
|
506
510
|
#repaintKeepingFocus() {
|
|
507
511
|
if (!this.#focusOrigin) {
|
|
508
512
|
const cell = this.dayTargets.find((day) => day === document.activeElement);
|
|
509
513
|
if (cell) {
|
|
510
514
|
this.#focusOrigin = {
|
|
511
515
|
cell,
|
|
512
|
-
date: cell.getAttribute("data-date"),
|
|
516
|
+
date: cell.getAttribute("data-date") ?? this.#paintedDates.get(cell) ?? null,
|
|
513
517
|
month: this.#paintedMonth
|
|
514
518
|
};
|
|
515
519
|
this.#settleFocus.schedule();
|
|
@@ -517,18 +521,6 @@ var CalendarController = class extends Controller {
|
|
|
517
521
|
}
|
|
518
522
|
this.#render();
|
|
519
523
|
}
|
|
520
|
-
/**
|
|
521
|
-
* Puts DOM focus back on a day it can stay on once a batch of repaints has
|
|
522
|
-
* landed.
|
|
523
|
-
*
|
|
524
|
-
* While the painted month stays, a bound only toggles `aria-disabled` and a
|
|
525
|
-
* disabled day stays focusable, so nothing is called (`focus()` would scroll
|
|
526
|
-
* the day into view); `weekStart` moves every date to another cell, so focus
|
|
527
|
-
* follows its date, or goes to the tab stop once the date has left the grid.
|
|
528
|
-
* When the painted month moves, every cell shows another day, and focus goes
|
|
529
|
-
* to the tab stop of the new month. Focus a listener of the paint's events
|
|
530
|
-
* moved off the recorded cell stays where it went.
|
|
531
|
-
*/
|
|
532
524
|
#restoreFocus() {
|
|
533
525
|
const origin = this.#focusOrigin;
|
|
534
526
|
this.#focusOrigin = null;
|
|
@@ -538,57 +530,68 @@ var CalendarController = class extends Controller {
|
|
|
538
530
|
const destination = (moved ? void 0 : this.dayTargets.find((cell) => cell.getAttribute("data-date") === origin.date)) ?? this.dayTargets.find((cell) => cell.getAttribute("tabindex") === "0");
|
|
539
531
|
destination?.focus();
|
|
540
532
|
}
|
|
541
|
-
|
|
533
|
+
labelTargetConnected() {
|
|
534
|
+
this.#repaint.schedule();
|
|
535
|
+
}
|
|
536
|
+
labelTargetDisconnected() {
|
|
537
|
+
this.#repaint.schedule();
|
|
538
|
+
}
|
|
542
539
|
fieldTargetConnected(field) {
|
|
543
|
-
writeField(field, this.#selection);
|
|
540
|
+
this.#writeField(field, this.#selection);
|
|
541
|
+
}
|
|
542
|
+
fieldTargetDisconnected() {
|
|
543
|
+
this.#repaint.schedule();
|
|
544
544
|
}
|
|
545
|
-
/** Fills a painted-month field inserted or replaced at runtime. */
|
|
546
545
|
monthFieldTargetConnected(field) {
|
|
547
|
-
writeField(field, toISOMonthString(this.#paintedMonthStart()));
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
*/
|
|
546
|
+
this.#writeField(field, toISOMonthString(this.#paintedMonthStart()));
|
|
547
|
+
}
|
|
548
|
+
monthFieldTargetDisconnected() {
|
|
549
|
+
this.#repaint.schedule();
|
|
550
|
+
}
|
|
551
|
+
#writeField(field, value) {
|
|
552
|
+
if (!writeField(field, value)) return false;
|
|
553
|
+
this.#fieldWrites.set(field, (this.#fieldWrites.get(field) ?? 0) + 1);
|
|
554
|
+
return true;
|
|
555
|
+
}
|
|
558
556
|
#mirrorFields(painted, selection, reconciled) {
|
|
559
|
-
const
|
|
560
|
-
this.#
|
|
557
|
+
const ownMove = this.#ownMove;
|
|
558
|
+
this.#ownMove = false;
|
|
561
559
|
const moved = [];
|
|
562
|
-
if (this.hasFieldTarget && writeField(this.fieldTarget, selection) && !reconciled) {
|
|
563
|
-
moved.push([this.fieldTarget, selection]);
|
|
560
|
+
if (this.hasFieldTarget && this.#writeField(this.fieldTarget, selection) && !reconciled) {
|
|
561
|
+
moved.push([this.fieldTarget, selection, this.#fieldWrites.get(this.fieldTarget) ?? 0]);
|
|
564
562
|
}
|
|
565
|
-
if (this.hasMonthFieldTarget && writeField(this.monthFieldTarget, painted)) {
|
|
566
|
-
moved.push([
|
|
563
|
+
if (this.hasMonthFieldTarget && this.#writeField(this.monthFieldTarget, painted)) {
|
|
564
|
+
moved.push([
|
|
565
|
+
this.monthFieldTarget,
|
|
566
|
+
painted,
|
|
567
|
+
this.#fieldWrites.get(this.monthFieldTarget) ?? 0
|
|
568
|
+
]);
|
|
567
569
|
}
|
|
568
|
-
return
|
|
570
|
+
return ownMove ? moved : [];
|
|
569
571
|
}
|
|
570
|
-
|
|
572
|
+
#selectDay(dayElement, reason) {
|
|
571
573
|
if (dayElement.getAttribute("aria-disabled") === "true") return;
|
|
572
574
|
const dateStr = dayElement.getAttribute("data-date");
|
|
573
575
|
if (!dateStr || this.#isDateOutOfBounds(dateStr)) return;
|
|
574
576
|
this.selectedValue = dateStr;
|
|
575
577
|
const selected = parseISODateString(dateStr);
|
|
576
578
|
if (selected) this.focusedDate = selected;
|
|
577
|
-
this.#
|
|
578
|
-
this.#
|
|
579
|
+
this.#ownMove = true;
|
|
580
|
+
const changed = dateStr !== this.#published;
|
|
581
|
+
if (changed) this.#selectionMoves += 1;
|
|
579
582
|
const move = this.#selectionMoves;
|
|
580
583
|
this.#published = dateStr;
|
|
581
584
|
this.#repaintKeepingFocus();
|
|
582
|
-
if (move !== this.#selectionMoves) return;
|
|
583
|
-
this.dispatch("select", { detail: { date: dateStr } });
|
|
585
|
+
if (!changed || move !== this.#selectionMoves) return;
|
|
586
|
+
this.dispatch("select", { detail: { date: dateStr, reason } });
|
|
584
587
|
}
|
|
585
588
|
#focusAndNavigateToDate(date) {
|
|
586
589
|
this.focusedDate = date;
|
|
587
590
|
const targetMonthStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
|
|
588
|
-
this.#
|
|
591
|
+
this.#ownMove = true;
|
|
589
592
|
if (targetMonthStr !== this.#monthRequest) {
|
|
590
593
|
this.monthValue = targetMonthStr;
|
|
591
|
-
return;
|
|
594
|
+
if (targetMonthStr !== this.#paintedMonth) return;
|
|
592
595
|
}
|
|
593
596
|
this.#render();
|
|
594
597
|
const dateStr = toISODateString(date);
|
|
@@ -599,50 +602,29 @@ var CalendarController = class extends Controller {
|
|
|
599
602
|
if (this.maxValue && dateStr > this.maxValue) return true;
|
|
600
603
|
return false;
|
|
601
604
|
}
|
|
602
|
-
/** The day `selected` requests: the Value when it names a real day, else `""`. */
|
|
603
605
|
get #requestedDay() {
|
|
604
606
|
return parseISODateString(this.selectedValue) ? this.selectedValue : "";
|
|
605
607
|
}
|
|
606
|
-
/** The selection published for `requested`: the day while `min` / `max` allow it, else `""`. */
|
|
607
608
|
#publishable(requested) {
|
|
608
609
|
return requested && !this.#isDateOutOfBounds(requested) ? requested : "";
|
|
609
610
|
}
|
|
610
|
-
/** The selection the grid publishes now. */
|
|
611
611
|
get #selection() {
|
|
612
612
|
return this.#publishable(this.#requestedDay);
|
|
613
613
|
}
|
|
614
614
|
#shiftMonth(delta) {
|
|
615
615
|
const monthInfo = parseISOMonthString(this.#monthRequest);
|
|
616
616
|
if (!monthInfo) return;
|
|
617
|
-
this.#
|
|
617
|
+
this.#ownMove = true;
|
|
618
618
|
const nextMonthDate = new Date(monthInfo.year, monthInfo.month - 1 + delta, 1);
|
|
619
619
|
this.monthValue = `${nextMonthDate.getFullYear()}-${String(nextMonthDate.getMonth() + 1).padStart(2, "0")}`;
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
targetDate.getFullYear(),
|
|
625
|
-
targetDate.getMonth() + 1,
|
|
626
|
-
0
|
|
627
|
-
).getDate();
|
|
628
|
-
const clampedDay = Math.min(baseDate.getDate(), lastDayInTarget);
|
|
629
|
-
targetDate.setDate(clampedDay);
|
|
630
|
-
return targetDate;
|
|
631
|
-
}
|
|
632
|
-
#calculateShiftedYearDate(baseDate, delta) {
|
|
633
|
-
const targetDate = new Date(baseDate.getFullYear() + delta, baseDate.getMonth(), 1);
|
|
634
|
-
const lastDayInTarget = new Date(
|
|
635
|
-
targetDate.getFullYear(),
|
|
636
|
-
targetDate.getMonth() + 1,
|
|
637
|
-
0
|
|
638
|
-
).getDate();
|
|
639
|
-
const clampedDay = Math.min(baseDate.getDate(), lastDayInTarget);
|
|
640
|
-
targetDate.setDate(clampedDay);
|
|
641
|
-
return targetDate;
|
|
620
|
+
if (this.monthValue === this.#paintedMonth) {
|
|
621
|
+
this.#syncFocusedDateWithMonth();
|
|
622
|
+
this.#repaintKeepingFocus();
|
|
623
|
+
}
|
|
642
624
|
}
|
|
643
625
|
#getStartOfWeekDate(date) {
|
|
644
626
|
const currentDay = date.getDay();
|
|
645
|
-
const shift = (currentDay - this
|
|
627
|
+
const shift = (currentDay - this.#safeWeekStart + 7) % 7;
|
|
646
628
|
const target = new Date(date);
|
|
647
629
|
target.setDate(date.getDate() - shift);
|
|
648
630
|
return target;
|
|
@@ -653,11 +635,6 @@ var CalendarController = class extends Controller {
|
|
|
653
635
|
target.setDate(start.getDate() + 6);
|
|
654
636
|
return target;
|
|
655
637
|
}
|
|
656
|
-
/**
|
|
657
|
-
* Opens the tab stop on the published selection, else on today when the shown
|
|
658
|
-
* month holds it, else on the 1st of the shown month. A selection the grid
|
|
659
|
-
* does not show falls back to the 1st in the paint.
|
|
660
|
-
*/
|
|
661
638
|
#initializeFocusedDate() {
|
|
662
639
|
const selection = parseISODateString(this.#selection);
|
|
663
640
|
if (selection) {
|
|
@@ -682,29 +659,20 @@ var CalendarController = class extends Controller {
|
|
|
682
659
|
if (today.getFullYear() === monthInfo.year && today.getMonth() === monthInfo.month - 1) {
|
|
683
660
|
this.focusedDate = today;
|
|
684
661
|
} else {
|
|
685
|
-
|
|
686
|
-
const lastDayInTarget = new Date(monthInfo.year, monthInfo.month, 0).getDate();
|
|
687
|
-
const clampedDay = Math.min(this.focusedDate.getDate(), lastDayInTarget);
|
|
688
|
-
targetDate.setDate(clampedDay);
|
|
689
|
-
this.focusedDate = targetDate;
|
|
662
|
+
this.focusedDate = dateInMonthClamped(this.focusedDate, monthInfo.year, monthInfo.month);
|
|
690
663
|
}
|
|
691
664
|
}
|
|
692
665
|
}
|
|
693
|
-
#
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
days.push(new Date(current));
|
|
702
|
-
current.setDate(current.getDate() + 1);
|
|
703
|
-
}
|
|
704
|
-
return days;
|
|
666
|
+
get #safeWeekStart() {
|
|
667
|
+
return this.#numbers.read(
|
|
668
|
+
this,
|
|
669
|
+
"weekStart",
|
|
670
|
+
this.weekStartValue,
|
|
671
|
+
_CalendarController.values.weekStart.default,
|
|
672
|
+
_CalendarController.valueConstraints.weekStart
|
|
673
|
+
);
|
|
705
674
|
}
|
|
706
675
|
};
|
|
707
676
|
|
|
708
677
|
export { CalendarController };
|
|
709
|
-
//# sourceMappingURL=calendar_controller.js.map
|
|
710
678
|
//# sourceMappingURL=calendar_controller.js.map
|