stimulus-rails 1.2.0 → 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 +272 -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,17 +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[2];
|
177
|
+
let keyFilter = matches[3];
|
178
|
+
if (keyFilter && !["keydown", "keyup", "keypress"].includes(eventName)) {
|
179
|
+
eventName += `.${keyFilter}`;
|
180
|
+
keyFilter = "";
|
181
|
+
}
|
176
182
|
return {
|
177
|
-
eventTarget: parseEventTarget(matches[
|
178
|
-
eventName
|
179
|
-
eventOptions: matches[
|
180
|
-
identifier: matches[
|
181
|
-
methodName: matches[
|
182
|
-
keyFilter: matches[
|
183
|
+
eventTarget: parseEventTarget(matches[4]),
|
184
|
+
eventName,
|
185
|
+
eventOptions: matches[7] ? parseEventOptions(matches[7]) : {},
|
186
|
+
identifier: matches[5],
|
187
|
+
methodName: matches[6],
|
188
|
+
keyFilter: matches[1] || keyFilter,
|
183
189
|
};
|
184
190
|
}
|
185
191
|
function parseEventTarget(eventTargetName) {
|
@@ -220,6 +226,14 @@ function tokenize(value) {
|
|
220
226
|
return value.match(/[^\s]+/g) || [];
|
221
227
|
}
|
222
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"];
|
223
237
|
class Action {
|
224
238
|
constructor(element, index, descriptor, schema) {
|
225
239
|
this.element = element;
|
@@ -240,25 +254,33 @@ class Action {
|
|
240
254
|
const eventTarget = this.eventTargetName ? `@${this.eventTargetName}` : "";
|
241
255
|
return `${this.eventName}${eventFilter}${eventTarget}->${this.identifier}#${this.methodName}`;
|
242
256
|
}
|
243
|
-
|
257
|
+
shouldIgnoreKeyboardEvent(event) {
|
244
258
|
if (!this.keyFilter) {
|
245
259
|
return false;
|
246
260
|
}
|
247
|
-
const
|
248
|
-
|
249
|
-
const [meta, ctrl, alt, shift] = modifiers.map((modifier) => filteres.includes(modifier));
|
250
|
-
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)) {
|
251
263
|
return true;
|
252
264
|
}
|
253
|
-
const standardFilter =
|
265
|
+
const standardFilter = filters.filter((key) => !allModifiers.includes(key))[0];
|
254
266
|
if (!standardFilter) {
|
255
267
|
return false;
|
256
268
|
}
|
257
|
-
if (!
|
258
|
-
error(`contains
|
269
|
+
if (!hasProperty(this.keyMappings, standardFilter)) {
|
270
|
+
error(`contains unknown key filter: ${this.keyFilter}`);
|
259
271
|
}
|
260
272
|
return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase();
|
261
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
|
+
}
|
262
284
|
get params() {
|
263
285
|
const params = {};
|
264
286
|
const pattern = new RegExp(`^data-${this.identifier}-(.+)-param$`, "i");
|
@@ -277,6 +299,10 @@ class Action {
|
|
277
299
|
get keyMappings() {
|
278
300
|
return this.schema.keyMappings;
|
279
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
|
+
}
|
280
306
|
}
|
281
307
|
const defaultEventNames = {
|
282
308
|
a: () => "click",
|
@@ -323,8 +349,9 @@ class Binding {
|
|
323
349
|
return this.context.identifier;
|
324
350
|
}
|
325
351
|
handleEvent(event) {
|
326
|
-
|
327
|
-
|
352
|
+
const actionEvent = this.prepareActionEvent(event);
|
353
|
+
if (this.willBeInvokedByEvent(event) && this.applyEventModifiers(actionEvent)) {
|
354
|
+
this.invokeWithEvent(actionEvent);
|
328
355
|
}
|
329
356
|
}
|
330
357
|
get eventName() {
|
@@ -340,11 +367,12 @@ class Binding {
|
|
340
367
|
applyEventModifiers(event) {
|
341
368
|
const { element } = this.action;
|
342
369
|
const { actionDescriptorFilters } = this.context.application;
|
370
|
+
const { controller } = this.context;
|
343
371
|
let passes = true;
|
344
372
|
for (const [name, value] of Object.entries(this.eventOptions)) {
|
345
373
|
if (name in actionDescriptorFilters) {
|
346
374
|
const filter = actionDescriptorFilters[name];
|
347
|
-
passes = passes && filter({ name, value, event, element });
|
375
|
+
passes = passes && filter({ name, value, event, element, controller });
|
348
376
|
}
|
349
377
|
else {
|
350
378
|
continue;
|
@@ -352,12 +380,13 @@ class Binding {
|
|
352
380
|
}
|
353
381
|
return passes;
|
354
382
|
}
|
383
|
+
prepareActionEvent(event) {
|
384
|
+
return Object.assign(event, { params: this.action.params });
|
385
|
+
}
|
355
386
|
invokeWithEvent(event) {
|
356
387
|
const { target, currentTarget } = event;
|
357
388
|
try {
|
358
|
-
|
359
|
-
const actionEvent = Object.assign(event, { params });
|
360
|
-
this.method.call(this.controller, actionEvent);
|
389
|
+
this.method.call(this.controller, event);
|
361
390
|
this.context.logDebugActivity(this.methodName, { event, target, currentTarget, action: this.methodName });
|
362
391
|
}
|
363
392
|
catch (error) {
|
@@ -368,7 +397,10 @@ class Binding {
|
|
368
397
|
}
|
369
398
|
willBeInvokedByEvent(event) {
|
370
399
|
const eventTarget = event.target;
|
371
|
-
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)) {
|
372
404
|
return false;
|
373
405
|
}
|
374
406
|
if (this.element === eventTarget) {
|
@@ -458,8 +490,7 @@ class ElementObserver {
|
|
458
490
|
this.processAddedNodes(mutation.addedNodes);
|
459
491
|
}
|
460
492
|
}
|
461
|
-
processAttributeChange(
|
462
|
-
const element = node;
|
493
|
+
processAttributeChange(element, attributeName) {
|
463
494
|
if (this.elements.has(element)) {
|
464
495
|
if (this.delegate.elementAttributeChanged && this.matchElement(element)) {
|
465
496
|
this.delegate.elementAttributeChanged(element, attributeName);
|
@@ -675,8 +706,8 @@ class IndexedMultimap extends Multimap {
|
|
675
706
|
}
|
676
707
|
|
677
708
|
class SelectorObserver {
|
678
|
-
constructor(element, selector, delegate, details
|
679
|
-
this.
|
709
|
+
constructor(element, selector, delegate, details) {
|
710
|
+
this._selector = selector;
|
680
711
|
this.details = details;
|
681
712
|
this.elementObserver = new ElementObserver(element, this);
|
682
713
|
this.delegate = delegate;
|
@@ -685,6 +716,13 @@ class SelectorObserver {
|
|
685
716
|
get started() {
|
686
717
|
return this.elementObserver.started;
|
687
718
|
}
|
719
|
+
get selector() {
|
720
|
+
return this._selector;
|
721
|
+
}
|
722
|
+
set selector(selector) {
|
723
|
+
this._selector = selector;
|
724
|
+
this.refresh();
|
725
|
+
}
|
688
726
|
start() {
|
689
727
|
this.elementObserver.start();
|
690
728
|
}
|
@@ -701,39 +739,61 @@ class SelectorObserver {
|
|
701
739
|
return this.elementObserver.element;
|
702
740
|
}
|
703
741
|
matchElement(element) {
|
704
|
-
const
|
705
|
-
if (
|
706
|
-
|
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;
|
707
752
|
}
|
708
|
-
return matches;
|
709
753
|
}
|
710
754
|
matchElementsInTree(tree) {
|
711
|
-
const
|
712
|
-
|
713
|
-
|
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
|
+
}
|
714
764
|
}
|
715
765
|
elementMatched(element) {
|
716
|
-
this
|
766
|
+
const { selector } = this;
|
767
|
+
if (selector) {
|
768
|
+
this.selectorMatched(element, selector);
|
769
|
+
}
|
717
770
|
}
|
718
771
|
elementUnmatched(element) {
|
719
|
-
this.
|
772
|
+
const selectors = this.matchesByElement.getKeysForValue(element);
|
773
|
+
for (const selector of selectors) {
|
774
|
+
this.selectorUnmatched(element, selector);
|
775
|
+
}
|
720
776
|
}
|
721
777
|
elementAttributeChanged(element, _attributeName) {
|
722
|
-
const
|
723
|
-
|
724
|
-
|
725
|
-
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
|
+
}
|
726
788
|
}
|
727
789
|
}
|
728
|
-
selectorMatched(element) {
|
729
|
-
|
730
|
-
|
731
|
-
this.matchesByElement.add(this.selector, element);
|
732
|
-
}
|
790
|
+
selectorMatched(element, selector) {
|
791
|
+
this.delegate.selectorMatched(element, selector, this.details);
|
792
|
+
this.matchesByElement.add(selector, element);
|
733
793
|
}
|
734
|
-
selectorUnmatched(element) {
|
735
|
-
this.delegate.selectorUnmatched(element,
|
736
|
-
this.matchesByElement.delete(
|
794
|
+
selectorUnmatched(element, selector) {
|
795
|
+
this.delegate.selectorUnmatched(element, selector, this.details);
|
796
|
+
this.matchesByElement.delete(selector, element);
|
737
797
|
}
|
738
798
|
}
|
739
799
|
|
@@ -1230,34 +1290,47 @@ function getOwnStaticObjectPairs(constructor, propertyName) {
|
|
1230
1290
|
|
1231
1291
|
class OutletObserver {
|
1232
1292
|
constructor(context, delegate) {
|
1293
|
+
this.started = false;
|
1233
1294
|
this.context = context;
|
1234
1295
|
this.delegate = delegate;
|
1235
1296
|
this.outletsByName = new Multimap();
|
1236
1297
|
this.outletElementsByName = new Multimap();
|
1237
1298
|
this.selectorObserverMap = new Map();
|
1299
|
+
this.attributeObserverMap = new Map();
|
1238
1300
|
}
|
1239
1301
|
start() {
|
1240
|
-
if (this.
|
1302
|
+
if (!this.started) {
|
1241
1303
|
this.outletDefinitions.forEach((outletName) => {
|
1242
|
-
|
1243
|
-
|
1244
|
-
if (selector) {
|
1245
|
-
this.selectorObserverMap.set(outletName, new SelectorObserver(document.body, selector, this, details));
|
1246
|
-
}
|
1304
|
+
this.setupSelectorObserverForOutlet(outletName);
|
1305
|
+
this.setupAttributeObserverForOutlet(outletName);
|
1247
1306
|
});
|
1248
|
-
this.
|
1307
|
+
this.started = true;
|
1308
|
+
this.dependentContexts.forEach((context) => context.refresh());
|
1249
1309
|
}
|
1250
|
-
|
1310
|
+
}
|
1311
|
+
refresh() {
|
1312
|
+
this.selectorObserverMap.forEach((observer) => observer.refresh());
|
1313
|
+
this.attributeObserverMap.forEach((observer) => observer.refresh());
|
1251
1314
|
}
|
1252
1315
|
stop() {
|
1253
|
-
if (this.
|
1316
|
+
if (this.started) {
|
1317
|
+
this.started = false;
|
1254
1318
|
this.disconnectAllOutlets();
|
1319
|
+
this.stopSelectorObservers();
|
1320
|
+
this.stopAttributeObservers();
|
1321
|
+
}
|
1322
|
+
}
|
1323
|
+
stopSelectorObservers() {
|
1324
|
+
if (this.selectorObserverMap.size > 0) {
|
1255
1325
|
this.selectorObserverMap.forEach((observer) => observer.stop());
|
1256
1326
|
this.selectorObserverMap.clear();
|
1257
1327
|
}
|
1258
1328
|
}
|
1259
|
-
|
1260
|
-
this.
|
1329
|
+
stopAttributeObservers() {
|
1330
|
+
if (this.attributeObserverMap.size > 0) {
|
1331
|
+
this.attributeObserverMap.forEach((observer) => observer.stop());
|
1332
|
+
this.attributeObserverMap.clear();
|
1333
|
+
}
|
1261
1334
|
}
|
1262
1335
|
selectorMatched(element, _selector, { outletName }) {
|
1263
1336
|
const outlet = this.getOutlet(element, outletName);
|
@@ -1272,8 +1345,33 @@ class OutletObserver {
|
|
1272
1345
|
}
|
1273
1346
|
}
|
1274
1347
|
selectorMatchElement(element, { outletName }) {
|
1275
|
-
|
1276
|
-
|
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
|
+
}
|
1277
1375
|
}
|
1278
1376
|
connectOutlet(outlet, element, outletName) {
|
1279
1377
|
var _a;
|
@@ -1301,9 +1399,33 @@ class OutletObserver {
|
|
1301
1399
|
}
|
1302
1400
|
}
|
1303
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
|
+
}
|
1304
1420
|
selector(outletName) {
|
1305
1421
|
return this.scope.outlets.getSelectorForOutletName(outletName);
|
1306
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
|
+
}
|
1307
1429
|
get outletDependencies() {
|
1308
1430
|
const dependencies = new Multimap();
|
1309
1431
|
this.router.modules.forEach((module) => {
|
@@ -1335,6 +1457,9 @@ class OutletObserver {
|
|
1335
1457
|
get scope() {
|
1336
1458
|
return this.context.scope;
|
1337
1459
|
}
|
1460
|
+
get schema() {
|
1461
|
+
return this.context.schema;
|
1462
|
+
}
|
1338
1463
|
get identifier() {
|
1339
1464
|
return this.context.identifier;
|
1340
1465
|
}
|
@@ -1822,6 +1947,9 @@ class ScopeObserver {
|
|
1822
1947
|
}
|
1823
1948
|
parseValueForToken(token) {
|
1824
1949
|
const { element, content: identifier } = token;
|
1950
|
+
return this.parseValueForElementAndIdentifier(element, identifier);
|
1951
|
+
}
|
1952
|
+
parseValueForElementAndIdentifier(element, identifier) {
|
1825
1953
|
const scopesByIdentifier = this.fetchScopesByIdentifierForElement(element);
|
1826
1954
|
let scope = scopesByIdentifier.get(identifier);
|
1827
1955
|
if (!scope) {
|
@@ -1893,7 +2021,7 @@ class Router {
|
|
1893
2021
|
this.connectModule(module);
|
1894
2022
|
const afterLoad = definition.controllerConstructor.afterLoad;
|
1895
2023
|
if (afterLoad) {
|
1896
|
-
afterLoad(definition.identifier, this.application);
|
2024
|
+
afterLoad.call(definition.controllerConstructor, definition.identifier, this.application);
|
1897
2025
|
}
|
1898
2026
|
}
|
1899
2027
|
unloadIdentifier(identifier) {
|
@@ -1908,6 +2036,15 @@ class Router {
|
|
1908
2036
|
return module.contexts.find((context) => context.element == element);
|
1909
2037
|
}
|
1910
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
|
+
}
|
1911
2048
|
handleError(error, message, detail) {
|
1912
2049
|
this.application.handleError(error, message, detail);
|
1913
2050
|
}
|
@@ -1946,7 +2083,7 @@ const defaultSchema = {
|
|
1946
2083
|
targetAttribute: "data-target",
|
1947
2084
|
targetAttributeForScope: (identifier) => `data-${identifier}-target`,
|
1948
2085
|
outletAttributeForScope: (identifier, outlet) => `data-${identifier}-${outlet}-outlet`,
|
1949
|
-
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]))),
|
1950
2087
|
};
|
1951
2088
|
function objectFromEntries(array) {
|
1952
2089
|
return array.reduce((memo, [k, v]) => (Object.assign(Object.assign({}, memo), { [k]: v })), {});
|
@@ -2072,22 +2209,32 @@ function OutletPropertiesBlessing(constructor) {
|
|
2072
2209
|
return Object.assign(properties, propertiesForOutletDefinition(outletDefinition));
|
2073
2210
|
}, {});
|
2074
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
|
+
}
|
2075
2224
|
function propertiesForOutletDefinition(name) {
|
2076
2225
|
const camelizedName = namespaceCamelize(name);
|
2077
2226
|
return {
|
2078
2227
|
[`${camelizedName}Outlet`]: {
|
2079
2228
|
get() {
|
2080
|
-
const
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
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)
|
2084
2234
|
return outletController;
|
2085
|
-
}
|
2086
|
-
else {
|
2087
|
-
throw new Error(`Missing "data-controller=${name}" attribute on outlet element for "${this.identifier}" controller`);
|
2088
|
-
}
|
2235
|
+
throw new Error(`The provided outlet element is missing an outlet controller "${name}" instance for host controller "${this.identifier}"`);
|
2089
2236
|
}
|
2090
|
-
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}".`);
|
2091
2238
|
},
|
2092
2239
|
},
|
2093
2240
|
[`${camelizedName}Outlets`]: {
|
@@ -2095,14 +2242,11 @@ function propertiesForOutletDefinition(name) {
|
|
2095
2242
|
const outlets = this.outlets.findAll(name);
|
2096
2243
|
if (outlets.length > 0) {
|
2097
2244
|
return outlets
|
2098
|
-
.map((
|
2099
|
-
const
|
2100
|
-
if (
|
2101
|
-
return
|
2102
|
-
}
|
2103
|
-
else {
|
2104
|
-
console.warn(`The provided outlet element is missing the outlet controller "${name}" for "${this.identifier}"`, outlet);
|
2105
|
-
}
|
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);
|
2106
2250
|
})
|
2107
2251
|
.filter((controller) => controller);
|
2108
2252
|
}
|
@@ -2111,12 +2255,13 @@ function propertiesForOutletDefinition(name) {
|
|
2111
2255
|
},
|
2112
2256
|
[`${camelizedName}OutletElement`]: {
|
2113
2257
|
get() {
|
2114
|
-
const
|
2115
|
-
|
2116
|
-
|
2258
|
+
const outletElement = this.outlets.find(name);
|
2259
|
+
const selector = this.outlets.getSelectorForOutletName(name);
|
2260
|
+
if (outletElement) {
|
2261
|
+
return outletElement;
|
2117
2262
|
}
|
2118
2263
|
else {
|
2119
|
-
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}".`);
|
2120
2265
|
}
|
2121
2266
|
},
|
2122
2267
|
},
|
@@ -2248,51 +2393,67 @@ function parseValueTypeDefault(defaultValue) {
|
|
2248
2393
|
return "object";
|
2249
2394
|
}
|
2250
2395
|
function parseValueTypeObject(payload) {
|
2251
|
-
const
|
2252
|
-
|
2253
|
-
|
2254
|
-
const
|
2255
|
-
|
2256
|
-
|
2257
|
-
|
2258
|
-
|
2259
|
-
|
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;
|
2260
2414
|
}
|
2261
2415
|
function parseValueTypeDefinition(payload) {
|
2262
|
-
const
|
2263
|
-
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2267
|
-
const typeFromDefaultValue = parseValueTypeDefault(payload.typeDefinition);
|
2268
|
-
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);
|
2269
2421
|
const type = typeFromObject || typeFromDefaultValue || typeFromConstant;
|
2270
2422
|
if (type)
|
2271
2423
|
return type;
|
2272
|
-
const propertyPath =
|
2273
|
-
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`);
|
2274
2426
|
}
|
2275
2427
|
function defaultValueForDefinition(typeDefinition) {
|
2276
2428
|
const constant = parseValueTypeConstant(typeDefinition);
|
2277
2429
|
if (constant)
|
2278
2430
|
return defaultValuesByType[constant];
|
2279
|
-
const
|
2280
|
-
|
2281
|
-
|
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
|
+
}
|
2282
2442
|
return typeDefinition;
|
2283
2443
|
}
|
2284
2444
|
function valueDescriptorForTokenAndTypeDefinition(payload) {
|
2285
|
-
const
|
2445
|
+
const { token, typeDefinition } = payload;
|
2446
|
+
const key = `${dasherize(token)}-value`;
|
2286
2447
|
const type = parseValueTypeDefinition(payload);
|
2287
2448
|
return {
|
2288
2449
|
type,
|
2289
2450
|
key,
|
2290
2451
|
name: camelize(key),
|
2291
2452
|
get defaultValue() {
|
2292
|
-
return defaultValueForDefinition(
|
2453
|
+
return defaultValueForDefinition(typeDefinition);
|
2293
2454
|
},
|
2294
2455
|
get hasCustomDefaultValue() {
|
2295
|
-
return parseValueTypeDefault(
|
2456
|
+
return parseValueTypeDefault(typeDefinition) !== undefined;
|
2296
2457
|
},
|
2297
2458
|
reader: readers[type],
|
2298
2459
|
writer: writers[type] || writers.default,
|
@@ -2321,7 +2482,7 @@ const readers = {
|
|
2321
2482
|
return !(value == "0" || String(value).toLowerCase() == "false");
|
2322
2483
|
},
|
2323
2484
|
number(value) {
|
2324
|
-
return Number(value);
|
2485
|
+
return Number(value.replace(/_/g, ""));
|
2325
2486
|
},
|
2326
2487
|
object(value) {
|
2327
2488
|
const object = JSON.parse(value);
|
@@ -2386,7 +2547,7 @@ class Controller {
|
|
2386
2547
|
}
|
2387
2548
|
disconnect() {
|
2388
2549
|
}
|
2389
|
-
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, } = {}) {
|
2390
2551
|
const type = prefix ? `${prefix}:${eventName}` : eventName;
|
2391
2552
|
const event = new CustomEvent(type, { detail, bubbles, cancelable });
|
2392
2553
|
target.dispatchEvent(event);
|