stimeo-ui 0.15.0 → 0.17.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 +209 -0
- data/README.md +128 -1
- data/dist/cable/index.js +392 -417
- data/dist/controllers/accordion_controller.js +228 -28
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -191
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +295 -110
- data/dist/controllers/avatar_controller.js +172 -106
- data/dist/controllers/breadcrumb_controller.js +0 -129
- data/dist/controllers/bulk_select_controller.js +264 -53
- data/dist/controllers/calendar_controller.js +346 -180
- data/dist/controllers/carousel_controller.js +587 -300
- data/dist/controllers/character_counter_controller.js +271 -143
- data/dist/controllers/checkbox_controller.js +77 -75
- data/dist/controllers/clipboard_controller.js +187 -108
- data/dist/controllers/collapsible_controller.js +374 -108
- data/dist/controllers/color_picker_controller.js +183 -127
- data/dist/controllers/combobox_controller.js +426 -109
- data/dist/controllers/command_palette_controller.js +994 -350
- data/dist/controllers/conditional_fields_controller.js +212 -125
- data/dist/controllers/confirm_controller.js +939 -245
- data/dist/controllers/context_menu_controller.js +275 -127
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +235 -97
- data/dist/controllers/currency_input_controller.js +241 -118
- data/dist/controllers/data_grid_controller.js +229 -135
- data/dist/controllers/date_range_picker_controller.js +359 -165
- data/dist/controllers/dialog_controller.js +887 -225
- data/dist/controllers/direct_upload_controller.js +80 -142
- data/dist/controllers/dirty_form_controller.js +26 -47
- data/dist/controllers/dismissible_controller.js +175 -20
- data/dist/controllers/drawer_controller.js +963 -336
- data/dist/controllers/dropdown_controller.js +342 -85
- data/dist/controllers/editable_controller.js +182 -58
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +426 -229
- data/dist/controllers/filter_controller.js +78 -36
- data/dist/controllers/flash_controller.js +328 -211
- data/dist/controllers/focus_controller.js +654 -239
- data/dist/controllers/form_field_controller.js +153 -136
- data/dist/controllers/form_validation_controller.js +29 -96
- data/dist/controllers/frame_loading_controller.js +263 -198
- data/dist/controllers/highlight_controller.js +119 -70
- data/dist/controllers/hover_card_controller.js +335 -118
- data/dist/controllers/idle_controller.js +316 -65
- data/dist/controllers/input_mask_controller.js +116 -61
- data/dist/controllers/intersection_controller.js +147 -103
- data/dist/controllers/lazy_frame_controller.js +9 -56
- data/dist/controllers/listbox_controller.js +413 -153
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -95
- data/dist/controllers/menu_controller.js +254 -167
- data/dist/controllers/menubar_controller.js +206 -331
- data/dist/controllers/meter_controller.js +159 -71
- data/dist/controllers/multi_select_controller.js +797 -388
- data/dist/controllers/navigation_menu_controller.js +156 -231
- data/dist/controllers/nested_form_controller.js +373 -174
- data/dist/controllers/network_status_controller.js +241 -45
- data/dist/controllers/number_input_controller.js +387 -227
- data/dist/controllers/optimistic_controller.js +28 -75
- data/dist/controllers/otp_controller.js +344 -196
- data/dist/controllers/overflow_indicator_controller.js +167 -93
- data/dist/controllers/overflow_menu_controller.js +408 -277
- data/dist/controllers/pagination_controller.js +342 -120
- data/dist/controllers/password_reveal_controller.js +424 -99
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +252 -96
- data/dist/controllers/pointer_drag_controller.js +263 -164
- data/dist/controllers/popover_controller.js +267 -94
- data/dist/controllers/portal_controller.js +209 -72
- data/dist/controllers/preview_guard_controller.js +243 -112
- data/dist/controllers/progress_controller.js +132 -59
- data/dist/controllers/radio_group_controller.js +138 -133
- data/dist/controllers/range_slider_controller.js +245 -112
- data/dist/controllers/rating_controller.js +429 -194
- data/dist/controllers/read_more_controller.js +185 -47
- data/dist/controllers/reading_progress_controller.js +147 -135
- data/dist/controllers/relative_time_controller.js +145 -110
- data/dist/controllers/{reset_before_cache_controller.js → reset_on_restore_controller.js} +97 -38
- data/dist/controllers/resizable_controller.js +294 -118
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -221
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -146
- data/dist/controllers/scrollspy_controller.js +181 -236
- data/dist/controllers/separator_controller.js +288 -132
- data/dist/controllers/sidebar_controller.js +1071 -375
- data/dist/controllers/skeleton_controller.js +248 -92
- data/dist/controllers/slider_controller.js +211 -96
- data/dist/controllers/smart_sticky_header_controller.js +163 -54
- data/dist/controllers/sortable_controller.js +13 -110
- data/dist/controllers/spinner_controller.js +277 -161
- data/dist/controllers/step_indicator_controller.js +107 -53
- data/dist/controllers/stepper_controller.js +171 -48
- data/dist/controllers/stick_to_bottom_controller.js +310 -116
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -192
- data/dist/controllers/switch_controller.js +107 -43
- data/dist/controllers/tabs_controller.js +50 -25
- data/dist/controllers/tags_input_controller.js +290 -149
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +148 -80
- data/dist/controllers/time_picker_controller.js +162 -81
- data/dist/controllers/toast_controller.js +365 -193
- data/dist/controllers/toggle_group_controller.js +194 -99
- data/dist/controllers/toolbar_controller.js +92 -90
- data/dist/controllers/tooltip_controller.js +185 -125
- data/dist/controllers/transition_controller.js +123 -115
- data/dist/controllers/tree_view_controller.js +248 -290
- data/dist/index.js +8649 -8572
- data/dist/positioning/index.js +148 -45
- data/lib/stimeo/ui/version.rb +1 -1
- metadata +3 -3
|
@@ -1,40 +1,42 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/resizable_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/arrow_step.ts
|
|
6
3
|
function isReservedArrowChord(event, allow = []) {
|
|
7
4
|
if (!event.key.startsWith("Arrow")) return false;
|
|
8
5
|
return event.altKey && !allow.includes("alt") || event.ctrlKey && !allow.includes("ctrl") || event.metaKey && !allow.includes("meta") || event.shiftKey && !allow.includes("shift");
|
|
9
6
|
}
|
|
10
7
|
|
|
11
|
-
// src/utils/event_owner.ts
|
|
12
8
|
function ownerIndex(candidates, node) {
|
|
13
9
|
if (!(node instanceof Node)) return -1;
|
|
14
10
|
return candidates.findIndex((candidate) => candidate.contains(node));
|
|
15
11
|
}
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
var ListenerSet = class {
|
|
14
|
+
#abort = new AbortController();
|
|
15
|
+
add(target, type, handler, options) {
|
|
16
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
17
|
+
}
|
|
18
|
+
dispose() {
|
|
19
|
+
this.#abort.abort();
|
|
20
|
+
this.#abort = new AbortController();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
18
24
|
var MicrotaskCoalescer = class {
|
|
19
25
|
#run;
|
|
20
26
|
#queued = false;
|
|
21
27
|
#active = false;
|
|
22
28
|
#generation = 0;
|
|
23
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
24
29
|
constructor(run) {
|
|
25
30
|
this.#run = run;
|
|
26
31
|
}
|
|
27
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
28
32
|
activate() {
|
|
29
33
|
this.#active = true;
|
|
30
34
|
}
|
|
31
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
32
35
|
cancel() {
|
|
33
36
|
this.#active = false;
|
|
34
37
|
this.#queued = false;
|
|
35
38
|
this.#generation += 1;
|
|
36
39
|
}
|
|
37
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
38
40
|
schedule() {
|
|
39
41
|
if (!this.#active || this.#queued) return;
|
|
40
42
|
this.#queued = true;
|
|
@@ -47,72 +49,197 @@ var MicrotaskCoalescer = class {
|
|
|
47
49
|
}
|
|
48
50
|
};
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
};
|
|
58
|
-
#rewind;
|
|
59
|
-
/** @param rewind - the pass that returns this controller's state to its initial form. */
|
|
60
|
-
constructor(rewind) {
|
|
61
|
-
this.#rewind = rewind;
|
|
52
|
+
var MorphRenderWatcher = class {
|
|
53
|
+
#listeners = new ListenerSet();
|
|
54
|
+
#pass;
|
|
55
|
+
#element = null;
|
|
56
|
+
#onMorph = () => this.#pass.schedule();
|
|
57
|
+
constructor(run) {
|
|
58
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
62
59
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
60
|
+
observe(element) {
|
|
61
|
+
if (this.#element === element) return;
|
|
62
|
+
this.disconnect();
|
|
63
|
+
this.#element = element;
|
|
64
|
+
this.#pass.activate();
|
|
65
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
70
66
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
67
|
+
schedule() {
|
|
68
|
+
this.#pass.schedule();
|
|
69
|
+
}
|
|
70
|
+
disconnect() {
|
|
71
|
+
this.#listeners.dispose();
|
|
72
|
+
this.#pass.cancel();
|
|
73
|
+
this.#element = null;
|
|
76
74
|
}
|
|
77
75
|
};
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
var NUMBER_BOUNDS = {
|
|
78
|
+
finite: { finite: true },
|
|
79
|
+
positive: { finite: true, exclusiveMin: 0 }};
|
|
80
|
+
function matchesNumberBounds(value, bounds) {
|
|
81
|
+
if (!Number.isFinite(value)) {
|
|
82
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
83
|
+
if (direction === null) return false;
|
|
84
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
85
|
+
}
|
|
86
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
87
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
88
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
89
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
90
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function readNumber(raw, fallback, bounds) {
|
|
95
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var NumberValueReader = class {
|
|
99
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
100
|
+
read(owner, name, raw, fallback, bounds) {
|
|
101
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
102
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
103
|
+
this.#lastRejected.delete(name);
|
|
104
|
+
return resolved;
|
|
105
|
+
}
|
|
106
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
107
|
+
const literal = owner.element.getAttribute(attribute);
|
|
108
|
+
if (literal === null) {
|
|
109
|
+
this.#lastRejected.delete(name);
|
|
110
|
+
return resolved;
|
|
111
|
+
}
|
|
112
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
113
|
+
this.#lastRejected.set(name, literal);
|
|
114
|
+
console.warn(
|
|
115
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return resolved;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
function queryOpenDescendants(root, selector) {
|
|
123
|
+
const matches = [];
|
|
124
|
+
const roots = [root];
|
|
125
|
+
for (const scope of roots) {
|
|
126
|
+
matches.push(...scope.querySelectorAll(selector));
|
|
127
|
+
if (scope.nodeType === Node.ELEMENT_NODE) {
|
|
128
|
+
const shadow = scope.shadowRoot;
|
|
129
|
+
if (shadow) roots.push(shadow);
|
|
130
|
+
}
|
|
131
|
+
for (const element of scope.querySelectorAll("*")) {
|
|
132
|
+
if (element.shadowRoot) roots.push(element.shadowRoot);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return matches;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function sharedRegistry(key, create) {
|
|
139
|
+
const symbol = Symbol.for(key);
|
|
140
|
+
const scope = globalThis;
|
|
141
|
+
const existing = scope[symbol];
|
|
142
|
+
if (existing !== void 0) return existing;
|
|
143
|
+
const registry = create();
|
|
144
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
145
|
+
return registry;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
var TabindexLoan = class _TabindexLoan {
|
|
149
|
+
static #registry = sharedRegistry(
|
|
150
|
+
"stimeo-ui.tabindex-loan.registry.v1",
|
|
151
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Set() })
|
|
152
|
+
);
|
|
81
153
|
#value;
|
|
154
|
+
#record;
|
|
82
155
|
#lent = /* @__PURE__ */ new Set();
|
|
83
|
-
|
|
84
|
-
#beforeCache = new BeforeCacheReset(() => this.returnAll());
|
|
85
|
-
/**
|
|
86
|
-
* @param value - the `tabindex` to lend. `"-1"` (the default) is
|
|
87
|
-
* programmatically focusable but not a Tab stop; `"0"` is a real Tab stop,
|
|
88
|
-
* which a scroll region with no focusable content of its own needs.
|
|
89
|
-
*/
|
|
90
|
-
constructor(value = "-1") {
|
|
156
|
+
constructor(value, owner) {
|
|
91
157
|
this.#value = value;
|
|
158
|
+
this.#record = `data-${owner}-tabindex-loan`;
|
|
159
|
+
_TabindexLoan.#registry.names.add(this.#record);
|
|
92
160
|
}
|
|
93
|
-
/** Lends `element` the value; no-ops when it already carries a `tabindex`. */
|
|
94
161
|
lend(element) {
|
|
95
|
-
|
|
96
|
-
element.
|
|
162
|
+
const stack = this.#stack(element);
|
|
163
|
+
const current = element.getAttribute("tabindex");
|
|
164
|
+
const held = stack.find((hold) => hold.loan === this) ?? stack.find(
|
|
165
|
+
(hold) => hold.loan === null && hold.record === this.#record && hold.value === this.#value
|
|
166
|
+
);
|
|
167
|
+
const linked = stack.at(-1)?.value === current;
|
|
168
|
+
if (!held && current !== null && !linked) return;
|
|
97
169
|
this.#lent.add(element);
|
|
98
|
-
|
|
170
|
+
if (held) held.loan = this;
|
|
171
|
+
else stack.push({ loan: this, record: this.#record, value: this.#value, linked });
|
|
172
|
+
this.#mark(element, stack);
|
|
173
|
+
if (current === null || !held && current !== this.#value) {
|
|
174
|
+
element.setAttribute("tabindex", this.#value);
|
|
175
|
+
}
|
|
99
176
|
}
|
|
100
|
-
/** Takes back every loan whose value is still the one that was lent. */
|
|
101
177
|
returnAll() {
|
|
102
178
|
for (const element of this.#lent) {
|
|
103
|
-
|
|
179
|
+
const stack = this.#stack(element);
|
|
180
|
+
const hold = stack.find((held) => held.loan === this);
|
|
181
|
+
if (hold) this.#release(element, stack, hold);
|
|
104
182
|
}
|
|
105
183
|
this.#lent.clear();
|
|
106
|
-
|
|
184
|
+
}
|
|
185
|
+
reclaim(element) {
|
|
186
|
+
if (element.getAttribute(this.#record) !== this.#value) return;
|
|
187
|
+
const stack = this.#stack(element);
|
|
188
|
+
const hold = stack.find((held) => held.loan === null && held.record === this.#record);
|
|
189
|
+
if (hold && !element.matches(":focus")) this.#release(element, stack, hold);
|
|
190
|
+
}
|
|
191
|
+
reclaimWithin(root) {
|
|
192
|
+
for (const element of queryOpenDescendants(root, `[${this.#record}]`)) {
|
|
193
|
+
this.reclaim(element);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
#stack(element) {
|
|
197
|
+
const stacks = _TabindexLoan.#registry.stacks;
|
|
198
|
+
const stack = stacks.get(element) ?? this.#seed(element);
|
|
199
|
+
stacks.set(element, stack);
|
|
200
|
+
return stack;
|
|
201
|
+
}
|
|
202
|
+
#seed(element) {
|
|
203
|
+
const current = element.getAttribute("tabindex");
|
|
204
|
+
const holds = [];
|
|
205
|
+
for (const record of _TabindexLoan.#registry.names) {
|
|
206
|
+
const value = element.getAttribute(record);
|
|
207
|
+
if (value !== null) holds.push({ loan: null, record, value, linked: true });
|
|
208
|
+
}
|
|
209
|
+
holds.sort((one, other) => Number(one.value === current) - Number(other.value === current));
|
|
210
|
+
for (const [index, hold] of holds.entries()) hold.linked = index > 0;
|
|
211
|
+
return holds;
|
|
212
|
+
}
|
|
213
|
+
#release(element, stack, hold) {
|
|
214
|
+
const current = element.getAttribute("tabindex");
|
|
215
|
+
const index = stack.indexOf(hold);
|
|
216
|
+
const owns = current === hold.value && !stack.slice(index + 1).some((above2) => above2.value === current);
|
|
217
|
+
stack.splice(index, 1);
|
|
218
|
+
const above = stack[index];
|
|
219
|
+
if (above) above.linked &&= hold.linked;
|
|
220
|
+
this.#mark(element, stack);
|
|
221
|
+
if (!owns) return;
|
|
222
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
223
|
+
if (!below) element.removeAttribute("tabindex");
|
|
224
|
+
else if (below.value !== current) element.setAttribute("tabindex", below.value);
|
|
225
|
+
}
|
|
226
|
+
#mark(element, stack) {
|
|
227
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
228
|
+
for (const hold of stack) records.set(hold.record, hold.value);
|
|
229
|
+
for (const [name, recorded] of records) {
|
|
230
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
231
|
+
if (recorded === null) element.removeAttribute(name);
|
|
232
|
+
else element.setAttribute(name, recorded);
|
|
233
|
+
}
|
|
107
234
|
}
|
|
108
235
|
};
|
|
109
236
|
|
|
110
|
-
// src/controllers/resizable_controller.ts
|
|
111
237
|
var DEFAULT_MIN = 0;
|
|
112
238
|
var DEFAULT_MAX = 100;
|
|
113
239
|
var DEFAULT_VALUE = 50;
|
|
114
240
|
var DEFAULT_STEP = 1;
|
|
115
|
-
var ResizableController = class extends Controller {
|
|
241
|
+
var ResizableController = class _ResizableController extends Controller {
|
|
242
|
+
#numbers = new NumberValueReader();
|
|
116
243
|
static targets = ["primary", "secondary", "separator"];
|
|
117
244
|
static values = {
|
|
118
245
|
min: { type: Number, default: DEFAULT_MIN },
|
|
@@ -120,38 +247,38 @@ var ResizableController = class extends Controller {
|
|
|
120
247
|
step: { type: Number, default: DEFAULT_STEP },
|
|
121
248
|
value: { type: Number, default: DEFAULT_VALUE }
|
|
122
249
|
};
|
|
250
|
+
static valueConstraints = {
|
|
251
|
+
min: NUMBER_BOUNDS.finite,
|
|
252
|
+
max: NUMBER_BOUNDS.finite,
|
|
253
|
+
step: NUMBER_BOUNDS.positive,
|
|
254
|
+
value: NUMBER_BOUNDS.finite
|
|
255
|
+
};
|
|
123
256
|
static actions = ["onKeydown", "onPointerDown", "toggle"];
|
|
124
|
-
static events = ["change"];
|
|
125
|
-
/** Where the divider sat before the current collapse; `null` when unknown. */
|
|
257
|
+
static events = ["change", "reconcile"];
|
|
126
258
|
#valueBeforeCollapse = null;
|
|
127
|
-
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
#paneTabindex = new TabindexLoan();
|
|
131
|
-
/** Releases the pane cycle listener bound in {@link connect}. */
|
|
259
|
+
#painted = DEFAULT_VALUE;
|
|
260
|
+
#confirmed = DEFAULT_VALUE;
|
|
261
|
+
#drag = null;
|
|
262
|
+
#paneTabindex = new TabindexLoan("-1", this.identifier);
|
|
132
263
|
#cycleAbort = null;
|
|
133
|
-
|
|
134
|
-
#repaint = new MicrotaskCoalescer(() => this.#render());
|
|
264
|
+
#repaint = new MorphRenderWatcher(() => this.#render());
|
|
135
265
|
connect() {
|
|
136
|
-
this.element
|
|
137
|
-
this.#repaint.
|
|
266
|
+
this.#paneTabindex.reclaimWithin(this.element);
|
|
267
|
+
this.#repaint.observe(this.element);
|
|
268
|
+
this.#confirmed = this.#position;
|
|
138
269
|
this.#render();
|
|
139
270
|
this.#cycleAbort = new AbortController();
|
|
140
271
|
this.element.addEventListener("keydown", this.#onCycleKeydown, {
|
|
141
272
|
signal: this.#cycleAbort.signal
|
|
142
273
|
});
|
|
143
274
|
}
|
|
144
|
-
/** Cancels any active pointer drag so listeners never leak past disconnect. */
|
|
145
275
|
disconnect() {
|
|
146
|
-
this.#
|
|
147
|
-
this.#dragAbort = null;
|
|
276
|
+
this.#endDrag();
|
|
148
277
|
this.#cycleAbort?.abort();
|
|
149
278
|
this.#cycleAbort = null;
|
|
150
|
-
this.#repaint.
|
|
279
|
+
this.#repaint.disconnect();
|
|
151
280
|
this.#paneTabindex.returnAll();
|
|
152
|
-
this.element.removeAttribute("data-dragging");
|
|
153
281
|
}
|
|
154
|
-
/** Moves focus to the next pane on `F6`, wrapping; entering at the first one. */
|
|
155
282
|
#onCycleKeydown = (event) => {
|
|
156
283
|
if (event.key !== "F6") return;
|
|
157
284
|
if (event.isComposing) return;
|
|
@@ -167,38 +294,39 @@ var ResizableController = class extends Controller {
|
|
|
167
294
|
this.#paneTabindex.lend(next);
|
|
168
295
|
next.focus();
|
|
169
296
|
};
|
|
170
|
-
/** Re-renders when application code (or a Turbo morph) changes `value` at runtime. */
|
|
171
297
|
valueValueChanged() {
|
|
172
298
|
this.#repaint.schedule();
|
|
173
299
|
}
|
|
174
|
-
/** Re-renders when application code (or a Turbo morph) changes `min` at runtime. */
|
|
175
300
|
minValueChanged() {
|
|
176
301
|
this.#repaint.schedule();
|
|
177
302
|
}
|
|
178
|
-
/** Re-renders when application code (or a Turbo morph) changes `max` at runtime. */
|
|
179
303
|
maxValueChanged() {
|
|
180
304
|
this.#repaint.schedule();
|
|
181
305
|
}
|
|
182
|
-
/** Re-publishes range and position onto a separator swapped in after connect. */
|
|
183
306
|
separatorTargetConnected() {
|
|
184
307
|
this.#repaint.schedule();
|
|
185
308
|
}
|
|
186
|
-
|
|
309
|
+
separatorTargetDisconnected(separator) {
|
|
310
|
+
if (this.#drag?.separator === separator && !this.separatorTargets.includes(separator)) {
|
|
311
|
+
this.#endDrag();
|
|
312
|
+
}
|
|
313
|
+
this.#repaint.schedule();
|
|
314
|
+
}
|
|
187
315
|
onPointerDown(event) {
|
|
188
316
|
if (!this.hasSeparatorTarget || event.button !== 0) return;
|
|
189
317
|
event.preventDefault();
|
|
190
|
-
const
|
|
318
|
+
const separators = this.separatorTargets;
|
|
319
|
+
const separator = separators[ownerIndex(separators, event.target)] ?? this.separatorTarget;
|
|
191
320
|
separator.setPointerCapture(event.pointerId);
|
|
192
321
|
separator.focus();
|
|
193
322
|
this.element.setAttribute("data-dragging", "true");
|
|
194
|
-
this.#
|
|
323
|
+
this.#drag?.abort.abort();
|
|
195
324
|
const abort = new AbortController();
|
|
196
|
-
this.#
|
|
325
|
+
this.#drag = { separator, pointerId: event.pointerId, abort };
|
|
197
326
|
separator.addEventListener("pointermove", this.#onPointerMove, { signal: abort.signal });
|
|
198
327
|
separator.addEventListener("pointerup", this.#onPointerUp, { signal: abort.signal });
|
|
199
328
|
separator.addEventListener("pointercancel", this.#onPointerUp, { signal: abort.signal });
|
|
200
329
|
}
|
|
201
|
-
/** Keydown adjustments for ArrowUp/Down/Left/Right and Home/End. */
|
|
202
330
|
onKeydown(event) {
|
|
203
331
|
if (isReservedArrowChord(event)) return;
|
|
204
332
|
if (!this.hasSeparatorTarget) return;
|
|
@@ -251,55 +379,67 @@ var ResizableController = class extends Controller {
|
|
|
251
379
|
}
|
|
252
380
|
if (handled) {
|
|
253
381
|
event.preventDefault();
|
|
254
|
-
this
|
|
255
|
-
this.#render();
|
|
256
|
-
this.#dispatchChange();
|
|
382
|
+
this.#commit(nextValue);
|
|
257
383
|
}
|
|
258
384
|
}
|
|
259
|
-
/** Collapses the primary pane to its minimum, or returns it to the last position. */
|
|
260
385
|
toggle() {
|
|
261
386
|
const { min, max } = this.#range;
|
|
262
387
|
if (this.#position > min) {
|
|
263
388
|
this.#valueBeforeCollapse = this.#position;
|
|
264
|
-
this
|
|
389
|
+
this.#commit(min);
|
|
265
390
|
} else {
|
|
266
|
-
|
|
391
|
+
const restored = this.#valueBeforeCollapse ?? max;
|
|
267
392
|
this.#valueBeforeCollapse = null;
|
|
393
|
+
this.#commit(restored);
|
|
268
394
|
}
|
|
269
|
-
this.#render();
|
|
270
|
-
this.#dispatchChange();
|
|
271
395
|
}
|
|
272
396
|
#onPointerMove = (event) => {
|
|
273
397
|
if (!this.hasSeparatorTarget) return;
|
|
274
398
|
const rect = this.element.getBoundingClientRect();
|
|
275
399
|
const raw = this.#isVertical ? (event.clientX - rect.left) / rect.width : (event.clientY - rect.top) / rect.height;
|
|
276
400
|
const fraction = Number.isFinite(raw) ? Math.max(0, Math.min(raw, 1)) : 0;
|
|
277
|
-
|
|
278
|
-
this.valueValue = Math.max(min, Math.min(Math.round(fraction * 100), max));
|
|
279
|
-
this.#render();
|
|
401
|
+
this.#move(Math.round(fraction * 100));
|
|
280
402
|
};
|
|
281
|
-
#onPointerUp = (
|
|
403
|
+
#onPointerUp = () => {
|
|
404
|
+
this.#endDrag();
|
|
405
|
+
this.#confirm();
|
|
406
|
+
};
|
|
407
|
+
#endDrag() {
|
|
282
408
|
this.element.removeAttribute("data-dragging");
|
|
283
|
-
this.#
|
|
284
|
-
this.#
|
|
285
|
-
if (
|
|
286
|
-
|
|
409
|
+
const drag = this.#drag;
|
|
410
|
+
this.#drag = null;
|
|
411
|
+
if (!drag) return;
|
|
412
|
+
drag.abort.abort();
|
|
413
|
+
try {
|
|
414
|
+
drag.separator.releasePointerCapture?.(drag.pointerId);
|
|
415
|
+
} catch {
|
|
287
416
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
417
|
+
}
|
|
418
|
+
#commit(raw) {
|
|
419
|
+
this.#move(raw);
|
|
420
|
+
this.#confirm();
|
|
421
|
+
}
|
|
422
|
+
#move(raw) {
|
|
423
|
+
const { min, max } = this.#range;
|
|
424
|
+
const position = Math.max(min, Math.min(raw, max));
|
|
425
|
+
this.valueValue = position;
|
|
426
|
+
this.#painted = position;
|
|
427
|
+
this.#paint();
|
|
428
|
+
}
|
|
297
429
|
#render() {
|
|
430
|
+
this.#paint();
|
|
431
|
+
const position = this.#position;
|
|
432
|
+
if (position === this.#painted) return;
|
|
433
|
+
this.#painted = position;
|
|
434
|
+
if (position === this.#confirmed) return;
|
|
435
|
+
this.#confirmed = position;
|
|
436
|
+
this.dispatch("reconcile", { detail: this.#detail(position) });
|
|
437
|
+
}
|
|
438
|
+
#paint() {
|
|
439
|
+
if (this.#drag) this.element.setAttribute("data-dragging", "true");
|
|
440
|
+
else this.element.removeAttribute("data-dragging");
|
|
298
441
|
const { min, max } = this.#range;
|
|
299
442
|
const position = this.#position;
|
|
300
|
-
if (this.valueValue !== position) {
|
|
301
|
-
this.valueValue = position;
|
|
302
|
-
}
|
|
303
443
|
this.element.style.setProperty("--stimeo--resizable-fraction", String(position / 100));
|
|
304
444
|
if (this.hasSeparatorTarget) {
|
|
305
445
|
this.separatorTarget.setAttribute("aria-valuenow", String(position));
|
|
@@ -307,32 +447,68 @@ var ResizableController = class extends Controller {
|
|
|
307
447
|
this.separatorTarget.setAttribute("aria-valuemax", String(max));
|
|
308
448
|
}
|
|
309
449
|
}
|
|
310
|
-
#
|
|
311
|
-
const
|
|
312
|
-
|
|
450
|
+
#confirm() {
|
|
451
|
+
const position = this.#position;
|
|
452
|
+
if (position === this.#confirmed) return;
|
|
453
|
+
this.#confirmed = position;
|
|
454
|
+
this.dispatch("change", { detail: this.#detail(position) });
|
|
455
|
+
}
|
|
456
|
+
#detail(position) {
|
|
457
|
+
return { value: position, fraction: position / 100 };
|
|
313
458
|
}
|
|
314
|
-
/** The declared range after validation; an unreadable bound uses its default. */
|
|
315
459
|
get #range() {
|
|
316
|
-
const min =
|
|
317
|
-
const max =
|
|
460
|
+
const min = this.#safeMin;
|
|
461
|
+
const max = this.#safeMax;
|
|
318
462
|
return { min, max: Math.max(min, max) };
|
|
319
463
|
}
|
|
320
|
-
/** The declared position, clamped into the validated range. */
|
|
321
464
|
get #position() {
|
|
322
465
|
const { min, max } = this.#range;
|
|
323
|
-
const value =
|
|
466
|
+
const value = this.#safeValue;
|
|
324
467
|
return Math.max(min, Math.min(value, max));
|
|
325
468
|
}
|
|
326
|
-
/** The keyboard increment; a non-positive or unreadable one uses the default. */
|
|
327
469
|
get #step() {
|
|
328
|
-
return
|
|
470
|
+
return this.#safeStep;
|
|
329
471
|
}
|
|
330
|
-
/** Whether the divider runs vertically; a separator that does not say is horizontal. */
|
|
331
472
|
get #isVertical() {
|
|
332
473
|
return this.hasSeparatorTarget && this.separatorTarget.getAttribute("aria-orientation") === "vertical";
|
|
333
474
|
}
|
|
475
|
+
get #safeMin() {
|
|
476
|
+
return this.#numbers.read(
|
|
477
|
+
this,
|
|
478
|
+
"min",
|
|
479
|
+
this.minValue,
|
|
480
|
+
_ResizableController.values.min.default,
|
|
481
|
+
_ResizableController.valueConstraints.min
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
get #safeMax() {
|
|
485
|
+
return this.#numbers.read(
|
|
486
|
+
this,
|
|
487
|
+
"max",
|
|
488
|
+
this.maxValue,
|
|
489
|
+
_ResizableController.values.max.default,
|
|
490
|
+
_ResizableController.valueConstraints.max
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
get #safeStep() {
|
|
494
|
+
return this.#numbers.read(
|
|
495
|
+
this,
|
|
496
|
+
"step",
|
|
497
|
+
this.stepValue,
|
|
498
|
+
_ResizableController.values.step.default,
|
|
499
|
+
_ResizableController.valueConstraints.step
|
|
500
|
+
);
|
|
501
|
+
}
|
|
502
|
+
get #safeValue() {
|
|
503
|
+
return this.#numbers.read(
|
|
504
|
+
this,
|
|
505
|
+
"value",
|
|
506
|
+
this.valueValue,
|
|
507
|
+
_ResizableController.values.value.default,
|
|
508
|
+
_ResizableController.valueConstraints.value
|
|
509
|
+
);
|
|
510
|
+
}
|
|
334
511
|
};
|
|
335
512
|
|
|
336
513
|
export { ResizableController };
|
|
337
|
-
//# sourceMappingURL=resizable_controller.js.map
|
|
338
514
|
//# sourceMappingURL=resizable_controller.js.map
|