stimeo-ui 0.16.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 +31 -0
- data/README.md +8 -1
- data/dist/cable/index.js +363 -436
- data/dist/controllers/accordion_controller.js +166 -55
- data/dist/controllers/alert_dialog_controller.js +862 -221
- data/dist/controllers/announcer_controller.js +135 -198
- data/dist/controllers/aspect_ratio_controller.js +0 -10
- data/dist/controllers/auto_submit_controller.js +271 -127
- data/dist/controllers/avatar_controller.js +171 -105
- data/dist/controllers/breadcrumb_controller.js +0 -130
- data/dist/controllers/bulk_select_controller.js +262 -52
- data/dist/controllers/calendar_controller.js +175 -209
- data/dist/controllers/carousel_controller.js +461 -399
- data/dist/controllers/character_counter_controller.js +246 -156
- data/dist/controllers/checkbox_controller.js +32 -74
- data/dist/controllers/clipboard_controller.js +168 -147
- data/dist/controllers/collapsible_controller.js +301 -120
- data/dist/controllers/color_picker_controller.js +149 -139
- data/dist/controllers/combobox_controller.js +360 -133
- data/dist/controllers/command_palette_controller.js +970 -358
- data/dist/controllers/conditional_fields_controller.js +166 -124
- data/dist/controllers/confirm_controller.js +939 -248
- data/dist/controllers/context_menu_controller.js +256 -128
- data/dist/controllers/count_up_controller.js +73 -21
- data/dist/controllers/countdown_controller.js +183 -148
- data/dist/controllers/currency_input_controller.js +134 -158
- data/dist/controllers/data_grid_controller.js +114 -186
- data/dist/controllers/date_range_picker_controller.js +288 -206
- data/dist/controllers/dialog_controller.js +858 -223
- data/dist/controllers/direct_upload_controller.js +73 -153
- data/dist/controllers/dirty_form_controller.js +14 -48
- data/dist/controllers/dismissible_controller.js +175 -21
- data/dist/controllers/drawer_controller.js +933 -341
- data/dist/controllers/dropdown_controller.js +312 -82
- data/dist/controllers/editable_controller.js +158 -68
- data/dist/controllers/empty_state_controller.js +67 -53
- data/dist/controllers/file_dropzone_controller.js +311 -207
- data/dist/controllers/filter_controller.js +67 -39
- data/dist/controllers/flash_controller.js +172 -314
- data/dist/controllers/focus_controller.js +654 -240
- data/dist/controllers/form_field_controller.js +147 -132
- data/dist/controllers/form_validation_controller.js +13 -86
- data/dist/controllers/frame_loading_controller.js +264 -213
- data/dist/controllers/highlight_controller.js +107 -75
- data/dist/controllers/hover_card_controller.js +309 -118
- data/dist/controllers/idle_controller.js +267 -101
- data/dist/controllers/input_mask_controller.js +71 -72
- data/dist/controllers/intersection_controller.js +147 -106
- data/dist/controllers/lazy_frame_controller.js +9 -65
- data/dist/controllers/listbox_controller.js +306 -204
- data/dist/controllers/local_time_controller.js +54 -64
- data/dist/controllers/masonry_controller.js +70 -93
- data/dist/controllers/menu_controller.js +214 -156
- data/dist/controllers/menubar_controller.js +170 -329
- data/dist/controllers/meter_controller.js +151 -67
- data/dist/controllers/multi_select_controller.js +593 -358
- data/dist/controllers/navigation_menu_controller.js +124 -232
- data/dist/controllers/nested_form_controller.js +352 -182
- data/dist/controllers/network_status_controller.js +242 -54
- data/dist/controllers/number_input_controller.js +320 -263
- data/dist/controllers/optimistic_controller.js +16 -76
- data/dist/controllers/otp_controller.js +198 -193
- data/dist/controllers/overflow_indicator_controller.js +116 -113
- data/dist/controllers/overflow_menu_controller.js +343 -283
- data/dist/controllers/pagination_controller.js +295 -119
- data/dist/controllers/password_reveal_controller.js +389 -121
- data/dist/controllers/password_strength_controller.js +99 -153
- data/dist/controllers/persist_controller.js +237 -103
- data/dist/controllers/pointer_drag_controller.js +185 -165
- data/dist/controllers/popover_controller.js +236 -97
- data/dist/controllers/portal_controller.js +209 -76
- data/dist/controllers/preview_guard_controller.js +232 -116
- data/dist/controllers/progress_controller.js +125 -56
- data/dist/controllers/radio_group_controller.js +117 -137
- data/dist/controllers/range_slider_controller.js +192 -105
- data/dist/controllers/rating_controller.js +418 -207
- data/dist/controllers/read_more_controller.js +134 -63
- 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 +255 -139
- data/dist/controllers/roving_controller.js +39 -84
- data/dist/controllers/scroll_area_controller.js +428 -225
- data/dist/controllers/scroll_restore_controller.js +0 -53
- data/dist/controllers/scroll_visibility_controller.js +395 -145
- data/dist/controllers/scrollspy_controller.js +181 -242
- data/dist/controllers/separator_controller.js +267 -140
- data/dist/controllers/sidebar_controller.js +1023 -386
- data/dist/controllers/skeleton_controller.js +248 -97
- data/dist/controllers/slider_controller.js +159 -79
- data/dist/controllers/smart_sticky_header_controller.js +162 -63
- data/dist/controllers/sortable_controller.js +0 -112
- data/dist/controllers/spinner_controller.js +277 -169
- data/dist/controllers/step_indicator_controller.js +99 -46
- data/dist/controllers/stepper_controller.js +114 -73
- data/dist/controllers/stick_to_bottom_controller.js +262 -138
- data/dist/controllers/sticky_observer_controller.js +214 -49
- data/dist/controllers/submit_once_controller.js +294 -205
- data/dist/controllers/switch_controller.js +33 -60
- data/dist/controllers/tabs_controller.js +35 -29
- data/dist/controllers/tags_input_controller.js +136 -145
- data/dist/controllers/textarea_autosize_controller.js +71 -58
- data/dist/controllers/theme_controller.js +130 -112
- data/dist/controllers/time_picker_controller.js +146 -80
- data/dist/controllers/toast_controller.js +186 -297
- data/dist/controllers/toggle_group_controller.js +95 -136
- data/dist/controllers/toolbar_controller.js +68 -98
- data/dist/controllers/tooltip_controller.js +158 -124
- data/dist/controllers/transition_controller.js +123 -119
- data/dist/controllers/tree_view_controller.js +150 -345
- data/dist/index.js +6370 -9346
- data/dist/positioning/index.js +148 -47
- 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,39 +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
337
|
this.#reconcile.schedule();
|
|
114
338
|
}
|
|
115
|
-
/** Repaints so wording changed at runtime is used by the next announcement. */
|
|
116
339
|
announceTextValueChanged() {
|
|
117
340
|
this.#reconcile.schedule();
|
|
118
341
|
}
|
|
119
|
-
/** Clears every selection (rows + select-all) and exits all-pages mode. */
|
|
120
342
|
clear() {
|
|
121
343
|
for (const item of this.itemTargets) item.checked = false;
|
|
122
344
|
if (this.hasAllTarget) {
|
|
@@ -126,23 +348,14 @@ var BulkSelectController = class extends Controller {
|
|
|
126
348
|
this.#exitAllPages();
|
|
127
349
|
this.#reportChange(this.#recompute());
|
|
128
350
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Enters "select all across pages" mode: the count shows `totalCount`, and every
|
|
131
|
-
* row on this page is checked.
|
|
132
|
-
*
|
|
133
|
-
* The mode's claim is that the whole set is selected, so leaving a visible row
|
|
134
|
-
* unchecked would put the page and the count in open disagreement.
|
|
135
|
-
*/
|
|
136
351
|
selectAllPages() {
|
|
137
352
|
this.#allPagesMode = true;
|
|
138
353
|
this.#checkEveryRow();
|
|
139
354
|
this.#reportChange(this.#recompute());
|
|
140
355
|
}
|
|
141
|
-
/** Marks every row on this page selected. */
|
|
142
356
|
#checkEveryRow() {
|
|
143
357
|
for (const item of this.itemTargets) item.checked = true;
|
|
144
358
|
}
|
|
145
|
-
/** Mirrors the select-all box to every row, then recomputes. */
|
|
146
359
|
#applyAll() {
|
|
147
360
|
if (!this.hasAllTarget) return;
|
|
148
361
|
const { checked } = this.allTarget;
|
|
@@ -153,7 +366,6 @@ var BulkSelectController = class extends Controller {
|
|
|
153
366
|
#exitAllPages() {
|
|
154
367
|
this.#allPagesMode = false;
|
|
155
368
|
}
|
|
156
|
-
/** Repairs the derived state after the page moved rows or a render input. */
|
|
157
369
|
#reconcileNow() {
|
|
158
370
|
if (this.#allPagesMode) this.#checkEveryRow();
|
|
159
371
|
const detail = this.#recompute();
|
|
@@ -161,28 +373,18 @@ var BulkSelectController = class extends Controller {
|
|
|
161
373
|
this.dispatch("reconcile", { detail });
|
|
162
374
|
this.#announce(detail);
|
|
163
375
|
}
|
|
164
|
-
/** Reports a selection the user moved. */
|
|
165
376
|
#reportChange(detail) {
|
|
166
377
|
if (!detail) return;
|
|
167
378
|
this.dispatch("change", { detail });
|
|
168
379
|
this.#announce(detail);
|
|
169
380
|
}
|
|
170
|
-
/** Hands the count to the shared announcer, worded by the consumer. */
|
|
171
381
|
#announce(detail) {
|
|
172
382
|
announce(fillTemplate(this.announceTextValue, { count: detail.count }));
|
|
173
383
|
}
|
|
174
|
-
/** The declared `totalCount`, or the default `0` in place of a non-finite one. */
|
|
175
384
|
get #totalCount() {
|
|
176
|
-
const declared = this
|
|
177
|
-
return
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Recomputes the count, the select-all checked/indeterminate state, and the bar
|
|
181
|
-
* visibility from the current DOM. Returns the figures when the emitted count or
|
|
182
|
-
* all-pages flag actually moved, and `null` when they did not.
|
|
183
|
-
*
|
|
184
|
-
* @stimeoRenderRoot
|
|
185
|
-
*/
|
|
385
|
+
const declared = this.#safeTotalCount;
|
|
386
|
+
return declared;
|
|
387
|
+
}
|
|
186
388
|
#recompute() {
|
|
187
389
|
const items = this.itemTargets;
|
|
188
390
|
const total = items.length;
|
|
@@ -198,7 +400,7 @@ var BulkSelectController = class extends Controller {
|
|
|
198
400
|
if (!show && this.hasAllTarget && this.barTarget.contains(document.activeElement)) {
|
|
199
401
|
this.allTarget.focus();
|
|
200
402
|
}
|
|
201
|
-
this.barTarget
|
|
403
|
+
this.#barHidden.write(this.barTarget, show ? null : "");
|
|
202
404
|
}
|
|
203
405
|
if (this.hasCountTarget) this.countTarget.textContent = String(count);
|
|
204
406
|
this.element.setAttribute("data-selected-count", String(checked));
|
|
@@ -209,8 +411,16 @@ var BulkSelectController = class extends Controller {
|
|
|
209
411
|
this.#lastAllPages = allPages;
|
|
210
412
|
return changed ? { count, allPages } : null;
|
|
211
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
|
+
}
|
|
212
423
|
};
|
|
213
424
|
|
|
214
425
|
export { BulkSelectController };
|
|
215
|
-
//# sourceMappingURL=bulk_select_controller.js.map
|
|
216
426
|
//# sourceMappingURL=bulk_select_controller.js.map
|