stimulus-rails 1.2.1 → 1.2.2
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/app/assets/javascripts/stimulus-loading.js +7 -5
- data/app/assets/javascripts/stimulus.js +266 -111
- data/app/assets/javascripts/stimulus.min.js +1 -1
- data/app/assets/javascripts/stimulus.min.js.map +1 -1
- data/lib/generators/stimulus/stimulus_generator.rb +3 -1
- data/lib/stimulus/version.rb +1 -1
- metadata +3 -3
@@ -3,8 +3,8 @@
|
|
3
3
|
//= link ./stimulus-loading.js
|
4
4
|
|
5
5
|
/*
|
6
|
-
Stimulus 3.2.
|
7
|
-
Copyright ©
|
6
|
+
Stimulus 3.2.2
|
7
|
+
Copyright © 2023 Basecamp, LLC
|
8
8
|
*/
|
9
9
|
class EventListener {
|
10
10
|
constructor(eventTarget, eventName, eventOptions) {
|
@@ -169,23 +169,23 @@ const defaultActionDescriptorFilters = {
|
|
169
169
|
}
|
170
170
|
},
|
171
171
|
};
|
172
|
-
const descriptorPattern = /^(?:(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;
|
172
|
+
const descriptorPattern = /^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;
|
173
173
|
function parseActionDescriptorString(descriptorString) {
|
174
174
|
const source = descriptorString.trim();
|
175
175
|
const matches = source.match(descriptorPattern) || [];
|
176
|
-
let eventName = matches[
|
177
|
-
let keyFilter = matches[
|
176
|
+
let eventName = matches[2];
|
177
|
+
let keyFilter = matches[3];
|
178
178
|
if (keyFilter && !["keydown", "keyup", "keypress"].includes(eventName)) {
|
179
179
|
eventName += `.${keyFilter}`;
|
180
180
|
keyFilter = "";
|
181
181
|
}
|
182
182
|
return {
|
183
|
-
eventTarget: parseEventTarget(matches[
|
183
|
+
eventTarget: parseEventTarget(matches[4]),
|
184
184
|
eventName,
|
185
|
-
eventOptions: matches[
|
186
|
-
identifier: matches[
|
187
|
-
methodName: matches[
|
188
|
-
keyFilter,
|
185
|
+
eventOptions: matches[7] ? parseEventOptions(matches[7]) : {},
|
186
|
+
identifier: matches[5],
|
187
|
+
methodName: matches[6],
|
188
|
+
keyFilter: matches[1] || keyFilter,
|
189
189
|
};
|
190
190
|
}
|
191
191
|
function parseEventTarget(eventTargetName) {
|
@@ -226,6 +226,14 @@ function tokenize(value) {
|
|
226
226
|
return value.match(/[^\s]+/g) || [];
|
227
227
|
}
|
228
228
|
|
229
|
+
function isSomething(object) {
|
230
|
+
return object !== null && object !== undefined;
|
231
|
+
}
|
232
|
+
function hasProperty(object, property) {
|
233
|
+
return Object.prototype.hasOwnProperty.call(object, property);
|
234
|
+
}
|
235
|
+
|
236
|
+
const allModifiers = ["meta", "ctrl", "alt", "shift"];
|
229
237
|
class Action {
|
230
238
|
constructor(element, index, descriptor, schema) {
|
231
239
|
this.element = element;
|
@@ -246,25 +254,33 @@ class Action {
|
|
246
254
|
const eventTarget = this.eventTargetName ? `@${this.eventTargetName}` : "";
|
247
255
|
return `${this.eventName}${eventFilter}${eventTarget}->${this.identifier}#${this.methodName}`;
|
248
256
|
}
|
249
|
-
|
257
|
+
shouldIgnoreKeyboardEvent(event) {
|
250
258
|
if (!this.keyFilter) {
|
251
259
|
return false;
|
252
260
|
}
|
253
|
-
const
|
254
|
-
|
255
|
-
const [meta, ctrl, alt, shift] = modifiers.map((modifier) => filteres.includes(modifier));
|
256
|
-
if (event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift) {
|
261
|
+
const filters = this.keyFilter.split("+");
|
262
|
+
if (this.keyFilterDissatisfied(event, filters)) {
|
257
263
|
return true;
|
258
264
|
}
|
259
|
-
const standardFilter =
|
265
|
+
const standardFilter = filters.filter((key) => !allModifiers.includes(key))[0];
|
260
266
|
if (!standardFilter) {
|
261
267
|
return false;
|
262
268
|
}
|
263
|
-
if (!
|
269
|
+
if (!hasProperty(this.keyMappings, standardFilter)) {
|
264
270
|
error(`contains unknown key filter: ${this.keyFilter}`);
|
265
271
|
}
|
266
272
|
return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase();
|
267
273
|
}
|
274
|
+
shouldIgnoreMouseEvent(event) {
|
275
|
+
if (!this.keyFilter) {
|
276
|
+
return false;
|
277
|
+
}
|
278
|
+
const filters = [this.keyFilter];
|
279
|
+
if (this.keyFilterDissatisfied(event, filters)) {
|
280
|
+
return true;
|
281
|
+
}
|
282
|
+
return false;
|
283
|
+
}
|
268
284
|
get params() {
|
269
285
|
const params = {};
|
270
286
|
const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
|
@@ -283,6 +299,10 @@ class Action {
|
|
283
299
|
get keyMappings() {
|
284
300
|
return this.schema.keyMappings;
|
285
301
|
}
|
302
|
+
keyFilterDissatisfied(event, filters) {
|
303
|
+
const [meta, ctrl, alt, shift] = allModifiers.map((modifier) => filters.includes(modifier));
|
304
|
+
return event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift;
|
305
|
+
}
|
286
306
|
}
|
287
307
|
const defaultEventNames = {
|
288
308
|
a: () => "click",
|
@@ -329,8 +349,9 @@ class Binding {
|
|
329
349
|
return this.context.identifier;
|
330
350
|
}
|
331
351
|
handleEvent(event) {
|
332
|
-
|
333
|
-
|
352
|
+
const actionEvent = this.prepareActionEvent(event);
|
353
|
+
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(actionEvent)) {
|
354
|
+
this.invokeWithEvent(actionEvent);
|
334
355
|
}
|
335
356
|
}
|
336
357
|
get eventName() {
|
@@ -346,11 +367,12 @@ class Binding {
|
|
346
367
|
applyEventModifiers(event) {
|
347
368
|
const { element } = this.action;
|
348
369
|
const { actionDescriptorFilters } = this.context.application;
|
370
|
+
const { controller } = this.context;
|
349
371
|
let passes = true;
|
350
372
|
for (const [name, value] of Object.entries(this.eventOptions)) {
|
351
373
|
if (name in actionDescriptorFilters) {
|
352
374
|
const filter = actionDescriptorFilters[name];
|
353
|
-
passes = passes && filter({ name, value, event, element });
|
375
|
+
passes = passes && filter({ name, value, event, element, controller });
|
354
376
|
}
|
355
377
|
else {
|
356
378
|
continue;
|
@@ -358,12 +380,13 @@ class Binding {
|
|
358
380
|
}
|
359
381
|
return passes;
|
360
382
|
}
|
383
|
+
prepareActionEvent(event) {
|
384
|
+
return Object.assign(event, { params: this.action.params });
|
385
|
+
}
|
361
386
|
invokeWithEvent(event) {
|
362
387
|
const { target, currentTarget } = event;
|
363
388
|
try {
|
364
|
-
|
365
|
-
const actionEvent = Object.assign(event, { params });
|
366
|
-
this.method.call(this.controller, actionEvent);
|
389
|
+
this.method.call(this.controller, event);
|
367
390
|
this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName });
|
368
391
|
}
|
369
392
|
catch (error) {
|
@@ -374,7 +397,10 @@ class Binding {
|
|
374
397
|
}
|
375
398
|
willBeInvokedByEvent(event) {
|
376
399
|
const eventTarget = event.target;
|
377
|
-
if (event instanceof KeyboardEvent && this.action.
|
400
|
+
if (event instanceof KeyboardEvent && this.action.shouldIgnoreKeyboardEvent(event)) {
|
401
|
+
return false;
|
402
|
+
}
|
403
|
+
if (event instanceof MouseEvent && this.action.shouldIgnoreMouseEvent(event)) {
|
378
404
|
return false;
|
379
405
|
}
|
380
406
|
if (this.element === eventTarget) {
|
@@ -464,8 +490,7 @@ class ElementObserver {
|
|
464
490
|
this.processAddedNodes(mutation.addedNodes);
|
465
491
|
}
|
466
492
|
}
|
467
|
-
processAttributeChange(
|
468
|
-
const element = node;
|
493
|
+
processAttributeChange(element, attributeName) {
|
469
494
|
if (this.elements.has(element)) {
|
470
495
|
if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
|
471
496
|
this.delegate.elementAttributeChanged(element, attributeName);
|
@@ -681,8 +706,8 @@ class IndexedMultimap extends Multimap {
|
|
681
706
|
}
|
682
707
|
|
683
708
|
class SelectorObserver {
|
684
|
-
constructor(element, selector, delegate, details
|
685
|
-
this.
|
709
|
+
constructor(element, selector, delegate, details) {
|
710
|
+
this._selector = selector;
|
686
711
|
this.details = details;
|
687
712
|
this.elementObserver = new ElementObserver(element, this);
|
688
713
|
this.delegate = delegate;
|
@@ -691,6 +716,13 @@ class SelectorObserver {
|
|
691
716
|
get started() {
|
692
717
|
return this.elementObserver.started;
|
693
718
|
}
|
719
|
+
get selector() {
|
720
|
+
return this._selector;
|
721
|
+
}
|
722
|
+
set selector(selector) {
|
723
|
+
this._selector = selector;
|
724
|
+
this.refresh();
|
725
|
+
}
|
694
726
|
start() {
|
695
727
|
this.elementObserver.start();
|
696
728
|
}
|
@@ -707,39 +739,61 @@ class SelectorObserver {
|
|
707
739
|
return this.elementObserver.element;
|
708
740
|
}
|
709
741
|
matchElement(element) {
|
710
|
-
const
|
711
|
-
if (
|
712
|
-
|
742
|
+
const { selector } = this;
|
743
|
+
if (selector) {
|
744
|
+
const matches = element.matches(selector);
|
745
|
+
if (this.delegate.selectorMatchElement) {
|
746
|
+
return matches && this.delegate.selectorMatchElement(element, this.details);
|
747
|
+
}
|
748
|
+
return matches;
|
749
|
+
}
|
750
|
+
else {
|
751
|
+
return false;
|
713
752
|
}
|
714
|
-
return matches;
|
715
753
|
}
|
716
754
|
matchElementsInTree(tree) {
|
717
|
-
const
|
718
|
-
|
719
|
-
|
755
|
+
const { selector } = this;
|
756
|
+
if (selector) {
|
757
|
+
const match = this.matchElement(tree) ? [tree] : [];
|
758
|
+
const matches = Array.from(tree.querySelectorAll(selector)).filter((match) => this.matchElement(match));
|
759
|
+
return match.concat(matches);
|
760
|
+
}
|
761
|
+
else {
|
762
|
+
return [];
|
763
|
+
}
|
720
764
|
}
|
721
765
|
elementMatched(element) {
|
722
|
-
this
|
766
|
+
const { selector } = this;
|
767
|
+
if (selector) {
|
768
|
+
this.selectorMatched(element, selector);
|
769
|
+
}
|
723
770
|
}
|
724
771
|
elementUnmatched(element) {
|
725
|
-
this.
|
772
|
+
const selectors = this.matchesByElement.getKeysForValue(element);
|
773
|
+
for (const selector of selectors) {
|
774
|
+
this.selectorUnmatched(element, selector);
|
775
|
+
}
|
726
776
|
}
|
727
777
|
elementAttributeChanged(element, _attributeName) {
|
728
|
-
const
|
729
|
-
|
730
|
-
|
731
|
-
this.
|
778
|
+
const { selector } = this;
|
779
|
+
if (selector) {
|
780
|
+
const matches = this.matchElement(element);
|
781
|
+
const matchedBefore = this.matchesByElement.has(selector, element);
|
782
|
+
if (matches && !matchedBefore) {
|
783
|
+
this.selectorMatched(element, selector);
|
784
|
+
}
|
785
|
+
else if (!matches && matchedBefore) {
|
786
|
+
this.selectorUnmatched(element, selector);
|
787
|
+
}
|
732
788
|
}
|
733
789
|
}
|
734
|
-
selectorMatched(element) {
|
735
|
-
|
736
|
-
|
737
|
-
this.matchesByElement.add(this.selector, element);
|
738
|
-
}
|
790
|
+
selectorMatched(element, selector) {
|
791
|
+
this.delegate.selectorMatched(element, selector, this.details);
|
792
|
+
this.matchesByElement.add(selector, element);
|
739
793
|
}
|
740
|
-
selectorUnmatched(element) {
|
741
|
-
this.delegate.selectorUnmatched(element,
|
742
|
-
this.matchesByElement.delete(
|
794
|
+
selectorUnmatched(element, selector) {
|
795
|
+
this.delegate.selectorUnmatched(element, selector, this.details);
|
796
|
+
this.matchesByElement.delete(selector, element);
|
743
797
|
}
|
744
798
|
}
|
745
799
|
|
@@ -1236,34 +1290,47 @@ function getOwnStaticObjectPairs(constructor, propertyName) {
|
|
1236
1290
|
|
1237
1291
|
class OutletObserver {
|
1238
1292
|
constructor(context, delegate) {
|
1293
|
+
this.started = false;
|
1239
1294
|
this.context = context;
|
1240
1295
|
this.delegate = delegate;
|
1241
1296
|
this.outletsByName = new Multimap();
|
1242
1297
|
this.outletElementsByName = new Multimap();
|
1243
1298
|
this.selectorObserverMap = new Map();
|
1299
|
+
this.attributeObserverMap = new Map();
|
1244
1300
|
}
|
1245
1301
|
start() {
|
1246
|
-
if (this.
|
1302
|
+
if (!this.started) {
|
1247
1303
|
this.outletDefinitions.forEach((outletName) => {
|
1248
|
-
|
1249
|
-
|
1250
|
-
if (selector) {
|
1251
|
-
this.selectorObserverMap.set(outletName, new SelectorObserver(document.body, selector, this, details));
|
1252
|
-
}
|
1304
|
+
this.setupSelectorObserverForOutlet(outletName);
|
1305
|
+
this.setupAttributeObserverForOutlet(outletName);
|
1253
1306
|
});
|
1254
|
-
this.
|
1307
|
+
this.started = true;
|
1308
|
+
this.dependentContexts.forEach((context) => context.refresh());
|
1255
1309
|
}
|
1256
|
-
|
1310
|
+
}
|
1311
|
+
refresh() {
|
1312
|
+
this.selectorObserverMap.forEach((observer) => observer.refresh());
|
1313
|
+
this.attributeObserverMap.forEach((observer) => observer.refresh());
|
1257
1314
|
}
|
1258
1315
|
stop() {
|
1259
|
-
if (this.
|
1316
|
+
if (this.started) {
|
1317
|
+
this.started = false;
|
1260
1318
|
this.disconnectAllOutlets();
|
1319
|
+
this.stopSelectorObservers();
|
1320
|
+
this.stopAttributeObservers();
|
1321
|
+
}
|
1322
|
+
}
|
1323
|
+
stopSelectorObservers() {
|
1324
|
+
if (this.selectorObserverMap.size > 0) {
|
1261
1325
|
this.selectorObserverMap.forEach((observer) => observer.stop());
|
1262
1326
|
this.selectorObserverMap.clear();
|
1263
1327
|
}
|
1264
1328
|
}
|
1265
|
-
|
1266
|
-
this.
|
1329
|
+
stopAttributeObservers() {
|
1330
|
+
if (this.attributeObserverMap.size > 0) {
|
1331
|
+
this.attributeObserverMap.forEach((observer) => observer.stop());
|
1332
|
+
this.attributeObserverMap.clear();
|
1333
|
+
}
|
1267
1334
|
}
|
1268
1335
|
selectorMatched(element, _selector, { outletName }) {
|
1269
1336
|
const outlet = this.getOutlet(element, outletName);
|
@@ -1278,8 +1345,33 @@ class OutletObserver {
|
|
1278
1345
|
}
|
1279
1346
|
}
|
1280
1347
|
selectorMatchElement(element, { outletName }) {
|
1281
|
-
|
1282
|
-
|
1348
|
+
const selector = this.selector(outletName);
|
1349
|
+
const hasOutlet = this.hasOutlet(element, outletName);
|
1350
|
+
const hasOutletController = element.matches(`[${this.schema.controllerAttribute}~=${outletName}]`);
|
1351
|
+
if (selector) {
|
1352
|
+
return hasOutlet && hasOutletController && element.matches(selector);
|
1353
|
+
}
|
1354
|
+
else {
|
1355
|
+
return false;
|
1356
|
+
}
|
1357
|
+
}
|
1358
|
+
elementMatchedAttribute(_element, attributeName) {
|
1359
|
+
const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
|
1360
|
+
if (outletName) {
|
1361
|
+
this.updateSelectorObserverForOutlet(outletName);
|
1362
|
+
}
|
1363
|
+
}
|
1364
|
+
elementAttributeValueChanged(_element, attributeName) {
|
1365
|
+
const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
|
1366
|
+
if (outletName) {
|
1367
|
+
this.updateSelectorObserverForOutlet(outletName);
|
1368
|
+
}
|
1369
|
+
}
|
1370
|
+
elementUnmatchedAttribute(_element, attributeName) {
|
1371
|
+
const outletName = this.getOutletNameFromOutletAttributeName(attributeName);
|
1372
|
+
if (outletName) {
|
1373
|
+
this.updateSelectorObserverForOutlet(outletName);
|
1374
|
+
}
|
1283
1375
|
}
|
1284
1376
|
connectOutlet(outlet, element, outletName) {
|
1285
1377
|
var _a;
|
@@ -1307,9 +1399,33 @@ class OutletObserver {
|
|
1307
1399
|
}
|
1308
1400
|
}
|
1309
1401
|
}
|
1402
|
+
updateSelectorObserverForOutlet(outletName) {
|
1403
|
+
const observer = this.selectorObserverMap.get(outletName);
|
1404
|
+
if (observer) {
|
1405
|
+
observer.selector = this.selector(outletName);
|
1406
|
+
}
|
1407
|
+
}
|
1408
|
+
setupSelectorObserverForOutlet(outletName) {
|
1409
|
+
const selector = this.selector(outletName);
|
1410
|
+
const selectorObserver = new SelectorObserver(document.body, selector, this, { outletName });
|
1411
|
+
this.selectorObserverMap.set(outletName, selectorObserver);
|
1412
|
+
selectorObserver.start();
|
1413
|
+
}
|
1414
|
+
setupAttributeObserverForOutlet(outletName) {
|
1415
|
+
const attributeName = this.attributeNameForOutletName(outletName);
|
1416
|
+
const attributeObserver = new AttributeObserver(this.scope.element, attributeName, this);
|
1417
|
+
this.attributeObserverMap.set(outletName, attributeObserver);
|
1418
|
+
attributeObserver.start();
|
1419
|
+
}
|
1310
1420
|
selector(outletName) {
|
1311
1421
|
return this.scope.outlets.getSelectorForOutletName(outletName);
|
1312
1422
|
}
|
1423
|
+
attributeNameForOutletName(outletName) {
|
1424
|
+
return this.scope.schema.outletAttributeForScope(this.identifier, outletName);
|
1425
|
+
}
|
1426
|
+
getOutletNameFromOutletAttributeName(attributeName) {
|
1427
|
+
return this.outletDefinitions.find((outletName) => this.attributeNameForOutletName(outletName) === attributeName);
|
1428
|
+
}
|
1313
1429
|
get outletDependencies() {
|
1314
1430
|
const dependencies = new Multimap();
|
1315
1431
|
this.router.modules.forEach((module) => {
|
@@ -1341,6 +1457,9 @@ class OutletObserver {
|
|
1341
1457
|
get scope() {
|
1342
1458
|
return this.context.scope;
|
1343
1459
|
}
|
1460
|
+
get schema() {
|
1461
|
+
return this.context.schema;
|
1462
|
+
}
|
1344
1463
|
get identifier() {
|
1345
1464
|
return this.context.identifier;
|
1346
1465
|
}
|
@@ -1828,6 +1947,9 @@ class ScopeObserver {
|
|
1828
1947
|
}
|
1829
1948
|
parseValueForToken(token) {
|
1830
1949
|
const { element, content: identifier } = token;
|
1950
|
+
return this.parseValueForElementAndIdentifier(element, identifier);
|
1951
|
+
}
|
1952
|
+
parseValueForElementAndIdentifier(element, identifier) {
|
1831
1953
|
const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element);
|
1832
1954
|
let scope = scopesByIdentifier.get(identifier);
|
1833
1955
|
if (!scope) {
|
@@ -1899,7 +2021,7 @@ class Router {
|
|
1899
2021
|
this.connectModule(module);
|
1900
2022
|
const afterLoad = definition.controllerConstructor.afterLoad;
|
1901
2023
|
if (afterLoad) {
|
1902
|
-
afterLoad(definition.identifier, this.application);
|
2024
|
+
afterLoad.call(definition.controllerConstructor, definition.identifier, this.application);
|
1903
2025
|
}
|
1904
2026
|
}
|
1905
2027
|
unloadIdentifier(identifier) {
|
@@ -1914,6 +2036,15 @@ class Router {
|
|
1914
2036
|
return module.contexts.find((context) => context.element == element);
|
1915
2037
|
}
|
1916
2038
|
}
|
2039
|
+
proposeToConnectScopeForElementAndIdentifier(element, identifier) {
|
2040
|
+
const scope = this.scopeObserver.parseValueForElementAndIdentifier(element, identifier);
|
2041
|
+
if (scope) {
|
2042
|
+
this.scopeObserver.elementMatchedValue(scope.element, scope);
|
2043
|
+
}
|
2044
|
+
else {
|
2045
|
+
console.error(`Couldn't find or create scope for identifier: "${identifier}" and element:`, element);
|
2046
|
+
}
|
2047
|
+
}
|
1917
2048
|
handleError(error, message, detail) {
|
1918
2049
|
this.application.handleError(error, message, detail);
|
1919
2050
|
}
|
@@ -1952,7 +2083,7 @@ const defaultSchema = {
|
|
1952
2083
|
targetAttribute: "data-target",
|
1953
2084
|
targetAttributeForScope: (identifier) => `data-${identifier}-target`,
|
1954
2085
|
outletAttributeForScope: (identifier, outlet) => `data-${identifier}-${outlet}-outlet`,
|
1955
|
-
keyMappings: Object.assign(Object.assign({ enter: "Enter", tab: "Tab", esc: "Escape", space: " ", up: "ArrowUp", down: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", home: "Home", end: "End" }, objectFromEntries("abcdefghijklmnopqrstuvwxyz".split("").map((c) => [c, c]))), objectFromEntries("0123456789".split("").map((n) => [n, n]))),
|
2086
|
+
keyMappings: Object.assign(Object.assign({ enter: "Enter", tab: "Tab", esc: "Escape", space: " ", up: "ArrowUp", down: "ArrowDown", left: "ArrowLeft", right: "ArrowRight", home: "Home", end: "End", page_up: "PageUp", page_down: "PageDown" }, objectFromEntries("abcdefghijklmnopqrstuvwxyz".split("").map((c) => [c, c]))), objectFromEntries("0123456789".split("").map((n) => [n, n]))),
|
1956
2087
|
};
|
1957
2088
|
function objectFromEntries(array) {
|
1958
2089
|
return array.reduce((memo, [k, v]) => (Object.assign(Object.assign({}, memo), { [k]: v })), {});
|
@@ -2078,22 +2209,32 @@ function OutletPropertiesBlessing(constructor) {
|
|
2078
2209
|
return Object.assign(properties, propertiesForOutletDefinition(outletDefinition));
|
2079
2210
|
}, {});
|
2080
2211
|
}
|
2212
|
+
function getOutletController(controller, element, identifier) {
|
2213
|
+
return controller.application.getControllerForElementAndIdentifier(element, identifier);
|
2214
|
+
}
|
2215
|
+
function getControllerAndEnsureConnectedScope(controller, element, outletName) {
|
2216
|
+
let outletController = getOutletController(controller, element, outletName);
|
2217
|
+
if (outletController)
|
2218
|
+
return outletController;
|
2219
|
+
controller.application.router.proposeToConnectScopeForElementAndIdentifier(element, outletName);
|
2220
|
+
outletController = getOutletController(controller, element, outletName);
|
2221
|
+
if (outletController)
|
2222
|
+
return outletController;
|
2223
|
+
}
|
2081
2224
|
function propertiesForOutletDefinition(name) {
|
2082
2225
|
const camelizedName = namespaceCamelize(name);
|
2083
2226
|
return {
|
2084
2227
|
[`${camelizedName}Outlet`]: {
|
2085
2228
|
get() {
|
2086
|
-
const
|
2087
|
-
|
2088
|
-
|
2089
|
-
|
2229
|
+
const outletElement = this.outlets.find(name);
|
2230
|
+
const selector = this.outlets.getSelectorForOutletName(name);
|
2231
|
+
if (outletElement) {
|
2232
|
+
const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name);
|
2233
|
+
if (outletController)
|
2090
2234
|
return outletController;
|
2091
|
-
}
|
2092
|
-
else {
|
2093
|
-
throw new Error(`Missing "data-controller=${name}" attribute on outlet element for "${this.identifier}" controller`);
|
2094
|
-
}
|
2235
|
+
throw new Error(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`);
|
2095
2236
|
}
|
2096
|
-
throw new Error(`Missing outlet element "${name}" for "${this.identifier}"
|
2237
|
+
throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`);
|
2097
2238
|
},
|
2098
2239
|
},
|
2099
2240
|
[`${camelizedName}Outlets`]: {
|
@@ -2101,14 +2242,11 @@ function propertiesForOutletDefinition(name) {
|
|
2101
2242
|
const outlets = this.outlets.findAll(name);
|
2102
2243
|
if (outlets.length > 0) {
|
2103
2244
|
return outlets
|
2104
|
-
.map((
|
2105
|
-
const
|
2106
|
-
if (
|
2107
|
-
return
|
2108
|
-
}
|
2109
|
-
else {
|
2110
|
-
console.warn(`The provided outlet element is missing the outlet controller "${name}" for "${this.identifier}"`, outlet);
|
2111
|
-
}
|
2245
|
+
.map((outletElement) => {
|
2246
|
+
const outletController = getControllerAndEnsureConnectedScope(this, outletElement, name);
|
2247
|
+
if (outletController)
|
2248
|
+
return outletController;
|
2249
|
+
console.warn(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`, outletElement);
|
2112
2250
|
})
|
2113
2251
|
.filter((controller) => controller);
|
2114
2252
|
}
|
@@ -2117,12 +2255,13 @@ function propertiesForOutletDefinition(name) {
|
|
2117
2255
|
},
|
2118
2256
|
[`${camelizedName}OutletElement`]: {
|
2119
2257
|
get() {
|
2120
|
-
const
|
2121
|
-
|
2122
|
-
|
2258
|
+
const outletElement = this.outlets.find(name);
|
2259
|
+
const selector = this.outlets.getSelectorForOutletName(name);
|
2260
|
+
if (outletElement) {
|
2261
|
+
return outletElement;
|
2123
2262
|
}
|
2124
2263
|
else {
|
2125
|
-
throw new Error(`Missing outlet element "${name}" for "${this.identifier}"
|
2264
|
+
throw new Error(`Missing outlet element "${name}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${selector}".`);
|
2126
2265
|
}
|
2127
2266
|
},
|
2128
2267
|
},
|
@@ -2254,51 +2393,67 @@ function parseValueTypeDefault(defaultValue) {
|
|
2254
2393
|
return "object";
|
2255
2394
|
}
|
2256
2395
|
function parseValueTypeObject(payload) {
|
2257
|
-
const
|
2258
|
-
|
2259
|
-
|
2260
|
-
const
|
2261
|
-
|
2262
|
-
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2396
|
+
const { controller, token, typeObject } = payload;
|
2397
|
+
const hasType = isSomething(typeObject.type);
|
2398
|
+
const hasDefault = isSomething(typeObject.default);
|
2399
|
+
const fullObject = hasType && hasDefault;
|
2400
|
+
const onlyType = hasType && !hasDefault;
|
2401
|
+
const onlyDefault = !hasType && hasDefault;
|
2402
|
+
const typeFromObject = parseValueTypeConstant(typeObject.type);
|
2403
|
+
const typeFromDefaultValue = parseValueTypeDefault(payload.typeObject.default);
|
2404
|
+
if (onlyType)
|
2405
|
+
return typeFromObject;
|
2406
|
+
if (onlyDefault)
|
2407
|
+
return typeFromDefaultValue;
|
2408
|
+
if (typeFromObject !== typeFromDefaultValue) {
|
2409
|
+
const propertyPath = controller ? `${controller}.${token}` : token;
|
2410
|
+
throw new Error(`The specified default value for the Stimulus Value "${propertyPath}" must match the defined type "${typeFromObject}". The provided default value of "${typeObject.default}" is of type "${typeFromDefaultValue}".`);
|
2411
|
+
}
|
2412
|
+
if (fullObject)
|
2413
|
+
return typeFromObject;
|
2266
2414
|
}
|
2267
2415
|
function parseValueTypeDefinition(payload) {
|
2268
|
-
const
|
2269
|
-
|
2270
|
-
|
2271
|
-
|
2272
|
-
|
2273
|
-
const typeFromDefaultValue = parseValueTypeDefault(payload.typeDefinition);
|
2274
|
-
const typeFromConstant = parseValueTypeConstant(payload.typeDefinition);
|
2416
|
+
const { controller, token, typeDefinition } = payload;
|
2417
|
+
const typeObject = { controller, token, typeObject: typeDefinition };
|
2418
|
+
const typeFromObject = parseValueTypeObject(typeObject);
|
2419
|
+
const typeFromDefaultValue = parseValueTypeDefault(typeDefinition);
|
2420
|
+
const typeFromConstant = parseValueTypeConstant(typeDefinition);
|
2275
2421
|
const type = typeFromObject || typeFromDefaultValue || typeFromConstant;
|
2276
2422
|
if (type)
|
2277
2423
|
return type;
|
2278
|
-
const propertyPath =
|
2279
|
-
throw new Error(`Unknown value type "${propertyPath}" for "${
|
2424
|
+
const propertyPath = controller ? `${controller}.${typeDefinition}` : token;
|
2425
|
+
throw new Error(`Unknown value type "${propertyPath}" for "${token}" value`);
|
2280
2426
|
}
|
2281
2427
|
function defaultValueForDefinition(typeDefinition) {
|
2282
2428
|
const constant = parseValueTypeConstant(typeDefinition);
|
2283
2429
|
if (constant)
|
2284
2430
|
return defaultValuesByType[constant];
|
2285
|
-
const
|
2286
|
-
|
2287
|
-
|
2431
|
+
const hasDefault = hasProperty(typeDefinition, "default");
|
2432
|
+
const hasType = hasProperty(typeDefinition, "type");
|
2433
|
+
const typeObject = typeDefinition;
|
2434
|
+
if (hasDefault)
|
2435
|
+
return typeObject.default;
|
2436
|
+
if (hasType) {
|
2437
|
+
const { type } = typeObject;
|
2438
|
+
const constantFromType = parseValueTypeConstant(type);
|
2439
|
+
if (constantFromType)
|
2440
|
+
return defaultValuesByType[constantFromType];
|
2441
|
+
}
|
2288
2442
|
return typeDefinition;
|
2289
2443
|
}
|
2290
2444
|
function valueDescriptorForTokenAndTypeDefinition(payload) {
|
2291
|
-
const
|
2445
|
+
const { token, typeDefinition } = payload;
|
2446
|
+
const key = `${dasherize(token)}-value`;
|
2292
2447
|
const type = parseValueTypeDefinition(payload);
|
2293
2448
|
return {
|
2294
2449
|
type,
|
2295
2450
|
key,
|
2296
2451
|
name: camelize(key),
|
2297
2452
|
get defaultValue() {
|
2298
|
-
return defaultValueForDefinition(
|
2453
|
+
return defaultValueForDefinition(typeDefinition);
|
2299
2454
|
},
|
2300
2455
|
get hasCustomDefaultValue() {
|
2301
|
-
return parseValueTypeDefault(
|
2456
|
+
return parseValueTypeDefault(typeDefinition) !== undefined;
|
2302
2457
|
},
|
2303
2458
|
reader: readers[type],
|
2304
2459
|
writer: writers[type] || writers.default,
|
@@ -2327,7 +2482,7 @@ const readers = {
|
|
2327
2482
|
return !(value == "0" || String(value).toLowerCase() == "false");
|
2328
2483
|
},
|
2329
2484
|
number(value) {
|
2330
|
-
return Number(value);
|
2485
|
+
return Number(value.replace(/_/g, ""));
|
2331
2486
|
},
|
2332
2487
|
object(value) {
|
2333
2488
|
const object = JSON.parse(value);
|
@@ -2392,7 +2547,7 @@ class Controller {
|
|
2392
2547
|
}
|
2393
2548
|
disconnect() {
|
2394
2549
|
}
|
2395
|
-
dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true } = {}) {
|
2550
|
+
dispatch(eventName, { target = this.element, detail = {}, prefix = this.identifier, bubbles = true, cancelable = true, } = {}) {
|
2396
2551
|
const type = prefix ? `${prefix}:${eventName}` : eventName;
|
2397
2552
|
const event = new CustomEvent(type, { detail, bubbles, cancelable });
|
2398
2553
|
target.dispatchEvent(event);
|