stimulus-rails 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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);
|