@b9g/crank 0.4.3 → 0.5.0-beta.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.
package/crank.d.ts CHANGED
@@ -166,9 +166,9 @@ export declare function isElement(value: any): value is Element;
166
166
  * children prop according to any additional arguments passed to the function.
167
167
  */
168
168
  export declare function createElement<TTag extends Tag>(tag: TTag, props?: TagProps<TTag> | null | undefined, ...children: Array<unknown>): Element<TTag>;
169
- /**
170
- * Clones a given element, shallowly copying the props object.
171
- */
169
+ /** A single-letter alias for createElement */
170
+ export declare const c: typeof createElement;
171
+ /** Clones a given element, shallowly copying the props object. */
172
172
  export declare function cloneElement<TTag extends Tag>(el: Element<TTag>): Element<TTag>;
173
173
  /**
174
174
  * A helper type which repesents all possible rendered values of an element.
package/crank.js CHANGED
@@ -105,6 +105,7 @@ class Element {
105
105
  this.static_ = static_;
106
106
  }
107
107
  }
108
+ // See Element interface
108
109
  Element.prototype.$$typeof = ElementSymbol;
109
110
  function isElement(value) {
110
111
  return value != null && value.$$typeof === ElementSymbol;
@@ -127,6 +128,7 @@ function createElement(tag, props, ...children) {
127
128
  switch (name) {
128
129
  case "crank-key":
129
130
  case "c-key":
131
+ case "$key":
130
132
  // We have to make sure we don’t assign null to the key because we
131
133
  // don’t check for null keys in the diffing functions.
132
134
  if (props[name] != null) {
@@ -135,12 +137,14 @@ function createElement(tag, props, ...children) {
135
137
  break;
136
138
  case "crank-ref":
137
139
  case "c-ref":
140
+ case "$ref":
138
141
  if (typeof props[name] === "function") {
139
142
  ref = props[name];
140
143
  }
141
144
  break;
142
145
  case "crank-static":
143
146
  case "c-static":
147
+ case "$static":
144
148
  static_ = !!props[name];
145
149
  break;
146
150
  default:
@@ -154,11 +158,27 @@ function createElement(tag, props, ...children) {
154
158
  else if (children.length === 1) {
155
159
  props1.children = children[0];
156
160
  }
161
+ // string aliases for the special tags
162
+ // TODO: Does this logic belong here, or in the Element constructor
163
+ switch (tag) {
164
+ case "$FRAGMENT":
165
+ tag = Fragment;
166
+ break;
167
+ case "$PORTAL":
168
+ tag = Portal;
169
+ break;
170
+ case "$COPY":
171
+ tag = Copy;
172
+ break;
173
+ case "$RAW":
174
+ tag = Raw;
175
+ break;
176
+ }
157
177
  return new Element(tag, props1, key, ref, static_);
158
178
  }
159
- /**
160
- * Clones a given element, shallowly copying the props object.
161
- */
179
+ /** A single-letter alias for createElement */
180
+ const c = createElement;
181
+ /** Clones a given element, shallowly copying the props object. */
162
182
  function cloneElement(el) {
163
183
  if (!isElement(el)) {
164
184
  throw new TypeError("Cannot clone non-element");
@@ -732,9 +752,9 @@ const IsDone = 1 << 4;
732
752
  *
733
753
  * NOTE: This is mainly used to prevent some false positives in component
734
754
  * yields or returns undefined warnings. The reason we’re using this versus
735
- * IsUnmounted is a very troubling jest test (cascades sync generator parent
736
- * and sync generator child) where synchronous code causes a stack overflow
737
- * error in a non-deterministic way. Deeply disturbing stuff.
755
+ * IsUnmounted is a very troubling test (cascades sync generator parent and
756
+ * sync generator child) where synchronous code causes a stack overflow error
757
+ * in a non-deterministic way. Deeply disturbing stuff.
738
758
  */
739
759
  const IsErrored = 1 << 5;
740
760
  /**
@@ -955,13 +975,170 @@ class Context {
955
975
  provisions.set(key, value);
956
976
  }
957
977
  addEventListener(type, listener, options) {
958
- return addEventListener(this[$ContextImpl], type, listener, options);
978
+ const impl = this[$ContextImpl];
979
+ let listeners;
980
+ if (!isListenerOrListenerObject(listener)) {
981
+ return;
982
+ }
983
+ else {
984
+ const listeners1 = listenersMap.get(impl);
985
+ if (listeners1) {
986
+ listeners = listeners1;
987
+ }
988
+ else {
989
+ listeners = [];
990
+ listenersMap.set(impl, listeners);
991
+ }
992
+ }
993
+ options = normalizeListenerOptions(options);
994
+ let callback;
995
+ if (typeof listener === "object") {
996
+ callback = () => listener.handleEvent.apply(listener, arguments);
997
+ }
998
+ else {
999
+ callback = listener;
1000
+ }
1001
+ const record = { type, callback, listener, options };
1002
+ if (options.once) {
1003
+ record.callback = function () {
1004
+ const i = listeners.indexOf(record);
1005
+ if (i !== -1) {
1006
+ listeners.splice(i, 1);
1007
+ }
1008
+ return callback.apply(this, arguments);
1009
+ };
1010
+ }
1011
+ if (listeners.some((record1) => record.type === record1.type &&
1012
+ record.listener === record1.listener &&
1013
+ !record.options.capture === !record1.options.capture)) {
1014
+ return;
1015
+ }
1016
+ listeners.push(record);
1017
+ // TODO: is it possible to separate out the EventTarget delegation logic
1018
+ for (const value of getChildValues(impl.ret)) {
1019
+ if (isEventTarget(value)) {
1020
+ value.addEventListener(record.type, record.callback, record.options);
1021
+ }
1022
+ }
959
1023
  }
960
1024
  removeEventListener(type, listener, options) {
961
- return removeEventListener(this[$ContextImpl], type, listener, options);
1025
+ const impl = this[$ContextImpl];
1026
+ const listeners = listenersMap.get(impl);
1027
+ if (listeners == null || !isListenerOrListenerObject(listener)) {
1028
+ return;
1029
+ }
1030
+ const options1 = normalizeListenerOptions(options);
1031
+ const i = listeners.findIndex((record) => record.type === type &&
1032
+ record.listener === listener &&
1033
+ !record.options.capture === !options1.capture);
1034
+ if (i === -1) {
1035
+ return;
1036
+ }
1037
+ const record = listeners[i];
1038
+ listeners.splice(i, 1);
1039
+ // TODO: is it possible to separate out the EventTarget delegation logic
1040
+ for (const value of getChildValues(impl.ret)) {
1041
+ if (isEventTarget(value)) {
1042
+ value.removeEventListener(record.type, record.callback, record.options);
1043
+ }
1044
+ }
962
1045
  }
963
1046
  dispatchEvent(ev) {
964
- return dispatchEvent(this[$ContextImpl], ev);
1047
+ const impl = this[$ContextImpl];
1048
+ const path = [];
1049
+ for (let parent = impl.parent; parent !== undefined; parent = parent.parent) {
1050
+ path.push(parent);
1051
+ }
1052
+ // We patch the stopImmediatePropagation method because ev.cancelBubble
1053
+ // only informs us if stopPropagation was called and there are no
1054
+ // properties which inform us if stopImmediatePropagation was called.
1055
+ let immediateCancelBubble = false;
1056
+ const stopImmediatePropagation = ev.stopImmediatePropagation;
1057
+ setEventProperty(ev, "stopImmediatePropagation", () => {
1058
+ immediateCancelBubble = true;
1059
+ return stopImmediatePropagation.call(ev);
1060
+ });
1061
+ setEventProperty(ev, "target", impl.ctx);
1062
+ // The only possible errors in this block are errors thrown by callbacks,
1063
+ // and dispatchEvent will only log these errors rather than throwing
1064
+ // them. Therefore, we place all code in a try block, log errors in the
1065
+ // catch block, and use an unsafe return statement in the finally block.
1066
+ //
1067
+ // Each early return within the try block returns true because while the
1068
+ // return value is overridden in the finally block, TypeScript
1069
+ // (justifiably) does not recognize the unsafe return statement.
1070
+ //
1071
+ // TODO: Run all callbacks even if one of them errors
1072
+ try {
1073
+ setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
1074
+ for (let i = path.length - 1; i >= 0; i--) {
1075
+ const target = path[i];
1076
+ const listeners = listenersMap.get(target);
1077
+ if (listeners) {
1078
+ setEventProperty(ev, "currentTarget", target.ctx);
1079
+ for (const record of listeners) {
1080
+ if (record.type === ev.type && record.options.capture) {
1081
+ record.callback.call(target.ctx, ev);
1082
+ if (immediateCancelBubble) {
1083
+ return true;
1084
+ }
1085
+ }
1086
+ }
1087
+ }
1088
+ if (ev.cancelBubble) {
1089
+ return true;
1090
+ }
1091
+ }
1092
+ {
1093
+ const listeners = listenersMap.get(impl);
1094
+ if (listeners) {
1095
+ setEventProperty(ev, "eventPhase", AT_TARGET);
1096
+ setEventProperty(ev, "currentTarget", impl.ctx);
1097
+ for (const record of listeners) {
1098
+ if (record.type === ev.type) {
1099
+ record.callback.call(impl.ctx, ev);
1100
+ if (immediateCancelBubble) {
1101
+ return true;
1102
+ }
1103
+ }
1104
+ }
1105
+ if (ev.cancelBubble) {
1106
+ return true;
1107
+ }
1108
+ }
1109
+ }
1110
+ if (ev.bubbles) {
1111
+ setEventProperty(ev, "eventPhase", BUBBLING_PHASE);
1112
+ for (let i = 0; i < path.length; i++) {
1113
+ const target = path[i];
1114
+ const listeners = listenersMap.get(target);
1115
+ if (listeners) {
1116
+ setEventProperty(ev, "currentTarget", target.ctx);
1117
+ for (const record of listeners) {
1118
+ if (record.type === ev.type && !record.options.capture) {
1119
+ record.callback.call(target.ctx, ev);
1120
+ if (immediateCancelBubble) {
1121
+ return true;
1122
+ }
1123
+ }
1124
+ }
1125
+ }
1126
+ if (ev.cancelBubble) {
1127
+ return true;
1128
+ }
1129
+ }
1130
+ }
1131
+ }
1132
+ catch (err) {
1133
+ // TODO: Use setTimeout to rethrow the error.
1134
+ console.error(err);
1135
+ }
1136
+ finally {
1137
+ setEventProperty(ev, "eventPhase", NONE);
1138
+ setEventProperty(ev, "currentTarget", null);
1139
+ // eslint-disable-next-line no-unsafe-finally
1140
+ return !ev.defaultPrevented;
1141
+ }
965
1142
  }
966
1143
  }
967
1144
  /*** PRIVATE CONTEXT FUNCTIONS ***/
@@ -1362,168 +1539,11 @@ const CAPTURING_PHASE = 1;
1362
1539
  const AT_TARGET = 2;
1363
1540
  const BUBBLING_PHASE = 3;
1364
1541
  const listenersMap = new WeakMap();
1365
- function addEventListener(ctx, type, listener, options) {
1366
- let listeners;
1367
- if (listener == null) {
1368
- return;
1369
- }
1370
- else {
1371
- const listeners1 = listenersMap.get(ctx);
1372
- if (listeners1) {
1373
- listeners = listeners1;
1374
- }
1375
- else {
1376
- listeners = [];
1377
- listenersMap.set(ctx, listeners);
1378
- }
1379
- }
1380
- options = normalizeListenerOptions(options);
1381
- let callback;
1382
- if (typeof listener === "object") {
1383
- callback = () => listener.handleEvent.apply(listener, arguments);
1384
- }
1385
- else {
1386
- callback = listener;
1387
- }
1388
- const record = { type, callback, listener, options };
1389
- if (options.once) {
1390
- record.callback = function () {
1391
- const i = listeners.indexOf(record);
1392
- if (i !== -1) {
1393
- listeners.splice(i, 1);
1394
- }
1395
- return callback.apply(this, arguments);
1396
- };
1397
- }
1398
- if (listeners.some((record1) => record.type === record1.type &&
1399
- record.listener === record1.listener &&
1400
- !record.options.capture === !record1.options.capture)) {
1401
- return;
1402
- }
1403
- listeners.push(record);
1404
- // TODO: is it possible to separate out the EventTarget delegation logic
1405
- for (const value of getChildValues(ctx.ret)) {
1406
- if (isEventTarget(value)) {
1407
- value.addEventListener(record.type, record.callback, record.options);
1408
- }
1409
- }
1410
- }
1411
- function removeEventListener(ctx, type, listener, options) {
1412
- const listeners = listenersMap.get(ctx);
1413
- if (listener == null || listeners == null) {
1414
- return;
1415
- }
1416
- const options1 = normalizeListenerOptions(options);
1417
- const i = listeners.findIndex((record) => record.type === type &&
1418
- record.listener === listener &&
1419
- !record.options.capture === !options1.capture);
1420
- if (i === -1) {
1421
- return;
1422
- }
1423
- const record = listeners[i];
1424
- listeners.splice(i, 1);
1425
- // TODO: is it possible to separate out the EventTarget delegation logic
1426
- for (const value of getChildValues(ctx.ret)) {
1427
- if (isEventTarget(value)) {
1428
- value.removeEventListener(record.type, record.callback, record.options);
1429
- }
1430
- }
1431
- }
1432
- function dispatchEvent(ctx, ev) {
1433
- const path = [];
1434
- for (let parent = ctx.parent; parent !== undefined; parent = parent.parent) {
1435
- path.push(parent);
1436
- }
1437
- // We patch the stopImmediatePropagation method because ev.cancelBubble
1438
- // only informs us if stopPropagation was called and there are no
1439
- // properties which inform us if stopImmediatePropagation was called.
1440
- let immediateCancelBubble = false;
1441
- const stopImmediatePropagation = ev.stopImmediatePropagation;
1442
- setEventProperty(ev, "stopImmediatePropagation", () => {
1443
- immediateCancelBubble = true;
1444
- return stopImmediatePropagation.call(ev);
1445
- });
1446
- setEventProperty(ev, "target", ctx.ctx);
1447
- // The only possible errors in this block are errors thrown by callbacks,
1448
- // and dispatchEvent will only log these errors rather than throwing
1449
- // them. Therefore, we place all code in a try block, log errors in the
1450
- // catch block, and use an unsafe return statement in the finally block.
1451
- //
1452
- // Each early return within the try block returns true because while the
1453
- // return value is overridden in the finally block, TypeScript
1454
- // (justifiably) does not recognize the unsafe return statement.
1455
- //
1456
- // TODO: Run all callbacks even if one of them errors
1457
- try {
1458
- setEventProperty(ev, "eventPhase", CAPTURING_PHASE);
1459
- for (let i = path.length - 1; i >= 0; i--) {
1460
- const target = path[i];
1461
- const listeners = listenersMap.get(target);
1462
- if (listeners) {
1463
- setEventProperty(ev, "currentTarget", target.ctx);
1464
- for (const record of listeners) {
1465
- if (record.type === ev.type && record.options.capture) {
1466
- record.callback.call(target.ctx, ev);
1467
- if (immediateCancelBubble) {
1468
- return true;
1469
- }
1470
- }
1471
- }
1472
- }
1473
- if (ev.cancelBubble) {
1474
- return true;
1475
- }
1476
- }
1477
- {
1478
- const listeners = listenersMap.get(ctx);
1479
- if (listeners) {
1480
- setEventProperty(ev, "eventPhase", AT_TARGET);
1481
- setEventProperty(ev, "currentTarget", ctx.ctx);
1482
- for (const record of listeners) {
1483
- if (record.type === ev.type) {
1484
- record.callback.call(ctx.ctx, ev);
1485
- if (immediateCancelBubble) {
1486
- return true;
1487
- }
1488
- }
1489
- }
1490
- if (ev.cancelBubble) {
1491
- return true;
1492
- }
1493
- }
1494
- }
1495
- if (ev.bubbles) {
1496
- setEventProperty(ev, "eventPhase", BUBBLING_PHASE);
1497
- for (let i = 0; i < path.length; i++) {
1498
- const target = path[i];
1499
- const listeners = listenersMap.get(target);
1500
- if (listeners) {
1501
- setEventProperty(ev, "currentTarget", target.ctx);
1502
- for (const record of listeners) {
1503
- if (record.type === ev.type && !record.options.capture) {
1504
- record.callback.call(target.ctx, ev);
1505
- if (immediateCancelBubble) {
1506
- return true;
1507
- }
1508
- }
1509
- }
1510
- }
1511
- if (ev.cancelBubble) {
1512
- return true;
1513
- }
1514
- }
1515
- }
1516
- }
1517
- catch (err) {
1518
- // TODO: Use setTimeout to rethrow the error.
1519
- console.error(err);
1520
- }
1521
- finally {
1522
- setEventProperty(ev, "eventPhase", NONE);
1523
- setEventProperty(ev, "currentTarget", null);
1524
- // eslint-disable-next-line no-unsafe-finally
1525
- return !ev.defaultPrevented;
1526
- }
1542
+ function isListenerOrListenerObject(value) {
1543
+ return (typeof value === "function" ||
1544
+ (value !== null &&
1545
+ typeof value === "object" &&
1546
+ typeof value.handleEvent === "function"));
1527
1547
  }
1528
1548
  function normalizeListenerOptions(options) {
1529
1549
  if (typeof options === "boolean") {
@@ -1635,5 +1655,5 @@ function propagateError(ctx, err) {
1635
1655
  // default export. Prefer named exports when importing directly.
1636
1656
  var crank = { createElement, Fragment };
1637
1657
 
1638
- export { Context, Copy, Element, Fragment, Portal, Raw, Renderer, cloneElement, createElement, crank as default, isElement };
1658
+ export { Context, Copy, Element, Fragment, Portal, Raw, Renderer, c, cloneElement, createElement, crank as default, isElement };
1639
1659
  //# sourceMappingURL=crank.js.map