@baleada/logic 0.21.1 → 0.22.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/lib/index.cjs CHANGED
@@ -291,16 +291,61 @@ const modifiersByAlias = {
291
291
  opt: "alt",
292
292
  option: "alt"
293
293
  };
294
- function createExceptAndOnlyEffect(effect, options) {
294
+ function createExceptAndOnlyEffect(type, effect, options) {
295
295
  const { except = [], only = [] } = options;
296
+ if (type === "keydown" || type === "keyup") {
297
+ return (event) => {
298
+ const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => lazyCollections.some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true], api = {
299
+ is: (keycombo) => eventMatchesKeycombo(event, ensureKeycombo(keycombo))
300
+ };
301
+ if (matchesOnly) {
302
+ effect(event, api);
303
+ return;
304
+ }
305
+ if (only.length === 0 && !matchesExcept) {
306
+ effect(event, api);
307
+ return;
308
+ }
309
+ };
310
+ }
311
+ if (type === "click" || type === "mousedown" || type === "mouseup" || type === "dblclick" || type === "contextmenu") {
312
+ return (event) => {
313
+ const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => lazyCollections.some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true], api = {
314
+ is: (clickcombo) => eventMatchesClickcombo(event, ensureClickcombo(clickcombo))
315
+ };
316
+ if (matchesOnly) {
317
+ effect(event, api);
318
+ return;
319
+ }
320
+ if (only.length === 0 && !matchesExcept) {
321
+ effect(event, api);
322
+ return;
323
+ }
324
+ };
325
+ }
326
+ if (type === "pointerdown" || type === "pointerup") {
327
+ return (event) => {
328
+ const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => lazyCollections.some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true], api = {
329
+ is: (pointercombo) => eventMatchesPointercombo(event, ensurePointercombo(pointercombo))
330
+ };
331
+ if (matchesOnly) {
332
+ effect(event, api);
333
+ return;
334
+ }
335
+ if (only.length === 0 && !matchesExcept) {
336
+ effect(event, api);
337
+ return;
338
+ }
339
+ };
340
+ }
296
341
  return (event) => {
297
342
  const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => lazyCollections.some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true];
298
343
  if (matchesOnly) {
299
- effect(event);
344
+ effect(event, {});
300
345
  return;
301
346
  }
302
347
  if (only.length === 0 && !matchesExcept) {
303
- effect(event);
348
+ effect(event, {});
304
349
  return;
305
350
  }
306
351
  };
@@ -349,19 +394,13 @@ class Recognizeable {
349
394
  maxSequenceLength;
350
395
  effects;
351
396
  effectApi;
352
- toType;
353
- constructor(sequence, options = { effectsIncludeCombos: true }) {
397
+ constructor(sequence, options = {}) {
354
398
  const defaultOptions = {
355
399
  maxSequenceLength: true,
356
- effectsIncludeCombos: true,
357
400
  effects: {}
358
401
  };
359
402
  this.maxSequenceLength = options?.maxSequenceLength || defaultOptions.maxSequenceLength;
360
- this.effects = new Map(isFunction(options?.effects) ? options.effects(createDefineEffect()) : Object.entries(options?.effects || defaultOptions.effects));
361
- this.toType = createToType({
362
- effectsIncludeCombos: options.effectsIncludeCombos,
363
- effects: this.effects
364
- });
403
+ this.effects = new Map(Object.entries(options?.effects || defaultOptions.effects));
365
404
  this.resetComputedMetadata();
366
405
  this.setSequence(sequence);
367
406
  this.effectApi = {
@@ -404,14 +443,13 @@ class Recognizeable {
404
443
  this.computedSequence = sequence;
405
444
  return this;
406
445
  }
407
- recognize(sequenceItem, { onRecognized } = {}) {
446
+ recognize(sequenceItem, api, { onRecognized } = {}) {
408
447
  this.recognizing();
409
448
  const type = this.toType(sequenceItem), excess = isNumber(this.maxSequenceLength) ? Math.max(0, this.sequence.length - this.maxSequenceLength) : 0, newSequence = createConcat(createSlice(excess)(this.sequence), [sequenceItem])([]);
410
- this.effectApi.sequenceItem = sequenceItem;
411
449
  this.effectApi.getSequence = () => newSequence;
412
450
  this.effectApi.onRecognized = onRecognized || (() => {
413
451
  });
414
- this.effects.get(type)?.(this.effectApi);
452
+ this.effects.get(type)?.(sequenceItem, { ...api, ...this.effectApi });
415
453
  switch (this.status) {
416
454
  case "denied":
417
455
  this.resetComputedMetadata();
@@ -427,34 +465,7 @@ class Recognizeable {
427
465
  recognizing() {
428
466
  this.computedStatus = "recognizing";
429
467
  }
430
- }
431
- function createDefineEffect() {
432
- return (type, effect) => {
433
- return [type, effect];
434
- };
435
- }
436
- function createToType({
437
- effectsIncludeCombos,
438
- effects
439
- }) {
440
- const effectLeftclickcombos = [], effectRightclickcombos = [], effectKeycombos = [];
441
- if (effectsIncludeCombos) {
442
- for (const [effectKey] of effects) {
443
- const implementation = toImplementation(effectKey);
444
- switch (implementation) {
445
- case "leftclickcombo":
446
- effectLeftclickcombos.push(ensureClickcombo(effectKey));
447
- break;
448
- case "rightclickcombo":
449
- effectRightclickcombos.push(ensureClickcombo(effectKey));
450
- break;
451
- case "keycombo":
452
- effectKeycombos.push(ensureKeycombo(effectKey));
453
- break;
454
- }
455
- }
456
- }
457
- return function toType(sequenceItem) {
468
+ toType(sequenceItem) {
458
469
  if (isArray(sequenceItem)) {
459
470
  if (sequenceItem[0] instanceof IntersectionObserverEntry) {
460
471
  return "intersect";
@@ -472,38 +483,10 @@ function createToType({
472
483
  if ("didTimeout" in sequenceItem) {
473
484
  return "idle";
474
485
  }
475
- if (effectLeftclickcombos?.length > 0) {
476
- if (leftclickcomboEventTypes.has(sequenceItem.type)) {
477
- for (const clickcombo of effectLeftclickcombos) {
478
- if (eventMatchesClickcombo({ event: sequenceItem, clickcombo })) {
479
- return toJoinedClickcombo(clickcombo);
480
- }
481
- }
482
- }
483
- }
484
- if (effectRightclickcombos?.length > 0) {
485
- if (rightclickComboEventTypes.has(sequenceItem.type)) {
486
- for (const clickcombo of effectRightclickcombos) {
487
- if (eventMatchesClickcombo({ event: sequenceItem, clickcombo })) {
488
- return toJoinedClickcombo(clickcombo);
489
- }
490
- }
491
- }
492
- }
493
- if (effectKeycombos?.length > 0) {
494
- if (keycomboEventTypes.has(sequenceItem.type)) {
495
- for (const keycombo of effectKeycombos) {
496
- if (eventMatchesKeycombo({ event: sequenceItem, keycombo })) {
497
- return toJoinedKeycombo(keycombo);
498
- }
499
- }
500
- }
501
- }
502
486
  return sequenceItem.type;
503
487
  }
504
- };
488
+ }
505
489
  }
506
- const leftclickcomboEventTypes = /* @__PURE__ */ new Set(["click", "mousedown", "mouseup", "dblclick"]), rightclickComboEventTypes = /* @__PURE__ */ new Set(["contextmenu"]), keycomboEventTypes = /* @__PURE__ */ new Set(["keydown", "keyup"]), toJoinedClickcombo = lazyCollections.join("+"), toJoinedKeycombo = lazyCollections.pipe(lazyCollections.map(({ name }) => name), toJoinedClickcombo);
507
490
 
508
491
  class Listenable {
509
492
  computedRecognizeable;
@@ -511,15 +494,8 @@ class Listenable {
511
494
  computedActive;
512
495
  constructor(type, options) {
513
496
  if (type === "recognizeable") {
514
- const recognizeableOptions = {
515
- ...options?.recognizeable || {},
516
- effects: isFunction(options?.recognizeable?.effects) ? createReduce((effects, [type2, effect]) => {
517
- effects[type2] = effect;
518
- return effects;
519
- }, {})(options.recognizeable.effects(createDefineEffect())) : options?.recognizeable?.effects || {}
520
- };
521
- this.computedRecognizeable = new Recognizeable([], recognizeableOptions);
522
- this.recognizeableEffectsKeys = Object.keys(recognizeableOptions.effects);
497
+ this.computedRecognizeable = new Recognizeable([], options?.recognizeable || {});
498
+ this.recognizeableEffectsKeys = Object.keys(options?.recognizeable?.effects || {});
523
499
  }
524
500
  this.computedActive = /* @__PURE__ */ new Set();
525
501
  this.setType(type);
@@ -575,16 +551,6 @@ class Listenable {
575
551
  case "documentevent":
576
552
  this.documentEventListen(effect, options);
577
553
  break;
578
- case "keycombo":
579
- this.keycomboListen(effect, options);
580
- break;
581
- case "leftclickcombo":
582
- case "rightclickcombo":
583
- this.clickcomboListen(effect, options);
584
- break;
585
- case "pointercombo":
586
- this.pointercomboListen(effect, options);
587
- break;
588
554
  case "event":
589
555
  this.eventListen(effect, options);
590
556
  break;
@@ -612,18 +578,19 @@ class Listenable {
612
578
  if (isFunction(options.instantEffect)) {
613
579
  options.instantEffect(target);
614
580
  }
615
- target.addEventListener("change", effect);
616
- this.active.add({ target, id: ["change", effect] });
581
+ const withApi = (event) => effect(event, {});
582
+ target.addEventListener("change", withApi);
583
+ this.active.add({ target, id: ["change", withApi] });
617
584
  }
618
585
  idleListen(effect, options) {
619
- const { requestIdleCallback } = options, id = window.requestIdleCallback(effect, requestIdleCallback);
586
+ const { requestIdleCallback } = options, id = window.requestIdleCallback((deadline) => effect(deadline, {}), requestIdleCallback);
620
587
  this.active.add({ target: window, id });
621
588
  }
622
589
  recognizeableListen(effect, options) {
623
- const guardedEffect = (sequenceItem) => {
624
- this.recognizeable.recognize(sequenceItem, { onRecognized: effect });
590
+ const guardedEffect = (sequenceItem, api) => {
591
+ this.recognizeable.recognize(sequenceItem, api, { onRecognized: (sequenceItem2) => effect(sequenceItem2, api) });
625
592
  if (this.recognizeable.status === "recognized") {
626
- effect(sequenceItem);
593
+ effect(sequenceItem, api);
627
594
  }
628
595
  };
629
596
  for (const type of this.recognizeableEffectsKeys) {
@@ -639,44 +606,8 @@ class Listenable {
639
606
  };
640
607
  this.eventListen(effect, ensuredOptions);
641
608
  }
642
- pointercomboListen(effect, options) {
643
- const pointercombo = ensurePointercombo(this.type), guardedEffect = (event) => {
644
- if (eventMatchesPointercombo({ event, pointercombo })) {
645
- effect(event);
646
- }
647
- };
648
- this.eventListen(guardedEffect, options);
649
- }
650
- clickcomboListen(effect, options) {
651
- const clickcombo = ensureClickcombo(this.type), guardedEffect = (event) => {
652
- if (eventMatchesClickcombo({ event, clickcombo })) {
653
- effect(event);
654
- }
655
- };
656
- this.eventListen(guardedEffect, options);
657
- }
658
- keycomboListen(effect, options) {
659
- const keycombo = ensureKeycombo(this.type), guardedEffect = (event) => {
660
- if (eventMatchesKeycombo({ event, keycombo })) {
661
- effect(event);
662
- }
663
- };
664
- this.eventListen(guardedEffect, options);
665
- }
666
609
  eventListen(effect, options) {
667
- const type = (() => {
668
- switch (this.implementation) {
669
- case "keycombo":
670
- return `key${options.keyDirection || "down"}`;
671
- case "leftclickcombo":
672
- return this.type.match(/(\w+)$/)[1];
673
- case "rightclickcombo":
674
- return "contextmenu";
675
- default:
676
- return this.type;
677
- }
678
- })();
679
- const { exceptAndOnlyEffect, effectOptions } = toAddEventListenerParams(effect, options), eventListeners = [[type, exceptAndOnlyEffect, ...effectOptions]];
610
+ const { exceptAndOnlyEffect, effectOptions } = toAddEventListenerParams(this.type, effect, options), eventListeners = [[this.type, exceptAndOnlyEffect, ...effectOptions]];
680
611
  this.addEventListeners(eventListeners, options);
681
612
  }
682
613
  addEventListeners(eventListeners, options) {
@@ -767,22 +698,6 @@ const predicatesByImplementation = /* @__PURE__ */ new Map([
767
698
  "documentevent",
768
699
  (type) => documentEvents.has(type)
769
700
  ],
770
- [
771
- "keycombo",
772
- (type) => implementationREs.keycombo.test(type)
773
- ],
774
- [
775
- "leftclickcombo",
776
- (type) => implementationREs.leftclickcombo.test(type)
777
- ],
778
- [
779
- "rightclickcombo",
780
- (type) => implementationREs.rightclickcombo.test(type)
781
- ],
782
- [
783
- "pointercombo",
784
- (type) => implementationREs.pointercombo.test(type)
785
- ],
786
701
  [
787
702
  "event",
788
703
  () => true
@@ -797,17 +712,13 @@ const documentEvents = /* @__PURE__ */ new Set([
797
712
  "visibilitychange"
798
713
  ]);
799
714
  const implementationREs = {
800
- mediaquery: /^\(.+\)$/,
801
- keycombo: /^((!?([a-zA-Z0-9,<.>/?;:'"[{\]}\\|`~!@#$%^&*()-_=+]|tab|space|arrow|vertical|horizontal|up|right|down|left|enter|backspace|esc|home|end|pagedown|pageup|capslock|f[0-9]{1,2}|camera|delete|cmd|command|meta|shift|ctrl|control|alt|opt|option))\+)*(!?([a-zA-Z0-9,<.>/?;:'"[{\]}\\|`~!@#$%^&*()-_=+]|tab|space|arrow|vertical|horizontal|up|right|down|left|enter|backspace|esc|home|end|pagedown|pageup|capslock|f[0-9]{1,2}|camera|delete|cmd|command|meta|shift|ctrl|control|alt|opt|option))$/,
802
- leftclickcombo: /^(!?((cmd|command|meta|shift|ctrl|control|alt|opt|option))\+){0,4}!?(click|mousedown|mouseup|dblclick)$/,
803
- rightclickcombo: /^(!?((cmd|command|meta|shift|ctrl|control|alt|opt|option))\+){0,4}!?(rightclick|contextmenu)$/,
804
- pointercombo: /^(!?((cmd|command|meta|shift|ctrl|control|alt|opt|option))\+){0,4}!?(pointerdown|pointerup)$/
715
+ mediaquery: /^\(.+\)$/
805
716
  };
806
- function toAddEventListenerParams(effect, options) {
807
- const { addEventListener, useCapture } = options, exceptAndOnlyEffect = createExceptAndOnlyEffect(effect, options), effectOptions = [addEventListener || useCapture];
717
+ function toAddEventListenerParams(type, effect, options) {
718
+ const { addEventListener, useCapture } = options, exceptAndOnlyEffect = createExceptAndOnlyEffect(type, effect, options), effectOptions = [addEventListener || useCapture];
808
719
  return { exceptAndOnlyEffect, effectOptions };
809
720
  }
810
- function eventMatchesKeycombo({ event, keycombo }) {
721
+ function eventMatchesKeycombo(event, keycombo) {
811
722
  return lazyCollections.every(({ name, type }, index) => {
812
723
  switch (type) {
813
724
  case "singleCharacter":
@@ -891,10 +802,10 @@ const predicatesByArrow = /* @__PURE__ */ new Map([
891
802
  const arrows = /* @__PURE__ */ new Set(["arrowup", "arrowright", "arrowdown", "arrowleft"]);
892
803
  const verticalArrows = /* @__PURE__ */ new Set(["arrowup", "arrowdown"]);
893
804
  const horizontalArrows = /* @__PURE__ */ new Set(["arrowright", "arrowleft"]);
894
- function eventMatchesClickcombo({ event, clickcombo }) {
805
+ function eventMatchesClickcombo(event, clickcombo) {
895
806
  return lazyCollections.every((name) => fromComboItemNameToType(name) === "click" || name.startsWith("!") && !isModified({ alias: name.slice(1), event }) || !name.startsWith("!") && isModified({ alias: name, event }))(clickcombo);
896
807
  }
897
- function eventMatchesPointercombo({ event, pointercombo }) {
808
+ function eventMatchesPointercombo(event, pointercombo) {
898
809
  return lazyCollections.every((name) => fromComboItemNameToType(name) === "pointer" || name.startsWith("!") && !isModified({ alias: name.slice(1), event }) || !name.startsWith("!") && isModified({ alias: name, event }))(pointercombo);
899
810
  }
900
811
  const observerAssertionsByType = {
@@ -2704,6 +2615,10 @@ class Navigateable {
2704
2615
  const defaultOptions$1 = {
2705
2616
  initialPicks: []
2706
2617
  };
2618
+ const defaultPickOptions = {
2619
+ replace: "none",
2620
+ allowsDuplicates: false
2621
+ };
2707
2622
  class Pickable {
2708
2623
  constructor(array, options = {}) {
2709
2624
  this.setArray(array);
@@ -2749,8 +2664,9 @@ class Pickable {
2749
2664
  return this.toItems(this.picks);
2750
2665
  }
2751
2666
  toItems = createMap((index) => this.array[index]);
2667
+ computedMultiple;
2752
2668
  get multiple() {
2753
- return this.picks.length > 1;
2669
+ return this.computedMultiple;
2754
2670
  }
2755
2671
  toPossiblePicks;
2756
2672
  setArray(array) {
@@ -2762,41 +2678,42 @@ class Pickable {
2762
2678
  return this.pick(indexOrIndices);
2763
2679
  }
2764
2680
  pick(indexOrIndices, options = {}) {
2765
- const { replace = "none" } = options;
2681
+ const { replace, allowsDuplicates } = { ...defaultPickOptions, ...options };
2766
2682
  this.computedPicks = new Pipeable(indexOrIndices).pipe(ensureIndices, this.toPossiblePicks, (possiblePicks) => {
2767
2683
  if (replace === "all") {
2768
- return toUnique(possiblePicks);
2684
+ return allowsDuplicates ? possiblePicks : toUnique(possiblePicks);
2769
2685
  }
2770
- const possibleWithoutDuplicates = createFilter((possiblePick) => typeof lazyCollections.find((pick) => pick === possiblePick)(this.picks || []) !== "number")(possiblePicks);
2686
+ const maybeWithoutDuplicates = allowsDuplicates ? possiblePicks : createFilter((possiblePick) => typeof lazyCollections.find((pick) => pick === possiblePick)(this.picks || []) !== "number")(possiblePicks);
2771
2687
  switch (replace) {
2772
2688
  case "none":
2773
- return createConcat(this.picks || [], possibleWithoutDuplicates)([]);
2689
+ return createConcat(this.picks || [], maybeWithoutDuplicates)([]);
2774
2690
  case "fifo":
2775
- if (possibleWithoutDuplicates.length === 0) {
2691
+ if (maybeWithoutDuplicates.length === 0) {
2776
2692
  return this.picks;
2777
2693
  }
2778
- if (possibleWithoutDuplicates.length === this.picks.length) {
2779
- return possibleWithoutDuplicates;
2694
+ if (maybeWithoutDuplicates.length === this.picks.length) {
2695
+ return maybeWithoutDuplicates;
2780
2696
  }
2781
- if (possibleWithoutDuplicates.length > this.picks.length) {
2782
- return createSlice(possibleWithoutDuplicates.length - this.picks.length)(possibleWithoutDuplicates);
2697
+ if (maybeWithoutDuplicates.length > this.picks.length) {
2698
+ return createSlice(maybeWithoutDuplicates.length - this.picks.length)(maybeWithoutDuplicates);
2783
2699
  }
2784
- return new Pipeable(this.picks).pipe(createSlice(possibleWithoutDuplicates.length), createConcat(possibleWithoutDuplicates));
2700
+ return new Pipeable(this.picks).pipe(createSlice(maybeWithoutDuplicates.length), createConcat(maybeWithoutDuplicates));
2785
2701
  case "lifo":
2786
- if (possibleWithoutDuplicates.length === 0) {
2702
+ if (maybeWithoutDuplicates.length === 0) {
2787
2703
  return this.picks;
2788
2704
  }
2789
- if (possibleWithoutDuplicates.length === this.picks.length) {
2790
- return possibleWithoutDuplicates;
2705
+ if (maybeWithoutDuplicates.length === this.picks.length) {
2706
+ return maybeWithoutDuplicates;
2791
2707
  }
2792
- if (possibleWithoutDuplicates.length > this.picks.length) {
2793
- return createSlice(0, possibleWithoutDuplicates.length - this.picks.length + 1)(possibleWithoutDuplicates);
2708
+ if (maybeWithoutDuplicates.length > this.picks.length) {
2709
+ return createSlice(0, maybeWithoutDuplicates.length - this.picks.length + 1)(maybeWithoutDuplicates);
2794
2710
  }
2795
- return new Pipeable(this.picks).pipe(createSlice(0, this.picks.length - possibleWithoutDuplicates.length), createConcat(possibleWithoutDuplicates));
2711
+ return new Pipeable(this.picks).pipe(createSlice(0, this.picks.length - maybeWithoutDuplicates.length), createConcat(maybeWithoutDuplicates));
2796
2712
  }
2797
2713
  });
2798
2714
  this.computedFirst = Math.min(...this.picks);
2799
2715
  this.computedLast = Math.max(...this.picks);
2716
+ this.computedMultiple = toUnique(this.picks).length > 1;
2800
2717
  this.picked();
2801
2718
  return this;
2802
2719
  }
@@ -2808,6 +2725,7 @@ class Pickable {
2808
2725
  this.computedPicks = [];
2809
2726
  this.computedFirst = void 0;
2810
2727
  this.computedLast = void 0;
2728
+ this.computedMultiple = false;
2811
2729
  this.omitted();
2812
2730
  return this;
2813
2731
  }
@@ -2815,6 +2733,7 @@ class Pickable {
2815
2733
  this.computedPicks = createFilter((pick, index) => options.reference === "array" ? isUndefined(lazyCollections.find((omit) => pick === omit)(omits)) : isUndefined(lazyCollections.find((omit) => index === omit)(omits)))(this.computedPicks);
2816
2734
  this.computedFirst = Math.min(...this.picks);
2817
2735
  this.computedLast = Math.max(...this.picks);
2736
+ this.computedMultiple = toUnique(this.picks).length > 1;
2818
2737
  this.omitted();
2819
2738
  return this;
2820
2739
  }
@@ -3102,8 +3021,6 @@ exports.easingsNetOutExpo = easingsNetOutExpo;
3102
3021
  exports.easingsNetOutQuad = easingsNetOutQuad;
3103
3022
  exports.easingsNetOutQuint = easingsNetOutQuint;
3104
3023
  exports.easingsNetOutSine = easingsNetOutSine;
3105
- exports.ensureKeycombo = ensureKeycombo;
3106
- exports.eventMatchesKeycombo = eventMatchesKeycombo;
3107
3024
  exports.linear = linear;
3108
3025
  exports.materialAccelerated = materialAccelerated;
3109
3026
  exports.materialDecelerated = materialDecelerated;
package/lib/index.d.ts CHANGED
@@ -411,23 +411,21 @@ declare class Grantable<DescriptorType extends PermissionDescriptor> {
411
411
 
412
412
  declare type RecognizeableOptions<Type extends ListenableSupportedType, Metadata extends Record<any, any>> = {
413
413
  maxSequenceLength?: true | number;
414
- effectsIncludeCombos?: boolean;
415
414
  effects?: {
416
- [type in Type]?: (api: RecognizeableEffectApi<Type, Metadata>) => any;
417
- } | ((defineEffect: DefineEffect<Type, Metadata>) => [type: Type, effect: (api: RecognizeableEffectApi<Type, Metadata>) => any][]);
415
+ [type in Type]?: RecognizeableEffect<type, Metadata>;
416
+ };
418
417
  };
419
- declare type DefineEffect<Type extends ListenableSupportedType, Metadata extends Record<any, any>> = <EffectType extends Type>(type: EffectType, effect: (api: RecognizeableEffectApi<EffectType, Metadata>) => any) => [type: Type, effect: (api: RecognizeableEffectApi<Type, Metadata>) => any];
420
- declare type RecognizeableStatus = 'recognized' | 'recognizing' | 'denied' | 'ready';
418
+ declare type RecognizeableEffect<Type extends ListenableSupportedType, Metadata extends Record<any, any>> = (sequenceItem: ListenEffectParam<Type>, api: RecognizeableEffectApi<Type, Metadata>) => void;
421
419
  declare type RecognizeableEffectApi<Type extends ListenableSupportedType, Metadata extends Record<any, any>> = {
422
- getStatus: () => 'recognized' | 'recognizing' | 'denied' | 'ready';
420
+ getStatus: () => RecognizeableStatus;
423
421
  getMetadata: () => Metadata;
424
422
  setMetadata: (metadata: Metadata) => void;
425
423
  recognized: () => void;
426
424
  denied: () => void;
427
- sequenceItem: ListenEffectParam<Type>;
428
425
  getSequence: () => ListenEffectParam<Type>[];
429
426
  onRecognized: (sequenceItem: ListenEffectParam<Type>) => any;
430
- };
427
+ } & ListenEffectApi<Type>;
428
+ declare type RecognizeableStatus = 'recognized' | 'recognizing' | 'denied' | 'ready';
431
429
  declare type RecognizeOptions<Type extends ListenableSupportedType, Metadata extends Record<any, any>> = {
432
430
  onRecognized?: (sequenceItem: ListenEffectParam<Type>) => any;
433
431
  };
@@ -435,7 +433,6 @@ declare class Recognizeable<Type extends ListenableSupportedType, Metadata exten
435
433
  private maxSequenceLength;
436
434
  private effects;
437
435
  private effectApi;
438
- private toType;
439
436
  constructor(sequence: ListenEffectParam<Type>[], options?: RecognizeableOptions<Type, Metadata>);
440
437
  private computedMetadata;
441
438
  private resetComputedMetadata;
@@ -449,18 +446,12 @@ declare class Recognizeable<Type extends ListenableSupportedType, Metadata exten
449
446
  get metadata(): Metadata;
450
447
  private computedSequence;
451
448
  setSequence(sequence: ListenEffectParam<Type>[]): this;
452
- recognize(sequenceItem: ListenEffectParam<Type>, { onRecognized }?: RecognizeOptions<Type, Metadata>): this;
449
+ recognize(sequenceItem: ListenEffectParam<Type>, api: ListenEffectApi<Type>, { onRecognized }?: RecognizeOptions<Type, Metadata>): this;
453
450
  private recognizing;
451
+ private toType;
454
452
  }
455
453
 
456
- declare type ListenableKeycomboItem = {
457
- name: string;
458
- type: ListenableComboItemType | 'custom';
459
- };
460
- declare function ensureKeycombo(type: string): ListenableKeycomboItem[];
461
- declare type ListenableComboItemType = 'singleCharacter' | 'arrow' | 'other' | 'modifier' | 'click' | 'pointer';
462
-
463
- declare type ListenableSupportedType = 'recognizeable' | 'intersect' | 'mutate' | 'resize' | 'idle' | ListenableMediaQuery | ListenableClickcombo | ListenableLeftClick | ListenableRightClick | ListenablePointercombo | ListenableKeycombo | keyof Omit<HTMLElementEventMap, 'resize'> | keyof Omit<DocumentEventMap, 'resize'>;
454
+ declare type ListenableSupportedType = 'recognizeable' | 'intersect' | 'mutate' | 'resize' | 'idle' | ListenableMediaQuery | keyof Omit<HTMLElementEventMap, 'resize'> | keyof Omit<DocumentEventMap, 'resize'>;
464
455
  declare type ListenableMediaQuery = `(${string})`;
465
456
  declare type ListenableClickcombo = `${string}+${ListenableLeftClick | ListenableRightClick}`;
466
457
  declare type ListenableLeftClick = 'click' | 'mousedown' | 'mouseup' | 'dblclick';
@@ -468,12 +459,22 @@ declare type ListenableRightClick = 'rightclick' | 'contextmenu';
468
459
  declare type ListenablePointercombo = `${string}+${ListenablePointer}`;
469
460
  declare type ListenablePointer = 'pointerdown' | 'pointerup';
470
461
  declare type ListenableKeycombo = `${string}+${string}`;
471
- declare type ListenableSupportedEventType = ListenableClickcombo | ListenablePointercombo | ListenableKeycombo | keyof Omit<HTMLElementEventMap, 'resize'> | keyof Omit<DocumentEventMap, 'resize'>;
462
+ declare type ListenableSupportedEventType = keyof Omit<HTMLElementEventMap, 'resize'> | keyof Omit<DocumentEventMap, 'resize'>;
472
463
  declare type ListenableOptions<Type extends ListenableSupportedType, RecognizeableMetadata extends Record<any, any> = Record<any, any>> = {
473
464
  recognizeable?: RecognizeableOptions<Type, RecognizeableMetadata>;
474
465
  };
475
- declare type ListenEffect<Type extends ListenableSupportedType> = Type extends 'intersect' ? (entries: ListenEffectParam<Type>) => any : Type extends 'mutate' ? (records: ListenEffectParam<Type>) => any : Type extends 'resize' ? (entries: ListenEffectParam<Type>) => any : Type extends 'idle' ? (deadline: ListenEffectParam<Type>) => any : Type extends ListenableMediaQuery ? (event: ListenEffectParam<Type>) => any : Type extends ListenableClickcombo ? (event: ListenEffectParam<Type>) => any : Type extends ListenablePointercombo ? (event: ListenEffectParam<Type>) => any : Type extends ListenableKeycombo ? (event: ListenEffectParam<Type>) => any : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? (event: ListenEffectParam<Type>) => any : Type extends keyof Omit<DocumentEventMap, 'resize'> ? (event: ListenEffectParam<Type>) => any : never;
476
- declare type ListenEffectParam<Type extends ListenableSupportedType> = Type extends 'intersect' ? IntersectionObserverEntry[] : Type extends 'mutate' ? MutationRecord[] : Type extends 'resize' ? ResizeObserverEntry[] : Type extends 'idle' ? IdleDeadline : Type extends ListenableMediaQuery ? MediaQueryListEvent : Type extends ListenableClickcombo ? MouseEvent : Type extends ListenableLeftClick ? MouseEvent : Type extends ListenableRightClick ? MouseEvent : Type extends ListenablePointercombo ? PointerEvent : Type extends ListenableKeycombo ? KeyboardEvent : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? HTMLElementEventMap[Type] : Type extends keyof Omit<DocumentEventMap, 'resize'> ? DocumentEventMap[Type] : never;
466
+ declare type ListenEffect<Type extends ListenableSupportedType> = Type extends 'intersect' ? (entries: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends 'mutate' ? (records: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends 'resize' ? (entries: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends 'idle' ? (deadline: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends ListenableMediaQuery ? (event: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends (ListenableLeftClick | ListenableRightClick) ? (event: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends (ListenablePointer) ? (event: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends ('keydown' | 'keyup') ? (event: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? (event: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : Type extends keyof Omit<DocumentEventMap, 'resize'> ? (event: ListenEffectParam<Type>, api: ListenEffectApi<Type>) => any : never;
467
+ declare type ListenEffectParam<Type extends ListenableSupportedType> = Type extends 'intersect' ? IntersectionObserverEntry[] : Type extends 'mutate' ? MutationRecord[] : Type extends 'resize' ? ResizeObserverEntry[] : Type extends 'idle' ? IdleDeadline : Type extends ListenableMediaQuery ? MediaQueryListEvent : Type extends ListenableRightClick ? MouseEvent : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? HTMLElementEventMap[Type] : Type extends keyof Omit<DocumentEventMap, 'resize'> ? DocumentEventMap[Type] : never;
468
+ declare type ListenEffectApi<Type extends ListenableSupportedType> = Type extends 'intersect' ? Record<never, never> : Type extends 'mutate' ? Record<never, never> : Type extends 'resize' ? Record<never, never> : Type extends 'idle' ? Record<never, never> : Type extends ListenableMediaQuery ? Record<never, never> : Type extends (ListenableLeftClick | ListenableRightClick) ? MouseEventApi : Type extends (ListenablePointer) ? PointerEventApi : Type extends ('keydown' | 'keyup') ? KeyboardEventApi : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? Record<never, never> : Type extends keyof Omit<DocumentEventMap, 'resize'> ? Record<never, never> : never;
469
+ declare type MouseEventApi = {
470
+ is: (clickcombo: ListenableClickcombo) => boolean;
471
+ };
472
+ declare type PointerEventApi = {
473
+ is: (pointercombo: ListenablePointercombo) => boolean;
474
+ };
475
+ declare type KeyboardEventApi = {
476
+ is: (keycombo: ListenableKeycombo) => boolean;
477
+ };
477
478
  declare type ListenOptions<Type extends ListenableSupportedType> = Type extends 'intersect' ? {
478
479
  observer?: IntersectionObserverInit;
479
480
  } & ObservationListenOptions : Type extends 'mutate' ? {
@@ -484,9 +485,7 @@ declare type ListenOptions<Type extends ListenableSupportedType> = Type extends
484
485
  requestIdleCallback?: IdleRequestOptions;
485
486
  } : Type extends ListenableMediaQuery ? {
486
487
  instantEffect?: (list: MediaQueryList) => any;
487
- } : Type extends ListenableClickcombo ? EventListenOptions : Type extends ListenablePointercombo ? EventListenOptions : Type extends ListenableKeycombo ? {
488
- keyDirection?: 'up' | 'down';
489
- } & EventListenOptions : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? EventListenOptions : Type extends keyof Omit<DocumentEventMap, 'resize'> ? EventListenOptions : never;
488
+ } : Type extends keyof Omit<HTMLElementEventMap, 'resize'> ? EventListenOptions : Type extends keyof Omit<DocumentEventMap, 'resize'> ? EventListenOptions : never;
490
489
  declare type ObservationListenOptions = {
491
490
  target?: Element;
492
491
  };
@@ -511,7 +510,7 @@ declare type ListenableActive<Type extends ListenableSupportedType, Recognizeabl
511
510
  id: number;
512
511
  } : Type extends ListenableMediaQuery ? {
513
512
  target: MediaQueryList;
514
- id: [type: string, effect: ListenEffect<Type>];
513
+ id: [type: string, effect: (param: ListenEffectParam<Type>) => void];
515
514
  } : Type extends ListenableSupportedEventType ? {
516
515
  target: Element | Document;
517
516
  id: ListenableActiveEventId<Type>;
@@ -520,7 +519,7 @@ declare type ListenableActive<Type extends ListenableSupportedType, Recognizeabl
520
519
  };
521
520
  declare type ListenableActiveEventId<Type extends ListenableSupportedEventType> = [
522
521
  type: string,
523
- exceptAndOnlyEffect: ListenEffect<Type>,
522
+ exceptAndOnlyEffect: (param: ListenEffectParam<Type>) => void,
524
523
  optionsOrUseCapture: AddEventListenerOptions | boolean
525
524
  ];
526
525
  declare type ListenableStatus = 'ready' | 'listening' | 'stopped';
@@ -547,9 +546,6 @@ declare class Listenable<Type extends ListenableSupportedType, RecognizeableMeta
547
546
  private idleListen;
548
547
  private recognizeableListen;
549
548
  private documentEventListen;
550
- private pointercomboListen;
551
- private clickcomboListen;
552
- private keycomboListen;
553
549
  private eventListen;
554
550
  private addEventListeners;
555
551
  private listening;
@@ -558,10 +554,6 @@ declare class Listenable<Type extends ListenableSupportedType, RecognizeableMeta
558
554
  }): this;
559
555
  private stopped;
560
556
  }
561
- declare function eventMatchesKeycombo({ event, keycombo }: {
562
- event: KeyboardEvent;
563
- keycombo: ListenableKeycomboItem[];
564
- }): boolean;
565
557
 
566
558
  declare type NavigateableOptions = {
567
559
  initialLocation?: number;
@@ -607,6 +599,10 @@ declare type PickableOptions = {
607
599
  initialPicks?: number | number[];
608
600
  };
609
601
  declare type PickableStatus = 'ready' | 'picked' | 'omitted';
602
+ declare type PickOptions = {
603
+ replace?: 'none' | 'all' | 'fifo' | 'lifo';
604
+ allowsDuplicates?: boolean;
605
+ };
610
606
  declare class Pickable<Item> {
611
607
  constructor(array: Item[], options?: PickableOptions);
612
608
  private computedStatus;
@@ -626,13 +622,12 @@ declare class Pickable<Item> {
626
622
  get status(): PickableStatus;
627
623
  get items(): Item[];
628
624
  private toItems;
625
+ computedMultiple: boolean;
629
626
  get multiple(): boolean;
630
627
  private toPossiblePicks;
631
628
  setArray(array: Item[]): this;
632
629
  setPicks(indexOrIndices: number | number[]): this;
633
- pick(indexOrIndices: number | number[], options?: {
634
- replace?: 'none' | 'all' | 'fifo' | 'lifo';
635
- }): this;
630
+ pick(indexOrIndices: number | number[], options?: PickOptions): this;
636
631
  private picked;
637
632
  omit(indexOrIndices?: number | number[], options?: {
638
633
  reference?: 'array' | 'picks';
@@ -753,4 +748,4 @@ declare class Pipeable {
753
748
  pipeAsync(...fns: ((...args: any[]) => Promise<any>)[]): Promise<any>;
754
749
  }
755
750
 
756
- export { AnimateFrame, AnimateFrameEffect, AnimateOptions, Animateable, AnimateableKeyframe, AnimateableOptions, AnimateableStatus, ArrayFunction, ArrayFunctionAsync, CompleteOptions, Completeable, CompleteableOptions, CompleteableStatus, Copyable, CopyableOptions, CopyableStatus, Delayable, DelayableEffect, DelayableOptions, DelayableStatus, Drawable, DrawableOptions, DrawableState, DrawableStatus, FetchOptions, FetchOptionsApi, Fetchable, FetchableOptions, FetchableStatus, Fullscreenable, FullscreenableGetElement, FullscreenableOptions, FullscreenableStatus, Grantable, GrantableOptions, GrantableStatus, ListenEffect, ListenEffectParam, ListenOptions, Listenable, ListenableActive, ListenableClickcombo, ListenableKeycombo, ListenableKeycomboItem, ListenableOptions, ListenablePointercombo, ListenableStatus, ListenableSupportedEventType, ListenableSupportedType, MapFunction, Navigateable, NavigateableOptions, NavigateableStatus, NumberFunction, ObjectFunction, Pickable, PickableOptions, PickableStatus, Pipeable, RecognizeOptions, Recognizeable, RecognizeableEffectApi, RecognizeableOptions, RecognizeableStatus, Resolveable, ResolveableGetPromise, ResolveableOptions, ResolveableStatus, Sanitizeable, SanitizeableOptions, SanitizeableStatus, Searchable, SearchableOptions, SearchableStatus, Storeable, StoreableOptions, StoreableStatus, StringFunction, createClamp, createClip, createConcat, createDelete, createDetermine, createFilter, createFilterAsync, createForEachAsync, createInsert, createMap, createMapAsync, createReduce, createReduceAsync, createRename, createReorder, createReplace, createReverse, createSlice, createSlug, createSort, createSwap, createToEntries, createUnique, easingsNetInBack, easingsNetInCirc, easingsNetInCubic, easingsNetInExpo, easingsNetInOutBack, easingsNetInOutCirc, easingsNetInOutCubic, easingsNetInOutExpo, easingsNetInOutQuad, easingsNetInOutQuint, easingsNetInOutSine, easingsNetInQuad, easingsNetInQuart, easingsNetInQuint, easingsNetInSine, easingsNetOutBack, easingsNetOutCirc, easingsNetOutCubic, easingsNetOutExpo, easingsNetOutQuad, easingsNetOutQuint, easingsNetOutSine, ensureKeycombo, eventMatchesKeycombo, linear, materialAccelerated, materialDecelerated, materialStandard, toD, toFlattenedD, verouEase, verouEaseIn, verouEaseInOut, verouEaseOut };
751
+ export { AnimateFrame, AnimateFrameEffect, AnimateOptions, Animateable, AnimateableKeyframe, AnimateableOptions, AnimateableStatus, ArrayFunction, ArrayFunctionAsync, CompleteOptions, Completeable, CompleteableOptions, CompleteableStatus, Copyable, CopyableOptions, CopyableStatus, Delayable, DelayableEffect, DelayableOptions, DelayableStatus, Drawable, DrawableOptions, DrawableState, DrawableStatus, FetchOptions, FetchOptionsApi, Fetchable, FetchableOptions, FetchableStatus, Fullscreenable, FullscreenableGetElement, FullscreenableOptions, FullscreenableStatus, Grantable, GrantableOptions, GrantableStatus, ListenEffect, ListenEffectParam, ListenOptions, Listenable, ListenableActive, ListenableClickcombo, ListenableKeycombo, ListenableOptions, ListenablePointercombo, ListenableStatus, ListenableSupportedEventType, ListenableSupportedType, MapFunction, Navigateable, NavigateableOptions, NavigateableStatus, NumberFunction, ObjectFunction, Pickable, PickableOptions, PickableStatus, Pipeable, RecognizeOptions, Recognizeable, RecognizeableEffectApi, RecognizeableOptions, RecognizeableStatus, Resolveable, ResolveableGetPromise, ResolveableOptions, ResolveableStatus, Sanitizeable, SanitizeableOptions, SanitizeableStatus, Searchable, SearchableOptions, SearchableStatus, Storeable, StoreableOptions, StoreableStatus, StringFunction, createClamp, createClip, createConcat, createDelete, createDetermine, createFilter, createFilterAsync, createForEachAsync, createInsert, createMap, createMapAsync, createReduce, createReduceAsync, createRename, createReorder, createReplace, createReverse, createSlice, createSlug, createSort, createSwap, createToEntries, createUnique, easingsNetInBack, easingsNetInCirc, easingsNetInCubic, easingsNetInExpo, easingsNetInOutBack, easingsNetInOutCirc, easingsNetInOutCubic, easingsNetInOutExpo, easingsNetInOutQuad, easingsNetInOutQuint, easingsNetInOutSine, easingsNetInQuad, easingsNetInQuart, easingsNetInQuint, easingsNetInSine, easingsNetOutBack, easingsNetOutCirc, easingsNetOutCubic, easingsNetOutExpo, easingsNetOutQuad, easingsNetOutQuint, easingsNetOutSine, linear, materialAccelerated, materialDecelerated, materialStandard, toD, toFlattenedD, verouEase, verouEaseIn, verouEaseInOut, verouEaseOut };
package/lib/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { reduce, pipe, filter, toArray, slice, concat, unique, map, find, findIndex, some, join, every } from 'lazy-collections';
1
+ import { reduce, pipe, filter, toArray, slice, concat, unique, map, find, findIndex, some, every, join } from 'lazy-collections';
2
2
  import slugify from '@sindresorhus/slugify';
3
3
  import BezierEasing from 'bezier-easing';
4
4
  import { mix } from '@snigo.dev/color';
@@ -280,16 +280,61 @@ const modifiersByAlias = {
280
280
  opt: "alt",
281
281
  option: "alt"
282
282
  };
283
- function createExceptAndOnlyEffect(effect, options) {
283
+ function createExceptAndOnlyEffect(type, effect, options) {
284
284
  const { except = [], only = [] } = options;
285
+ if (type === "keydown" || type === "keyup") {
286
+ return (event) => {
287
+ const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true], api = {
288
+ is: (keycombo) => eventMatchesKeycombo(event, ensureKeycombo(keycombo))
289
+ };
290
+ if (matchesOnly) {
291
+ effect(event, api);
292
+ return;
293
+ }
294
+ if (only.length === 0 && !matchesExcept) {
295
+ effect(event, api);
296
+ return;
297
+ }
298
+ };
299
+ }
300
+ if (type === "click" || type === "mousedown" || type === "mouseup" || type === "dblclick" || type === "contextmenu") {
301
+ return (event) => {
302
+ const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true], api = {
303
+ is: (clickcombo) => eventMatchesClickcombo(event, ensureClickcombo(clickcombo))
304
+ };
305
+ if (matchesOnly) {
306
+ effect(event, api);
307
+ return;
308
+ }
309
+ if (only.length === 0 && !matchesExcept) {
310
+ effect(event, api);
311
+ return;
312
+ }
313
+ };
314
+ }
315
+ if (type === "pointerdown" || type === "pointerup") {
316
+ return (event) => {
317
+ const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true], api = {
318
+ is: (pointercombo) => eventMatchesPointercombo(event, ensurePointercombo(pointercombo))
319
+ };
320
+ if (matchesOnly) {
321
+ effect(event, api);
322
+ return;
323
+ }
324
+ if (only.length === 0 && !matchesExcept) {
325
+ effect(event, api);
326
+ return;
327
+ }
328
+ };
329
+ }
285
330
  return (event) => {
286
331
  const { target } = event, [matchesOnly, matchesExcept] = target instanceof Element ? createMap((selectors) => some((selector) => target.matches(selector))(selectors))([only, except]) : [false, true];
287
332
  if (matchesOnly) {
288
- effect(event);
333
+ effect(event, {});
289
334
  return;
290
335
  }
291
336
  if (only.length === 0 && !matchesExcept) {
292
- effect(event);
337
+ effect(event, {});
293
338
  return;
294
339
  }
295
340
  };
@@ -338,19 +383,13 @@ class Recognizeable {
338
383
  maxSequenceLength;
339
384
  effects;
340
385
  effectApi;
341
- toType;
342
- constructor(sequence, options = { effectsIncludeCombos: true }) {
386
+ constructor(sequence, options = {}) {
343
387
  const defaultOptions = {
344
388
  maxSequenceLength: true,
345
- effectsIncludeCombos: true,
346
389
  effects: {}
347
390
  };
348
391
  this.maxSequenceLength = options?.maxSequenceLength || defaultOptions.maxSequenceLength;
349
- this.effects = new Map(isFunction(options?.effects) ? options.effects(createDefineEffect()) : Object.entries(options?.effects || defaultOptions.effects));
350
- this.toType = createToType({
351
- effectsIncludeCombos: options.effectsIncludeCombos,
352
- effects: this.effects
353
- });
392
+ this.effects = new Map(Object.entries(options?.effects || defaultOptions.effects));
354
393
  this.resetComputedMetadata();
355
394
  this.setSequence(sequence);
356
395
  this.effectApi = {
@@ -393,14 +432,13 @@ class Recognizeable {
393
432
  this.computedSequence = sequence;
394
433
  return this;
395
434
  }
396
- recognize(sequenceItem, { onRecognized } = {}) {
435
+ recognize(sequenceItem, api, { onRecognized } = {}) {
397
436
  this.recognizing();
398
437
  const type = this.toType(sequenceItem), excess = isNumber(this.maxSequenceLength) ? Math.max(0, this.sequence.length - this.maxSequenceLength) : 0, newSequence = createConcat(createSlice(excess)(this.sequence), [sequenceItem])([]);
399
- this.effectApi.sequenceItem = sequenceItem;
400
438
  this.effectApi.getSequence = () => newSequence;
401
439
  this.effectApi.onRecognized = onRecognized || (() => {
402
440
  });
403
- this.effects.get(type)?.(this.effectApi);
441
+ this.effects.get(type)?.(sequenceItem, { ...api, ...this.effectApi });
404
442
  switch (this.status) {
405
443
  case "denied":
406
444
  this.resetComputedMetadata();
@@ -416,34 +454,7 @@ class Recognizeable {
416
454
  recognizing() {
417
455
  this.computedStatus = "recognizing";
418
456
  }
419
- }
420
- function createDefineEffect() {
421
- return (type, effect) => {
422
- return [type, effect];
423
- };
424
- }
425
- function createToType({
426
- effectsIncludeCombos,
427
- effects
428
- }) {
429
- const effectLeftclickcombos = [], effectRightclickcombos = [], effectKeycombos = [];
430
- if (effectsIncludeCombos) {
431
- for (const [effectKey] of effects) {
432
- const implementation = toImplementation(effectKey);
433
- switch (implementation) {
434
- case "leftclickcombo":
435
- effectLeftclickcombos.push(ensureClickcombo(effectKey));
436
- break;
437
- case "rightclickcombo":
438
- effectRightclickcombos.push(ensureClickcombo(effectKey));
439
- break;
440
- case "keycombo":
441
- effectKeycombos.push(ensureKeycombo(effectKey));
442
- break;
443
- }
444
- }
445
- }
446
- return function toType(sequenceItem) {
457
+ toType(sequenceItem) {
447
458
  if (isArray(sequenceItem)) {
448
459
  if (sequenceItem[0] instanceof IntersectionObserverEntry) {
449
460
  return "intersect";
@@ -461,38 +472,10 @@ function createToType({
461
472
  if ("didTimeout" in sequenceItem) {
462
473
  return "idle";
463
474
  }
464
- if (effectLeftclickcombos?.length > 0) {
465
- if (leftclickcomboEventTypes.has(sequenceItem.type)) {
466
- for (const clickcombo of effectLeftclickcombos) {
467
- if (eventMatchesClickcombo({ event: sequenceItem, clickcombo })) {
468
- return toJoinedClickcombo(clickcombo);
469
- }
470
- }
471
- }
472
- }
473
- if (effectRightclickcombos?.length > 0) {
474
- if (rightclickComboEventTypes.has(sequenceItem.type)) {
475
- for (const clickcombo of effectRightclickcombos) {
476
- if (eventMatchesClickcombo({ event: sequenceItem, clickcombo })) {
477
- return toJoinedClickcombo(clickcombo);
478
- }
479
- }
480
- }
481
- }
482
- if (effectKeycombos?.length > 0) {
483
- if (keycomboEventTypes.has(sequenceItem.type)) {
484
- for (const keycombo of effectKeycombos) {
485
- if (eventMatchesKeycombo({ event: sequenceItem, keycombo })) {
486
- return toJoinedKeycombo(keycombo);
487
- }
488
- }
489
- }
490
- }
491
475
  return sequenceItem.type;
492
476
  }
493
- };
477
+ }
494
478
  }
495
- const leftclickcomboEventTypes = /* @__PURE__ */ new Set(["click", "mousedown", "mouseup", "dblclick"]), rightclickComboEventTypes = /* @__PURE__ */ new Set(["contextmenu"]), keycomboEventTypes = /* @__PURE__ */ new Set(["keydown", "keyup"]), toJoinedClickcombo = join("+"), toJoinedKeycombo = pipe(map(({ name }) => name), toJoinedClickcombo);
496
479
 
497
480
  class Listenable {
498
481
  computedRecognizeable;
@@ -500,15 +483,8 @@ class Listenable {
500
483
  computedActive;
501
484
  constructor(type, options) {
502
485
  if (type === "recognizeable") {
503
- const recognizeableOptions = {
504
- ...options?.recognizeable || {},
505
- effects: isFunction(options?.recognizeable?.effects) ? createReduce((effects, [type2, effect]) => {
506
- effects[type2] = effect;
507
- return effects;
508
- }, {})(options.recognizeable.effects(createDefineEffect())) : options?.recognizeable?.effects || {}
509
- };
510
- this.computedRecognizeable = new Recognizeable([], recognizeableOptions);
511
- this.recognizeableEffectsKeys = Object.keys(recognizeableOptions.effects);
486
+ this.computedRecognizeable = new Recognizeable([], options?.recognizeable || {});
487
+ this.recognizeableEffectsKeys = Object.keys(options?.recognizeable?.effects || {});
512
488
  }
513
489
  this.computedActive = /* @__PURE__ */ new Set();
514
490
  this.setType(type);
@@ -564,16 +540,6 @@ class Listenable {
564
540
  case "documentevent":
565
541
  this.documentEventListen(effect, options);
566
542
  break;
567
- case "keycombo":
568
- this.keycomboListen(effect, options);
569
- break;
570
- case "leftclickcombo":
571
- case "rightclickcombo":
572
- this.clickcomboListen(effect, options);
573
- break;
574
- case "pointercombo":
575
- this.pointercomboListen(effect, options);
576
- break;
577
543
  case "event":
578
544
  this.eventListen(effect, options);
579
545
  break;
@@ -601,18 +567,19 @@ class Listenable {
601
567
  if (isFunction(options.instantEffect)) {
602
568
  options.instantEffect(target);
603
569
  }
604
- target.addEventListener("change", effect);
605
- this.active.add({ target, id: ["change", effect] });
570
+ const withApi = (event) => effect(event, {});
571
+ target.addEventListener("change", withApi);
572
+ this.active.add({ target, id: ["change", withApi] });
606
573
  }
607
574
  idleListen(effect, options) {
608
- const { requestIdleCallback } = options, id = window.requestIdleCallback(effect, requestIdleCallback);
575
+ const { requestIdleCallback } = options, id = window.requestIdleCallback((deadline) => effect(deadline, {}), requestIdleCallback);
609
576
  this.active.add({ target: window, id });
610
577
  }
611
578
  recognizeableListen(effect, options) {
612
- const guardedEffect = (sequenceItem) => {
613
- this.recognizeable.recognize(sequenceItem, { onRecognized: effect });
579
+ const guardedEffect = (sequenceItem, api) => {
580
+ this.recognizeable.recognize(sequenceItem, api, { onRecognized: (sequenceItem2) => effect(sequenceItem2, api) });
614
581
  if (this.recognizeable.status === "recognized") {
615
- effect(sequenceItem);
582
+ effect(sequenceItem, api);
616
583
  }
617
584
  };
618
585
  for (const type of this.recognizeableEffectsKeys) {
@@ -628,44 +595,8 @@ class Listenable {
628
595
  };
629
596
  this.eventListen(effect, ensuredOptions);
630
597
  }
631
- pointercomboListen(effect, options) {
632
- const pointercombo = ensurePointercombo(this.type), guardedEffect = (event) => {
633
- if (eventMatchesPointercombo({ event, pointercombo })) {
634
- effect(event);
635
- }
636
- };
637
- this.eventListen(guardedEffect, options);
638
- }
639
- clickcomboListen(effect, options) {
640
- const clickcombo = ensureClickcombo(this.type), guardedEffect = (event) => {
641
- if (eventMatchesClickcombo({ event, clickcombo })) {
642
- effect(event);
643
- }
644
- };
645
- this.eventListen(guardedEffect, options);
646
- }
647
- keycomboListen(effect, options) {
648
- const keycombo = ensureKeycombo(this.type), guardedEffect = (event) => {
649
- if (eventMatchesKeycombo({ event, keycombo })) {
650
- effect(event);
651
- }
652
- };
653
- this.eventListen(guardedEffect, options);
654
- }
655
598
  eventListen(effect, options) {
656
- const type = (() => {
657
- switch (this.implementation) {
658
- case "keycombo":
659
- return `key${options.keyDirection || "down"}`;
660
- case "leftclickcombo":
661
- return this.type.match(/(\w+)$/)[1];
662
- case "rightclickcombo":
663
- return "contextmenu";
664
- default:
665
- return this.type;
666
- }
667
- })();
668
- const { exceptAndOnlyEffect, effectOptions } = toAddEventListenerParams(effect, options), eventListeners = [[type, exceptAndOnlyEffect, ...effectOptions]];
599
+ const { exceptAndOnlyEffect, effectOptions } = toAddEventListenerParams(this.type, effect, options), eventListeners = [[this.type, exceptAndOnlyEffect, ...effectOptions]];
669
600
  this.addEventListeners(eventListeners, options);
670
601
  }
671
602
  addEventListeners(eventListeners, options) {
@@ -756,22 +687,6 @@ const predicatesByImplementation = /* @__PURE__ */ new Map([
756
687
  "documentevent",
757
688
  (type) => documentEvents.has(type)
758
689
  ],
759
- [
760
- "keycombo",
761
- (type) => implementationREs.keycombo.test(type)
762
- ],
763
- [
764
- "leftclickcombo",
765
- (type) => implementationREs.leftclickcombo.test(type)
766
- ],
767
- [
768
- "rightclickcombo",
769
- (type) => implementationREs.rightclickcombo.test(type)
770
- ],
771
- [
772
- "pointercombo",
773
- (type) => implementationREs.pointercombo.test(type)
774
- ],
775
690
  [
776
691
  "event",
777
692
  () => true
@@ -786,17 +701,13 @@ const documentEvents = /* @__PURE__ */ new Set([
786
701
  "visibilitychange"
787
702
  ]);
788
703
  const implementationREs = {
789
- mediaquery: /^\(.+\)$/,
790
- keycombo: /^((!?([a-zA-Z0-9,<.>/?;:'"[{\]}\\|`~!@#$%^&*()-_=+]|tab|space|arrow|vertical|horizontal|up|right|down|left|enter|backspace|esc|home|end|pagedown|pageup|capslock|f[0-9]{1,2}|camera|delete|cmd|command|meta|shift|ctrl|control|alt|opt|option))\+)*(!?([a-zA-Z0-9,<.>/?;:'"[{\]}\\|`~!@#$%^&*()-_=+]|tab|space|arrow|vertical|horizontal|up|right|down|left|enter|backspace|esc|home|end|pagedown|pageup|capslock|f[0-9]{1,2}|camera|delete|cmd|command|meta|shift|ctrl|control|alt|opt|option))$/,
791
- leftclickcombo: /^(!?((cmd|command|meta|shift|ctrl|control|alt|opt|option))\+){0,4}!?(click|mousedown|mouseup|dblclick)$/,
792
- rightclickcombo: /^(!?((cmd|command|meta|shift|ctrl|control|alt|opt|option))\+){0,4}!?(rightclick|contextmenu)$/,
793
- pointercombo: /^(!?((cmd|command|meta|shift|ctrl|control|alt|opt|option))\+){0,4}!?(pointerdown|pointerup)$/
704
+ mediaquery: /^\(.+\)$/
794
705
  };
795
- function toAddEventListenerParams(effect, options) {
796
- const { addEventListener, useCapture } = options, exceptAndOnlyEffect = createExceptAndOnlyEffect(effect, options), effectOptions = [addEventListener || useCapture];
706
+ function toAddEventListenerParams(type, effect, options) {
707
+ const { addEventListener, useCapture } = options, exceptAndOnlyEffect = createExceptAndOnlyEffect(type, effect, options), effectOptions = [addEventListener || useCapture];
797
708
  return { exceptAndOnlyEffect, effectOptions };
798
709
  }
799
- function eventMatchesKeycombo({ event, keycombo }) {
710
+ function eventMatchesKeycombo(event, keycombo) {
800
711
  return every(({ name, type }, index) => {
801
712
  switch (type) {
802
713
  case "singleCharacter":
@@ -880,10 +791,10 @@ const predicatesByArrow = /* @__PURE__ */ new Map([
880
791
  const arrows = /* @__PURE__ */ new Set(["arrowup", "arrowright", "arrowdown", "arrowleft"]);
881
792
  const verticalArrows = /* @__PURE__ */ new Set(["arrowup", "arrowdown"]);
882
793
  const horizontalArrows = /* @__PURE__ */ new Set(["arrowright", "arrowleft"]);
883
- function eventMatchesClickcombo({ event, clickcombo }) {
794
+ function eventMatchesClickcombo(event, clickcombo) {
884
795
  return every((name) => fromComboItemNameToType(name) === "click" || name.startsWith("!") && !isModified({ alias: name.slice(1), event }) || !name.startsWith("!") && isModified({ alias: name, event }))(clickcombo);
885
796
  }
886
- function eventMatchesPointercombo({ event, pointercombo }) {
797
+ function eventMatchesPointercombo(event, pointercombo) {
887
798
  return every((name) => fromComboItemNameToType(name) === "pointer" || name.startsWith("!") && !isModified({ alias: name.slice(1), event }) || !name.startsWith("!") && isModified({ alias: name, event }))(pointercombo);
888
799
  }
889
800
  const observerAssertionsByType = {
@@ -2693,6 +2604,10 @@ class Navigateable {
2693
2604
  const defaultOptions$1 = {
2694
2605
  initialPicks: []
2695
2606
  };
2607
+ const defaultPickOptions = {
2608
+ replace: "none",
2609
+ allowsDuplicates: false
2610
+ };
2696
2611
  class Pickable {
2697
2612
  constructor(array, options = {}) {
2698
2613
  this.setArray(array);
@@ -2738,8 +2653,9 @@ class Pickable {
2738
2653
  return this.toItems(this.picks);
2739
2654
  }
2740
2655
  toItems = createMap((index) => this.array[index]);
2656
+ computedMultiple;
2741
2657
  get multiple() {
2742
- return this.picks.length > 1;
2658
+ return this.computedMultiple;
2743
2659
  }
2744
2660
  toPossiblePicks;
2745
2661
  setArray(array) {
@@ -2751,41 +2667,42 @@ class Pickable {
2751
2667
  return this.pick(indexOrIndices);
2752
2668
  }
2753
2669
  pick(indexOrIndices, options = {}) {
2754
- const { replace = "none" } = options;
2670
+ const { replace, allowsDuplicates } = { ...defaultPickOptions, ...options };
2755
2671
  this.computedPicks = new Pipeable(indexOrIndices).pipe(ensureIndices, this.toPossiblePicks, (possiblePicks) => {
2756
2672
  if (replace === "all") {
2757
- return toUnique(possiblePicks);
2673
+ return allowsDuplicates ? possiblePicks : toUnique(possiblePicks);
2758
2674
  }
2759
- const possibleWithoutDuplicates = createFilter((possiblePick) => typeof find((pick) => pick === possiblePick)(this.picks || []) !== "number")(possiblePicks);
2675
+ const maybeWithoutDuplicates = allowsDuplicates ? possiblePicks : createFilter((possiblePick) => typeof find((pick) => pick === possiblePick)(this.picks || []) !== "number")(possiblePicks);
2760
2676
  switch (replace) {
2761
2677
  case "none":
2762
- return createConcat(this.picks || [], possibleWithoutDuplicates)([]);
2678
+ return createConcat(this.picks || [], maybeWithoutDuplicates)([]);
2763
2679
  case "fifo":
2764
- if (possibleWithoutDuplicates.length === 0) {
2680
+ if (maybeWithoutDuplicates.length === 0) {
2765
2681
  return this.picks;
2766
2682
  }
2767
- if (possibleWithoutDuplicates.length === this.picks.length) {
2768
- return possibleWithoutDuplicates;
2683
+ if (maybeWithoutDuplicates.length === this.picks.length) {
2684
+ return maybeWithoutDuplicates;
2769
2685
  }
2770
- if (possibleWithoutDuplicates.length > this.picks.length) {
2771
- return createSlice(possibleWithoutDuplicates.length - this.picks.length)(possibleWithoutDuplicates);
2686
+ if (maybeWithoutDuplicates.length > this.picks.length) {
2687
+ return createSlice(maybeWithoutDuplicates.length - this.picks.length)(maybeWithoutDuplicates);
2772
2688
  }
2773
- return new Pipeable(this.picks).pipe(createSlice(possibleWithoutDuplicates.length), createConcat(possibleWithoutDuplicates));
2689
+ return new Pipeable(this.picks).pipe(createSlice(maybeWithoutDuplicates.length), createConcat(maybeWithoutDuplicates));
2774
2690
  case "lifo":
2775
- if (possibleWithoutDuplicates.length === 0) {
2691
+ if (maybeWithoutDuplicates.length === 0) {
2776
2692
  return this.picks;
2777
2693
  }
2778
- if (possibleWithoutDuplicates.length === this.picks.length) {
2779
- return possibleWithoutDuplicates;
2694
+ if (maybeWithoutDuplicates.length === this.picks.length) {
2695
+ return maybeWithoutDuplicates;
2780
2696
  }
2781
- if (possibleWithoutDuplicates.length > this.picks.length) {
2782
- return createSlice(0, possibleWithoutDuplicates.length - this.picks.length + 1)(possibleWithoutDuplicates);
2697
+ if (maybeWithoutDuplicates.length > this.picks.length) {
2698
+ return createSlice(0, maybeWithoutDuplicates.length - this.picks.length + 1)(maybeWithoutDuplicates);
2783
2699
  }
2784
- return new Pipeable(this.picks).pipe(createSlice(0, this.picks.length - possibleWithoutDuplicates.length), createConcat(possibleWithoutDuplicates));
2700
+ return new Pipeable(this.picks).pipe(createSlice(0, this.picks.length - maybeWithoutDuplicates.length), createConcat(maybeWithoutDuplicates));
2785
2701
  }
2786
2702
  });
2787
2703
  this.computedFirst = Math.min(...this.picks);
2788
2704
  this.computedLast = Math.max(...this.picks);
2705
+ this.computedMultiple = toUnique(this.picks).length > 1;
2789
2706
  this.picked();
2790
2707
  return this;
2791
2708
  }
@@ -2797,6 +2714,7 @@ class Pickable {
2797
2714
  this.computedPicks = [];
2798
2715
  this.computedFirst = void 0;
2799
2716
  this.computedLast = void 0;
2717
+ this.computedMultiple = false;
2800
2718
  this.omitted();
2801
2719
  return this;
2802
2720
  }
@@ -2804,6 +2722,7 @@ class Pickable {
2804
2722
  this.computedPicks = createFilter((pick, index) => options.reference === "array" ? isUndefined(find((omit) => pick === omit)(omits)) : isUndefined(find((omit) => index === omit)(omits)))(this.computedPicks);
2805
2723
  this.computedFirst = Math.min(...this.picks);
2806
2724
  this.computedLast = Math.max(...this.picks);
2725
+ this.computedMultiple = toUnique(this.picks).length > 1;
2807
2726
  this.omitted();
2808
2727
  return this;
2809
2728
  }
@@ -3029,4 +2948,4 @@ class Storeable {
3029
2948
  }
3030
2949
  }
3031
2950
 
3032
- export { Animateable, Completeable, Copyable, Delayable, Drawable, Fetchable, Fullscreenable, Grantable, Listenable, Navigateable, Pickable, Pipeable, Recognizeable, Resolveable, Sanitizeable, Searchable, Storeable, createClamp, createClip, createConcat, createDelete, createDetermine, createFilter, createFilterAsync, createForEachAsync, createInsert, createMap, createMapAsync, createReduce, createReduceAsync, createRename, createReorder, createReplace, createReverse, createSlice, createSlug, createSort, createSwap, createToEntries, createUnique, easingsNetInBack, easingsNetInCirc, easingsNetInCubic, easingsNetInExpo, easingsNetInOutBack, easingsNetInOutCirc, easingsNetInOutCubic, easingsNetInOutExpo, easingsNetInOutQuad, easingsNetInOutQuint, easingsNetInOutSine, easingsNetInQuad, easingsNetInQuart, easingsNetInQuint, easingsNetInSine, easingsNetOutBack, easingsNetOutCirc, easingsNetOutCubic, easingsNetOutExpo, easingsNetOutQuad, easingsNetOutQuint, easingsNetOutSine, ensureKeycombo, eventMatchesKeycombo, linear, materialAccelerated, materialDecelerated, materialStandard, toD, toFlattenedD, verouEase, verouEaseIn, verouEaseInOut, verouEaseOut };
2951
+ export { Animateable, Completeable, Copyable, Delayable, Drawable, Fetchable, Fullscreenable, Grantable, Listenable, Navigateable, Pickable, Pipeable, Recognizeable, Resolveable, Sanitizeable, Searchable, Storeable, createClamp, createClip, createConcat, createDelete, createDetermine, createFilter, createFilterAsync, createForEachAsync, createInsert, createMap, createMapAsync, createReduce, createReduceAsync, createRename, createReorder, createReplace, createReverse, createSlice, createSlug, createSort, createSwap, createToEntries, createUnique, easingsNetInBack, easingsNetInCirc, easingsNetInCubic, easingsNetInExpo, easingsNetInOutBack, easingsNetInOutCirc, easingsNetInOutCubic, easingsNetInOutExpo, easingsNetInOutQuad, easingsNetInOutQuint, easingsNetInOutSine, easingsNetInQuad, easingsNetInQuart, easingsNetInQuint, easingsNetInSine, easingsNetOutBack, easingsNetOutCirc, easingsNetOutCubic, easingsNetOutExpo, easingsNetOutQuad, easingsNetOutQuint, easingsNetOutSine, linear, materialAccelerated, materialDecelerated, materialStandard, toD, toFlattenedD, verouEase, verouEaseIn, verouEaseInOut, verouEaseOut };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baleada/logic",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "UI logic for the Baleada toolkit",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "lib/index.js",