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,8 +1,5 @@
|
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
|
2
2
|
|
|
3
|
-
// src/controllers/bulk_select_controller.ts
|
|
4
|
-
|
|
5
|
-
// src/utils/announce.ts
|
|
6
3
|
function announce(message, options = {}) {
|
|
7
4
|
const text = message.trim();
|
|
8
5
|
if (text.length === 0) return;
|
|
@@ -19,27 +16,180 @@ function fillTemplate(template, values) {
|
|
|
19
16
|
});
|
|
20
17
|
}
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
function sharedRegistry(key, create) {
|
|
20
|
+
const symbol = Symbol.for(key);
|
|
21
|
+
const scope = globalThis;
|
|
22
|
+
const existing = scope[symbol];
|
|
23
|
+
if (existing !== void 0) return existing;
|
|
24
|
+
const registry = create();
|
|
25
|
+
Object.defineProperty(globalThis, symbol, { value: registry });
|
|
26
|
+
return registry;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isAttributeValue(value) {
|
|
30
|
+
return value === null || typeof value === "string";
|
|
31
|
+
}
|
|
32
|
+
function readLeaseRecord(element, record) {
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(element.getAttribute(record) ?? "null");
|
|
35
|
+
if (!Array.isArray(parsed)) return void 0;
|
|
36
|
+
const [original, written, ...rest] = parsed;
|
|
37
|
+
const linked = rest[0] !== false;
|
|
38
|
+
const beneath = linked ? rest : rest.slice(1);
|
|
39
|
+
if (!beneath.every((under) => typeof under === "string")) return void 0;
|
|
40
|
+
return isAttributeValue(original) && isAttributeValue(written) ? { original, written, linked, beneath } : void 0;
|
|
41
|
+
} catch {
|
|
42
|
+
return void 0;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
var AttributeLease = class _AttributeLease {
|
|
46
|
+
static #registry = sharedRegistry(
|
|
47
|
+
"stimeo-ui.attribute-lease.registry.v1",
|
|
48
|
+
() => ({ stacks: /* @__PURE__ */ new WeakMap(), names: /* @__PURE__ */ new Map() })
|
|
49
|
+
);
|
|
50
|
+
#attribute;
|
|
51
|
+
#record;
|
|
52
|
+
#base;
|
|
53
|
+
#held = /* @__PURE__ */ new Set();
|
|
54
|
+
constructor(attribute, owner, options = {}) {
|
|
55
|
+
this.#attribute = attribute;
|
|
56
|
+
this.#record = `data-${owner}-${attribute}-lease`;
|
|
57
|
+
this.#base = options.base === true;
|
|
58
|
+
const names = _AttributeLease.#registry.names;
|
|
59
|
+
names.set(attribute, (names.get(attribute) ?? /* @__PURE__ */ new Set()).add(this.#record));
|
|
60
|
+
}
|
|
61
|
+
write(element, value) {
|
|
62
|
+
const stack = this.#stack(element);
|
|
63
|
+
const hold = this.#take(stack) ?? this.#acquire(element, stack);
|
|
64
|
+
this.#held.add(element);
|
|
65
|
+
const shows = !this.#covered(element, stack, hold);
|
|
66
|
+
hold.written = value;
|
|
67
|
+
this.#mark(element, stack);
|
|
68
|
+
if (shows) this.#reflect(element, value);
|
|
69
|
+
}
|
|
70
|
+
return(element) {
|
|
71
|
+
const held = this.#held.delete(element);
|
|
72
|
+
const known = _AttributeLease.#registry.stacks.get(element)?.has(this.#attribute);
|
|
73
|
+
const stack = held || known || element.hasAttribute(this.#record) ? this.#stack(element) : [];
|
|
74
|
+
const hold = this.#take(stack);
|
|
75
|
+
if (!hold) return false;
|
|
76
|
+
const carries = element.getAttribute(this.#attribute) === hold.written;
|
|
77
|
+
const owns = carries && !this.#covered(element, stack, hold);
|
|
78
|
+
const index = stack.indexOf(hold);
|
|
79
|
+
stack.splice(index, 1);
|
|
80
|
+
const above = stack[index];
|
|
81
|
+
if (above) above.linked &&= hold.linked;
|
|
82
|
+
this.#mark(element, stack);
|
|
83
|
+
if (!owns) return true;
|
|
84
|
+
const below = hold.linked ? stack[index - 1] : void 0;
|
|
85
|
+
this.#reflect(element, below ? below.written : hold.original);
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
returnAll() {
|
|
89
|
+
for (const element of Array.from(this.#held)) this.return(element);
|
|
90
|
+
}
|
|
91
|
+
#stack(element) {
|
|
92
|
+
const stacks = _AttributeLease.#registry.stacks;
|
|
93
|
+
const byAttribute = stacks.get(element) ?? /* @__PURE__ */ new Map();
|
|
94
|
+
stacks.set(element, byAttribute);
|
|
95
|
+
const stack = byAttribute.get(this.#attribute) ?? this.#seed(element);
|
|
96
|
+
byAttribute.set(this.#attribute, stack);
|
|
97
|
+
return stack;
|
|
98
|
+
}
|
|
99
|
+
#seed(element) {
|
|
100
|
+
const found = [];
|
|
101
|
+
for (const record of _AttributeLease.#registry.names.get(this.#attribute) ?? []) {
|
|
102
|
+
const read = readLeaseRecord(element, record);
|
|
103
|
+
if (read) found.push({ ...read, record });
|
|
104
|
+
}
|
|
105
|
+
const depth = ({ beneath }) => beneath.filter((name) => found.some((other) => other.record === name)).length;
|
|
106
|
+
return found.sort((one, other) => depth(one) - depth(other)).map(({ record, written, original, linked }, index) => ({
|
|
107
|
+
lease: null,
|
|
108
|
+
record,
|
|
109
|
+
written,
|
|
110
|
+
original,
|
|
111
|
+
linked: linked && index > 0
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
#take(stack) {
|
|
115
|
+
const hold = stack.find((held) => held.lease === this) ?? stack.find((held) => held.lease === null && held.record === this.#record);
|
|
116
|
+
if (hold) hold.lease = this;
|
|
117
|
+
return hold;
|
|
118
|
+
}
|
|
119
|
+
#acquire(element, stack) {
|
|
120
|
+
const current = element.getAttribute(this.#attribute);
|
|
121
|
+
const top = stack.at(-1);
|
|
122
|
+
const bottom = stack[0];
|
|
123
|
+
const over = top?.written === current ? top : void 0;
|
|
124
|
+
const from = this.#base ? bottom : over;
|
|
125
|
+
const hold = {
|
|
126
|
+
lease: this,
|
|
127
|
+
record: this.#record,
|
|
128
|
+
written: current,
|
|
129
|
+
original: from ? from.original : current,
|
|
130
|
+
linked: !this.#base && over !== void 0
|
|
131
|
+
};
|
|
132
|
+
if (this.#base && bottom) {
|
|
133
|
+
bottom.linked = true;
|
|
134
|
+
stack.unshift(hold);
|
|
135
|
+
} else stack.push(hold);
|
|
136
|
+
return hold;
|
|
137
|
+
}
|
|
138
|
+
#covered(element, stack, hold) {
|
|
139
|
+
const current = element.getAttribute(this.#attribute);
|
|
140
|
+
return stack.slice(stack.indexOf(hold) + 1).some((above) => above.written === current);
|
|
141
|
+
}
|
|
142
|
+
#mark(element, stack) {
|
|
143
|
+
const records = /* @__PURE__ */ new Map([[this.#record, null]]);
|
|
144
|
+
stack.forEach((hold, index) => {
|
|
145
|
+
const names = stack.slice(0, index).map((under) => under.record);
|
|
146
|
+
const beneath = [...new Set(names.reverse())].filter((name) => name !== hold.record);
|
|
147
|
+
const marker = hold.linked || beneath.length === 0 ? [] : [false];
|
|
148
|
+
records.set(
|
|
149
|
+
hold.record,
|
|
150
|
+
hold.written === hold.original && beneath.length === 0 && index === stack.length - 1 ? null : JSON.stringify([hold.original, hold.written, ...marker, ...beneath])
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
for (const [name, recorded] of records) {
|
|
154
|
+
if (element.getAttribute(name) === recorded) continue;
|
|
155
|
+
if (recorded === null) element.removeAttribute(name);
|
|
156
|
+
else element.setAttribute(name, recorded);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
#reflect(element, value) {
|
|
160
|
+
if (element.getAttribute(this.#attribute) === value) return;
|
|
161
|
+
if (value === null) element.removeAttribute(this.#attribute);
|
|
162
|
+
else element.setAttribute(this.#attribute, value);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
var ListenerSet = class {
|
|
167
|
+
#abort = new AbortController();
|
|
168
|
+
add(target, type, handler, options) {
|
|
169
|
+
target.addEventListener(type, handler, { ...options, signal: this.#abort.signal });
|
|
170
|
+
}
|
|
171
|
+
dispose() {
|
|
172
|
+
this.#abort.abort();
|
|
173
|
+
this.#abort = new AbortController();
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
23
177
|
var MicrotaskCoalescer = class {
|
|
24
178
|
#run;
|
|
25
179
|
#queued = false;
|
|
26
180
|
#active = false;
|
|
27
181
|
#generation = 0;
|
|
28
|
-
/** @param run - the single reconciliation pass, invoked at most once per batch. */
|
|
29
182
|
constructor(run) {
|
|
30
183
|
this.#run = run;
|
|
31
184
|
}
|
|
32
|
-
/** Opens the window in which {@link schedule} is honoured; call from `connect()`. */
|
|
33
185
|
activate() {
|
|
34
186
|
this.#active = true;
|
|
35
187
|
}
|
|
36
|
-
/** Closes the window and drops any pending pass; call from `disconnect()`. */
|
|
37
188
|
cancel() {
|
|
38
189
|
this.#active = false;
|
|
39
190
|
this.#queued = false;
|
|
40
191
|
this.#generation += 1;
|
|
41
192
|
}
|
|
42
|
-
/** Requests one pass after the batch settles. Idempotent; inert outside the window. */
|
|
43
193
|
schedule() {
|
|
44
194
|
if (!this.#active || this.#queued) return;
|
|
45
195
|
this.#queued = true;
|
|
@@ -52,26 +202,92 @@ var MicrotaskCoalescer = class {
|
|
|
52
202
|
}
|
|
53
203
|
};
|
|
54
204
|
|
|
55
|
-
|
|
56
|
-
|
|
205
|
+
var MorphRenderWatcher = class {
|
|
206
|
+
#listeners = new ListenerSet();
|
|
207
|
+
#pass;
|
|
208
|
+
#element = null;
|
|
209
|
+
#onMorph = () => this.#pass.schedule();
|
|
210
|
+
constructor(run) {
|
|
211
|
+
this.#pass = new MicrotaskCoalescer(run);
|
|
212
|
+
}
|
|
213
|
+
observe(element) {
|
|
214
|
+
if (this.#element === element) return;
|
|
215
|
+
this.disconnect();
|
|
216
|
+
this.#element = element;
|
|
217
|
+
this.#pass.activate();
|
|
218
|
+
this.#listeners.add(element, "turbo:morph-element", this.#onMorph);
|
|
219
|
+
}
|
|
220
|
+
schedule() {
|
|
221
|
+
this.#pass.schedule();
|
|
222
|
+
}
|
|
223
|
+
disconnect() {
|
|
224
|
+
this.#listeners.dispose();
|
|
225
|
+
this.#pass.cancel();
|
|
226
|
+
this.#element = null;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
var NUMBER_BOUNDS = {
|
|
231
|
+
nonNegative: { finite: true, min: 0 }};
|
|
232
|
+
function matchesNumberBounds(value, bounds) {
|
|
233
|
+
if (!Number.isFinite(value)) {
|
|
234
|
+
const direction = value === Infinity ? "positive" : value === -Infinity ? "negative" : null;
|
|
235
|
+
if (direction === null) return false;
|
|
236
|
+
if (bounds.allowInfinity !== "both" && bounds.allowInfinity !== direction) return false;
|
|
237
|
+
}
|
|
238
|
+
if (bounds.min !== void 0 && value < bounds.min) return false;
|
|
239
|
+
if (bounds.max !== void 0 && value > bounds.max) return false;
|
|
240
|
+
if (bounds.exclusiveMin !== void 0 && value <= bounds.exclusiveMin) return false;
|
|
241
|
+
if (bounds.integer && !Number.isInteger(value)) return false;
|
|
242
|
+
if (bounds.allowedValues !== void 0 && !bounds.allowedValues.includes(value)) return false;
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function readNumber(raw, fallback, bounds) {
|
|
247
|
+
return matchesNumberBounds(raw, bounds) ? raw : fallback;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
var NumberValueReader = class {
|
|
251
|
+
#lastRejected = /* @__PURE__ */ new Map();
|
|
252
|
+
read(owner, name, raw, fallback, bounds) {
|
|
253
|
+
const resolved = readNumber(raw, fallback, bounds);
|
|
254
|
+
if (matchesNumberBounds(raw, bounds)) {
|
|
255
|
+
this.#lastRejected.delete(name);
|
|
256
|
+
return resolved;
|
|
257
|
+
}
|
|
258
|
+
const attribute = `data-${owner.identifier}-${name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}-value`;
|
|
259
|
+
const literal = owner.element.getAttribute(attribute);
|
|
260
|
+
if (literal === null) {
|
|
261
|
+
this.#lastRejected.delete(name);
|
|
262
|
+
return resolved;
|
|
263
|
+
}
|
|
264
|
+
if (this.#lastRejected.get(name) !== literal) {
|
|
265
|
+
this.#lastRejected.set(name, literal);
|
|
266
|
+
console.warn(
|
|
267
|
+
`Stimeo UI: "${owner.identifier}" has an invalid number Value "${name}" declaration ${JSON.stringify(literal)}; using ${fallback}.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return resolved;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
var BulkSelectController = class _BulkSelectController extends Controller {
|
|
275
|
+
#numbers = new NumberValueReader();
|
|
57
276
|
static targets = ["all", "item", "bar", "count", "selectAllPages"];
|
|
58
277
|
static values = {
|
|
59
278
|
totalCount: { type: Number, default: 0 },
|
|
60
279
|
announceText: { type: String, default: "" }
|
|
61
280
|
};
|
|
281
|
+
static valueConstraints = {
|
|
282
|
+
totalCount: NUMBER_BOUNDS.nonNegative
|
|
283
|
+
};
|
|
62
284
|
static actions = ["clear", "selectAllPages"];
|
|
63
285
|
static events = ["change", "reconcile"];
|
|
64
|
-
/** All-pages mode is a transient UI state, mirrored to `data-all-pages` so a
|
|
65
|
-
* `connect()` over markup that already carries the attribute rehydrates the
|
|
66
|
-
* mode — a morph, a Turbo Stream, a server that renders it back, or a restore
|
|
67
|
-
* visit, whose cached snapshot carries the attribute too. */
|
|
68
286
|
#allPagesMode = false;
|
|
69
|
-
/** Last emitted figures, so a recompute reports only on a real change. */
|
|
70
287
|
#lastCount = -1;
|
|
71
288
|
#lastAllPages = false;
|
|
72
|
-
|
|
73
|
-
#
|
|
74
|
-
/** Delegated `change` handler covering the select-all box and every row. */
|
|
289
|
+
#reconcile = new MorphRenderWatcher(() => this.#reconcileNow());
|
|
290
|
+
#barHidden = new AttributeLease("hidden", this.identifier);
|
|
75
291
|
#onChange = (event) => {
|
|
76
292
|
const target = event.target;
|
|
77
293
|
if (!target) return;
|
|
@@ -84,43 +300,45 @@ var BulkSelectController = class extends Controller {
|
|
|
84
300
|
};
|
|
85
301
|
connect() {
|
|
86
302
|
this.#allPagesMode = this.element.dataset.allPages === "true";
|
|
87
|
-
this.#reconcile.
|
|
303
|
+
this.#reconcile.observe(this.element);
|
|
88
304
|
this.element.addEventListener("change", this.#onChange);
|
|
89
305
|
this.#recompute();
|
|
90
306
|
}
|
|
91
307
|
disconnect() {
|
|
92
308
|
this.element.removeEventListener("change", this.#onChange);
|
|
93
|
-
this.#reconcile.
|
|
309
|
+
this.#reconcile.disconnect();
|
|
94
310
|
}
|
|
95
|
-
/** Repairs the figures for a row that arrived at runtime. */
|
|
96
311
|
itemTargetConnected() {
|
|
97
312
|
this.#reconcile.schedule();
|
|
98
313
|
}
|
|
99
|
-
/** Repairs the figures after a row leaves, so a removed selection stops counting. */
|
|
100
314
|
itemTargetDisconnected() {
|
|
101
315
|
this.#reconcile.schedule();
|
|
102
316
|
}
|
|
103
|
-
/** Reflects the current selection onto a select-all box added at runtime. */
|
|
104
317
|
allTargetConnected() {
|
|
105
318
|
this.#reconcile.schedule();
|
|
106
319
|
}
|
|
107
|
-
/** Repairs the figures after the select-all box leaves. */
|
|
108
320
|
allTargetDisconnected() {
|
|
109
321
|
this.#reconcile.schedule();
|
|
110
322
|
}
|
|
111
|
-
|
|
323
|
+
barTargetConnected() {
|
|
324
|
+
this.#reconcile.schedule();
|
|
325
|
+
}
|
|
326
|
+
barTargetDisconnected(bar) {
|
|
327
|
+
if (!this.barTargets.includes(bar)) this.#barHidden.return(bar);
|
|
328
|
+
this.#reconcile.schedule();
|
|
329
|
+
}
|
|
330
|
+
countTargetConnected() {
|
|
331
|
+
this.#reconcile.schedule();
|
|
332
|
+
}
|
|
333
|
+
countTargetDisconnected() {
|
|
334
|
+
this.#reconcile.schedule();
|
|
335
|
+
}
|
|
112
336
|
totalCountValueChanged() {
|
|
113
|
-
if (!Number.isFinite(this.totalCountValue)) {
|
|
114
|
-
this.totalCountValue = 0;
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
337
|
this.#reconcile.schedule();
|
|
118
338
|
}
|
|
119
|
-
/** Repaints so wording changed at runtime is used by the next announcement. */
|
|
120
339
|
announceTextValueChanged() {
|
|
121
340
|
this.#reconcile.schedule();
|
|
122
341
|
}
|
|
123
|
-
/** Clears every selection (rows + select-all) and exits all-pages mode. */
|
|
124
342
|
clear() {
|
|
125
343
|
for (const item of this.itemTargets) item.checked = false;
|
|
126
344
|
if (this.hasAllTarget) {
|
|
@@ -130,23 +348,14 @@ var BulkSelectController = class extends Controller {
|
|
|
130
348
|
this.#exitAllPages();
|
|
131
349
|
this.#reportChange(this.#recompute());
|
|
132
350
|
}
|
|
133
|
-
/**
|
|
134
|
-
* Enters "select all across pages" mode: the count shows `totalCount`, and every
|
|
135
|
-
* row on this page is checked.
|
|
136
|
-
*
|
|
137
|
-
* The mode's claim is that the whole set is selected, so leaving a visible row
|
|
138
|
-
* unchecked would put the page and the count in open disagreement.
|
|
139
|
-
*/
|
|
140
351
|
selectAllPages() {
|
|
141
352
|
this.#allPagesMode = true;
|
|
142
353
|
this.#checkEveryRow();
|
|
143
354
|
this.#reportChange(this.#recompute());
|
|
144
355
|
}
|
|
145
|
-
/** Marks every row on this page selected. */
|
|
146
356
|
#checkEveryRow() {
|
|
147
357
|
for (const item of this.itemTargets) item.checked = true;
|
|
148
358
|
}
|
|
149
|
-
/** Mirrors the select-all box to every row, then recomputes. */
|
|
150
359
|
#applyAll() {
|
|
151
360
|
if (!this.hasAllTarget) return;
|
|
152
361
|
const { checked } = this.allTarget;
|
|
@@ -157,7 +366,6 @@ var BulkSelectController = class extends Controller {
|
|
|
157
366
|
#exitAllPages() {
|
|
158
367
|
this.#allPagesMode = false;
|
|
159
368
|
}
|
|
160
|
-
/** Repairs the derived state after the page moved rows or a render input. */
|
|
161
369
|
#reconcileNow() {
|
|
162
370
|
if (this.#allPagesMode) this.#checkEveryRow();
|
|
163
371
|
const detail = this.#recompute();
|
|
@@ -165,23 +373,18 @@ var BulkSelectController = class extends Controller {
|
|
|
165
373
|
this.dispatch("reconcile", { detail });
|
|
166
374
|
this.#announce(detail);
|
|
167
375
|
}
|
|
168
|
-
/** Reports a selection the user moved. */
|
|
169
376
|
#reportChange(detail) {
|
|
170
377
|
if (!detail) return;
|
|
171
378
|
this.dispatch("change", { detail });
|
|
172
379
|
this.#announce(detail);
|
|
173
380
|
}
|
|
174
|
-
/** Hands the count to the shared announcer, worded by the consumer. */
|
|
175
381
|
#announce(detail) {
|
|
176
382
|
announce(fillTemplate(this.announceTextValue, { count: detail.count }));
|
|
177
383
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
*
|
|
183
|
-
* @stimeoRenderRoot
|
|
184
|
-
*/
|
|
384
|
+
get #totalCount() {
|
|
385
|
+
const declared = this.#safeTotalCount;
|
|
386
|
+
return declared;
|
|
387
|
+
}
|
|
185
388
|
#recompute() {
|
|
186
389
|
const items = this.itemTargets;
|
|
187
390
|
const total = items.length;
|
|
@@ -191,13 +394,13 @@ var BulkSelectController = class extends Controller {
|
|
|
191
394
|
this.allTarget.checked = total > 0 && checked === total;
|
|
192
395
|
this.allTarget.indeterminate = checked > 0 && checked < total;
|
|
193
396
|
}
|
|
194
|
-
const count = allPages ? this
|
|
397
|
+
const count = allPages ? this.#totalCount : checked;
|
|
195
398
|
const show = allPages || checked > 0;
|
|
196
399
|
if (this.hasBarTarget) {
|
|
197
400
|
if (!show && this.hasAllTarget && this.barTarget.contains(document.activeElement)) {
|
|
198
401
|
this.allTarget.focus();
|
|
199
402
|
}
|
|
200
|
-
this.barTarget
|
|
403
|
+
this.#barHidden.write(this.barTarget, show ? null : "");
|
|
201
404
|
}
|
|
202
405
|
if (this.hasCountTarget) this.countTarget.textContent = String(count);
|
|
203
406
|
this.element.setAttribute("data-selected-count", String(checked));
|
|
@@ -208,8 +411,16 @@ var BulkSelectController = class extends Controller {
|
|
|
208
411
|
this.#lastAllPages = allPages;
|
|
209
412
|
return changed ? { count, allPages } : null;
|
|
210
413
|
}
|
|
414
|
+
get #safeTotalCount() {
|
|
415
|
+
return this.#numbers.read(
|
|
416
|
+
this,
|
|
417
|
+
"totalCount",
|
|
418
|
+
this.totalCountValue,
|
|
419
|
+
_BulkSelectController.values.totalCount.default,
|
|
420
|
+
_BulkSelectController.valueConstraints.totalCount
|
|
421
|
+
);
|
|
422
|
+
}
|
|
211
423
|
};
|
|
212
424
|
|
|
213
425
|
export { BulkSelectController };
|
|
214
|
-
//# sourceMappingURL=bulk_select_controller.js.map
|
|
215
426
|
//# sourceMappingURL=bulk_select_controller.js.map
|