@wix/interact 2.0.3 → 2.1.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.
Files changed (43) hide show
  1. package/dist/cjs/index.js +1 -1
  2. package/dist/cjs/react.js +1 -1
  3. package/dist/cjs/web.js +1 -1
  4. package/dist/es/index.js +1 -1
  5. package/dist/es/react.js +2 -2
  6. package/dist/es/web.js +2 -2
  7. package/dist/{index-BfcN_rkn.mjs → index-BP07b-Y1.mjs} +1202 -751
  8. package/dist/index-BP07b-Y1.mjs.map +1 -0
  9. package/dist/index-D4orAyUu.js +18 -0
  10. package/dist/index-D4orAyUu.js.map +1 -0
  11. package/dist/tsconfig.build.tsbuildinfo +1 -1
  12. package/dist/types/core/Interact.d.ts +12 -1
  13. package/dist/types/core/Interact.d.ts.map +1 -1
  14. package/dist/types/core/add.d.ts.map +1 -1
  15. package/dist/types/core/remove.d.ts.map +1 -1
  16. package/dist/types/handlers/animationEnd.d.ts +1 -1
  17. package/dist/types/handlers/animationEnd.d.ts.map +1 -1
  18. package/dist/types/handlers/effectHandlers.d.ts +2 -1
  19. package/dist/types/handlers/effectHandlers.d.ts.map +1 -1
  20. package/dist/types/handlers/eventTrigger.d.ts +1 -1
  21. package/dist/types/handlers/eventTrigger.d.ts.map +1 -1
  22. package/dist/types/handlers/viewEnter.d.ts +1 -1
  23. package/dist/types/handlers/viewEnter.d.ts.map +1 -1
  24. package/dist/types/types.d.ts +29 -2
  25. package/dist/types/types.d.ts.map +1 -1
  26. package/docs/api/types.md +114 -0
  27. package/docs/examples/README.md +10 -1
  28. package/docs/examples/list-patterns.md +148 -0
  29. package/docs/guides/README.md +5 -0
  30. package/docs/guides/sequences.md +421 -0
  31. package/package.json +2 -2
  32. package/rules/MASTER-CLEANUP-PLAN.md +286 -0
  33. package/rules/click.md +124 -32
  34. package/rules/full-lean.md +93 -7
  35. package/rules/hover.md +142 -153
  36. package/rules/integration.md +83 -82
  37. package/rules/pointermove.md +32 -57
  38. package/rules/scroll-list.md +82 -280
  39. package/rules/viewenter.md +158 -253
  40. package/rules/viewprogress.md +139 -845
  41. package/dist/index-BfcN_rkn.mjs.map +0 -1
  42. package/dist/index-HXLBEIjG.js +0 -18
  43. package/dist/index-HXLBEIjG.js.map +0 -1
@@ -12,6 +12,7 @@ Comprehensive examples of list and grid animations using `@wix/interact`. All ex
12
12
  - [Filtering & Sorting](#filtering--sorting)
13
13
  - [Grid Layouts](#grid-layouts)
14
14
  - [Real-World Examples](#real-world-examples)
15
+ - [Sequence-Based Staggering](#sequence-based-staggering)
15
16
 
16
17
  ## Entrance Animations
17
18
 
@@ -799,8 +800,155 @@ const config = {
799
800
  </interact-element>
800
801
  ```
801
802
 
803
+ ## Sequence-Based Staggering
804
+
805
+ The `sequences` config provides built-in stagger support with easing-driven delay distribution — no CSS `animation-delay` hacks needed.
806
+
807
+ ### 17. Staggered List Entrance with Sequences
808
+
809
+ ```typescript
810
+ const config = {
811
+ interactions: [
812
+ {
813
+ key: 'cards',
814
+ trigger: 'viewEnter',
815
+ listContainer: '.card-grid',
816
+ params: { type: 'once', threshold: 0.1 },
817
+ sequences: [
818
+ {
819
+ offset: 80,
820
+ offsetEasing: 'quadIn',
821
+ effects: [
822
+ {
823
+ key: 'cards',
824
+ listContainer: '.card-grid',
825
+ effectId: 'card-entrance',
826
+ },
827
+ ],
828
+ },
829
+ ],
830
+ },
831
+ ],
832
+ effects: {
833
+ 'card-entrance': {
834
+ duration: 600,
835
+ easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
836
+ keyframeEffect: {
837
+ name: 'card-entrance',
838
+ keyframes: [
839
+ { opacity: '0', transform: 'translateY(40px) scale(0.95)' },
840
+ { opacity: '1', transform: 'translateY(0) scale(1)' },
841
+ ],
842
+ },
843
+ },
844
+ },
845
+ };
846
+
847
+ Interact.create(config);
848
+ ```
849
+
850
+ ### 18. Dynamic List Items with Sequences
851
+
852
+ New items added to the DOM automatically join the Sequence with recalculated stagger offsets:
853
+
854
+ ```typescript
855
+ const config = {
856
+ interactions: [
857
+ {
858
+ key: 'feed',
859
+ trigger: 'viewEnter',
860
+ listContainer: '.feed-items',
861
+ params: { type: 'repeat' },
862
+ sequences: [
863
+ {
864
+ offset: 60,
865
+ offsetEasing: 'sineOut',
866
+ effects: [
867
+ {
868
+ key: 'feed',
869
+ listContainer: '.feed-items',
870
+ keyframeEffect: {
871
+ name: 'feed-entrance',
872
+ keyframes: [
873
+ { opacity: '0', transform: 'translateY(20px)' },
874
+ { opacity: '1', transform: 'translateY(0)' },
875
+ ],
876
+ },
877
+ duration: 400,
878
+ easing: 'ease-out',
879
+ },
880
+ ],
881
+ },
882
+ ],
883
+ },
884
+ ],
885
+ effects: {},
886
+ };
887
+
888
+ Interact.create(config);
889
+ ```
890
+
891
+ ### 19. Easing Comparison for List Stagger
892
+
893
+ Different `offsetEasing` values produce distinct stagger patterns:
894
+
895
+ ```typescript
896
+ // Linear: even spacing (0, 80, 160, 240, 320ms)
897
+ { offset: 80, offsetEasing: 'linear' }
898
+
899
+ // quadIn: slow start then rapid (0, 20, 80, 180, 320ms)
900
+ { offset: 80, offsetEasing: 'quadIn' }
901
+
902
+ // sineOut: fast start then gradual (0, 125, 227, 302, 320ms)
903
+ { offset: 80, offsetEasing: 'sineOut' }
904
+ ```
905
+
906
+ ### 20. Reusable Sequences with `sequenceId`
907
+
908
+ Define a sequence once, reference it from multiple interactions:
909
+
910
+ ```typescript
911
+ const config = {
912
+ sequences: {
913
+ 'list-stagger': {
914
+ offset: 100,
915
+ offsetEasing: 'quadIn',
916
+ effects: [{ effectId: 'fade-up' }],
917
+ },
918
+ },
919
+ interactions: [
920
+ {
921
+ key: 'section-a',
922
+ trigger: 'viewEnter',
923
+ listContainer: '.list-a',
924
+ sequences: [{ sequenceId: 'list-stagger' }],
925
+ },
926
+ {
927
+ key: 'section-b',
928
+ trigger: 'viewEnter',
929
+ listContainer: '.list-b',
930
+ sequences: [{ sequenceId: 'list-stagger', offset: 150 }], // Override offset
931
+ },
932
+ ],
933
+ effects: {
934
+ 'fade-up': {
935
+ duration: 500,
936
+ easing: 'ease-out',
937
+ keyframeEffect: {
938
+ name: 'fade-up',
939
+ keyframes: [
940
+ { opacity: '0', transform: 'translateY(20px)' },
941
+ { opacity: '1', transform: 'translateY(0)' },
942
+ ],
943
+ },
944
+ },
945
+ },
946
+ };
947
+ ```
948
+
802
949
  ## See Also
803
950
 
951
+ - [Sequences & Staggering Guide](../guides/sequences.md)
804
952
  - [Lists and Dynamic Content Guide](../guides/lists-and-dynamic-content.md)
805
953
  - [Element Selection](../api/element-selection.md)
806
954
  - [Performance Guide](../guides/performance.md)
@@ -36,6 +36,10 @@ Creating responsive interactions that adapt to different screen sizes and condit
36
36
 
37
37
  Working with dynamic lists, list containers, staggered animations, and automatic mutation tracking.
38
38
 
39
+ ### 🎼 [Sequences & Staggering](./sequences.md)
40
+
41
+ Coordinate multiple effects with staggered timing using easing-driven delay offsets. Covers inline and reusable sequences, cross-element orchestration, `listContainer` integration, dynamic add/remove, and conditional sequences.
42
+
39
43
  ## Learning Path
40
44
 
41
45
  If you're new to `@wix/interact`, we recommend following the guides in this order:
@@ -48,6 +52,7 @@ If you're new to `@wix/interact`, we recommend following the guides in this orde
48
52
  6. **Lists and Dynamic Content** - Work with repeating elements
49
53
  7. **State Management** - Advanced state handling
50
54
  8. **Conditions and Media Queries** - Responsive design
55
+ 9. **Sequences & Staggering** - Coordinated multi-effect timing
51
56
 
52
57
  ## Prerequisites
53
58
 
@@ -0,0 +1,421 @@
1
+ # Sequences & Staggering
2
+
3
+ Sequences let you coordinate multiple effects as a single timeline with staggered delay offsets. Built on the `@wix/motion` `Sequence` class, they provide easing-driven timing distribution across effects — ideal for list entrances, multi-element orchestrations, and any pattern requiring coordinated animation timing.
4
+
5
+ ## What is a Sequence?
6
+
7
+ A Sequence groups multiple effects and applies calculated delay offsets to each one, so they play back in a staggered pattern. The stagger timing is shaped by an easing function, producing natural-feeling cascades rather than uniform delays.
8
+
9
+ ```
10
+ offset[i] = easing(i / last) * last * offsetMs
11
+ ```
12
+
13
+ For example, with 5 effects and `offset: 200`:
14
+
15
+ | Easing | Computed delays | Feel |
16
+ | --------- | --------------------- | ------------------------ |
17
+ | `linear` | 0, 200, 400, 600, 800 | Even spacing |
18
+ | `quadIn` | 0, 50, 200, 450, 800 | Slow start, then rapid |
19
+ | `sineOut` | 0, 306, 565, 739, 800 | Fast start, then gradual |
20
+
21
+ ## Config Structure
22
+
23
+ Sequences can be defined at two levels:
24
+
25
+ ### Reusable Sequences (`InteractConfig.sequences`)
26
+
27
+ Define named sequences in the top-level `sequences` map, then reference them by ID from any interaction:
28
+
29
+ ```typescript
30
+ const config: InteractConfig = {
31
+ sequences: {
32
+ 'card-stagger': {
33
+ offset: 150,
34
+ offsetEasing: 'quadIn',
35
+ effects: [{ effectId: 'fade-up' }],
36
+ },
37
+ },
38
+ interactions: [
39
+ {
40
+ key: 'cards',
41
+ trigger: 'viewEnter',
42
+ listContainer: '.card-grid',
43
+ sequences: [{ sequenceId: 'card-stagger' }],
44
+ },
45
+ ],
46
+ effects: {
47
+ 'fade-up': {
48
+ duration: 600,
49
+ easing: 'ease-out',
50
+ keyframeEffect: {
51
+ name: 'fade-up',
52
+ keyframes: [
53
+ { opacity: 0, transform: 'translateY(20px)' },
54
+ { opacity: 1, transform: 'translateY(0)' },
55
+ ],
56
+ },
57
+ },
58
+ },
59
+ };
60
+ ```
61
+
62
+ ### Inline Sequences (`Interaction.sequences`)
63
+
64
+ Define sequences directly on an interaction:
65
+
66
+ ```typescript
67
+ const config: InteractConfig = {
68
+ interactions: [
69
+ {
70
+ key: 'items',
71
+ trigger: 'viewEnter',
72
+ listContainer: '.item-list',
73
+ sequences: [
74
+ {
75
+ offset: 100,
76
+ offsetEasing: 'sineOut',
77
+ effects: [
78
+ {
79
+ duration: 500,
80
+ keyframeEffect: {
81
+ name: 'slide-in',
82
+ keyframes: [
83
+ { opacity: 0, transform: 'translateX(-30px)' },
84
+ { opacity: 1, transform: 'translateX(0)' },
85
+ ],
86
+ },
87
+ },
88
+ ],
89
+ },
90
+ ],
91
+ },
92
+ ],
93
+ effects: {},
94
+ };
95
+ ```
96
+
97
+ ### Combining Effects and Sequences
98
+
99
+ An interaction can have both `effects` and `sequences`:
100
+
101
+ ```typescript
102
+ {
103
+ key: 'hero',
104
+ trigger: 'viewEnter',
105
+ effects: [{ effectId: 'background-fade' }],
106
+ sequences: [{
107
+ offset: 200,
108
+ effects: [
109
+ { key: 'hero-title', effectId: 'slide-down' },
110
+ { key: 'hero-subtitle', effectId: 'fade-in' },
111
+ ],
112
+ }],
113
+ }
114
+ ```
115
+
116
+ ## Types Reference
117
+
118
+ ### `SequenceOptionsConfig`
119
+
120
+ Shared options for sequence timing and identity:
121
+
122
+ ```typescript
123
+ type SequenceOptionsConfig = {
124
+ delay?: number; // Base delay (ms). Default: 0
125
+ offset?: number; // Stagger interval (ms). Default: 0
126
+ offsetEasing?: string | ((p: number) => number); // Easing for offset distribution
127
+ sequenceId?: string; // ID for reusable sequence reference
128
+ conditions?: string[]; // Media query condition IDs
129
+ };
130
+ ```
131
+
132
+ ### `SequenceConfig`
133
+
134
+ Inline sequence definition (extends `SequenceOptionsConfig`):
135
+
136
+ ```typescript
137
+ type SequenceConfig = SequenceOptionsConfig & {
138
+ effects: (Effect | EffectRef)[];
139
+ };
140
+ ```
141
+
142
+ ### `SequenceConfigRef`
143
+
144
+ Reference to a reusable sequence with optional overrides:
145
+
146
+ ```typescript
147
+ type SequenceConfigRef = {
148
+ sequenceId: string;
149
+ delay?: number;
150
+ offset?: number;
151
+ offsetEasing?: string | ((p: number) => number);
152
+ conditions?: string[];
153
+ };
154
+ ```
155
+
156
+ Overrides in the ref merge on top of the referenced sequence's values.
157
+
158
+ ## Cross-Element Sequences
159
+
160
+ Effects within a sequence can target different elements using the `key` property:
161
+
162
+ ```typescript
163
+ {
164
+ key: 'trigger-element',
165
+ trigger: 'click',
166
+ params: { type: 'alternate' },
167
+ sequences: [{
168
+ offset: 150,
169
+ offsetEasing: 'sineOut',
170
+ effects: [
171
+ { key: 'heading', effectId: 'slide-down' },
172
+ { key: 'body-text', effectId: 'fade-in' },
173
+ { key: 'hero-image', effectId: 'scale-in' },
174
+ ],
175
+ }],
176
+ }
177
+ ```
178
+
179
+ Cross-element sequences are resolved at add-time. When a sequence effect targets a `key` different from the source interaction, Interact waits for both elements to be registered before creating the Sequence. The effects are processed via `addEffectsForTarget()` when the target controller connects.
180
+
181
+ ## Sequences with `listContainer`
182
+
183
+ The most common use case: staggering list item animations.
184
+
185
+ ### Initial Load
186
+
187
+ When the source element connects, all existing list items are gathered and a Sequence is created with one `AnimationGroup` per item:
188
+
189
+ ```typescript
190
+ {
191
+ key: 'product-grid',
192
+ trigger: 'viewEnter',
193
+ listContainer: '.products',
194
+ params: { type: 'once' },
195
+ sequences: [{
196
+ offset: 80,
197
+ offsetEasing: 'quadIn',
198
+ effects: [{
199
+ key: 'product-grid',
200
+ listContainer: '.products',
201
+ effectId: 'card-entrance',
202
+ }],
203
+ }],
204
+ }
205
+ ```
206
+
207
+ ### Dynamic Additions (`addListItems`)
208
+
209
+ When new items are appended to the DOM (detected by MutationObserver), `addListItems` is called. For sequences, this calls `Interact.addToSequence()` with `IndexedGroup` entries at the correct indices, triggering automatic offset recalculation across the entire Sequence.
210
+
211
+ Each `addListItems` invocation uses a unique cache key to manage its Sequence independently.
212
+
213
+ ### Dynamic Removals (`removeListItems`)
214
+
215
+ When items are removed from the DOM, `removeListItems` automatically calls `Interact.removeFromSequences(elements)`. This:
216
+
217
+ 1. Looks up associated Sequences via the `elementSequenceMap` WeakMap (O(1) per element)
218
+ 2. Calls `sequence.removeGroups()` to cancel and remove the matching groups
219
+ 3. Recalculates offsets for remaining groups
220
+ 4. Cleans up the element from the map
221
+
222
+ No manual cleanup is needed — MutationObserver handles it automatically.
223
+
224
+ ## Conditions on Sequences
225
+
226
+ ### Sequence-Level Conditions
227
+
228
+ Gate an entire sequence with media query conditions:
229
+
230
+ ```typescript
231
+ {
232
+ key: 'hero',
233
+ trigger: 'viewEnter',
234
+ sequences: [{
235
+ conditions: ['desktop-only'],
236
+ offset: 200,
237
+ effects: [
238
+ { key: 'hero-title', effectId: 'slide-down' },
239
+ { key: 'hero-body', effectId: 'fade-in' },
240
+ ],
241
+ }],
242
+ }
243
+ ```
244
+
245
+ When the condition doesn't match, the entire sequence is skipped. Interact sets up `matchMedia` listeners so the sequence is added/removed dynamically when the condition changes.
246
+
247
+ ### Effect-Level Conditions
248
+
249
+ Individual effects within a sequence can have their own conditions:
250
+
251
+ ```typescript
252
+ sequences: [
253
+ {
254
+ offset: 150,
255
+ effects: [
256
+ { effectId: 'base-entrance' },
257
+ { effectId: 'fancy-entrance', conditions: ['desktop-only'] },
258
+ ],
259
+ },
260
+ ];
261
+ ```
262
+
263
+ When an effect-level condition doesn't match, that effect is excluded from the Sequence's animation groups, and offsets are calculated for the remaining effects only.
264
+
265
+ ## Sequence Caching
266
+
267
+ Interact caches Sequences to avoid recreating them:
268
+
269
+ - **`Interact.sequenceCache`** (`Map<string, Sequence>`) — maps cache keys to Sequence instances
270
+ - **`Interact.elementSequenceMap`** (`WeakMap<HTMLElement, Set<Sequence>>`) — maps elements to their Sequences for efficient removal
271
+
272
+ Cache keys are derived from the interaction key, sequence index, and context. Cleanup happens automatically:
273
+
274
+ - `Interact.destroy()` clears both `sequenceCache` and `elementSequenceMap`
275
+ - `clearInteractionStateForKey()` removes cache entries by key prefix
276
+ - Element removal cleans up `elementSequenceMap` entries via the WeakMap
277
+
278
+ ## Examples
279
+
280
+ ### Staggered Card Grid Entrance
281
+
282
+ ```typescript
283
+ const config: InteractConfig = {
284
+ interactions: [
285
+ {
286
+ key: 'cards',
287
+ trigger: 'viewEnter',
288
+ listContainer: '.card-grid',
289
+ params: { type: 'once', threshold: 0.1 },
290
+ sequences: [
291
+ {
292
+ offset: 80,
293
+ offsetEasing: 'quadIn',
294
+ effects: [
295
+ {
296
+ key: 'cards',
297
+ listContainer: '.card-grid',
298
+ effectId: 'card-entrance',
299
+ },
300
+ ],
301
+ },
302
+ ],
303
+ },
304
+ ],
305
+ effects: {
306
+ 'card-entrance': {
307
+ duration: 600,
308
+ easing: 'cubic-bezier(0.16, 1, 0.3, 1)',
309
+ keyframeEffect: {
310
+ name: 'card-entrance',
311
+ keyframes: [
312
+ { opacity: 0, transform: 'translateY(40px) scale(0.95)' },
313
+ { opacity: 1, transform: 'translateY(0) scale(1)' },
314
+ ],
315
+ },
316
+ },
317
+ },
318
+ };
319
+ ```
320
+
321
+ ### Click-Triggered Multi-Element Orchestration
322
+
323
+ ```typescript
324
+ const config: InteractConfig = {
325
+ interactions: [
326
+ {
327
+ key: 'reveal-btn',
328
+ trigger: 'click',
329
+ params: { type: 'alternate' },
330
+ sequences: [
331
+ {
332
+ offset: 150,
333
+ offsetEasing: 'sineOut',
334
+ effects: [
335
+ { key: 'section-heading', effectId: 'slide-down' },
336
+ { key: 'section-body', effectId: 'fade-in' },
337
+ { key: 'section-image', effectId: 'scale-in' },
338
+ ],
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ effects: {
344
+ 'slide-down': {
345
+ duration: 400,
346
+ keyframeEffect: {
347
+ name: 'slide-down',
348
+ keyframes: [
349
+ { transform: 'translateY(-20px)', opacity: 0 },
350
+ { transform: 'translateY(0)', opacity: 1 },
351
+ ],
352
+ },
353
+ },
354
+ 'fade-in': {
355
+ duration: 500,
356
+ keyframeEffect: {
357
+ name: 'fade-in',
358
+ keyframes: [{ opacity: 0 }, { opacity: 1 }],
359
+ },
360
+ },
361
+ 'scale-in': {
362
+ duration: 600,
363
+ keyframeEffect: {
364
+ name: 'scale-in',
365
+ keyframes: [
366
+ { transform: 'scale(0.9)', opacity: 0 },
367
+ { transform: 'scale(1)', opacity: 1 },
368
+ ],
369
+ },
370
+ },
371
+ },
372
+ };
373
+ ```
374
+
375
+ ### Sequence with Media-Query Conditions
376
+
377
+ ```typescript
378
+ const config: InteractConfig = {
379
+ conditions: {
380
+ 'desktop-only': { type: 'media', predicate: '(min-width: 1024px)' },
381
+ 'no-reduced-motion': { type: 'media', predicate: '(prefers-reduced-motion: no-preference)' },
382
+ },
383
+ interactions: [
384
+ {
385
+ key: 'features',
386
+ trigger: 'viewEnter',
387
+ listContainer: '.feature-list',
388
+ conditions: ['no-reduced-motion'],
389
+ sequences: [
390
+ {
391
+ conditions: ['desktop-only'],
392
+ offset: 120,
393
+ offsetEasing: 'quadIn',
394
+ effects: [{ effectId: 'feature-entrance' }],
395
+ },
396
+ ],
397
+ },
398
+ ],
399
+ effects: {
400
+ 'feature-entrance': {
401
+ duration: 700,
402
+ easing: 'ease-out',
403
+ keyframeEffect: {
404
+ name: 'feature-entrance',
405
+ keyframes: [
406
+ { opacity: 0, transform: 'translateY(30px)' },
407
+ { opacity: 1, transform: 'translateY(0)' },
408
+ ],
409
+ },
410
+ },
411
+ },
412
+ };
413
+ ```
414
+
415
+ ## See Also
416
+
417
+ - [Lists and Dynamic Content](./lists-and-dynamic-content.md) — `listContainer`, `addListItems`, mutation tracking
418
+ - [Conditions and Media Queries](./conditions-and-media-queries.md) — conditional interactions
419
+ - [Effects and Animations](./effects-and-animations.md) — effect types and properties
420
+ - [Interact Class API](../api/interact-class.md) — `getSequence()`, `addToSequence()`, `removeFromSequences()`
421
+ - [Type Definitions](../api/types.md) — `SequenceConfig`, `SequenceConfigRef`, `SequenceOptionsConfig`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/interact",
3
- "version": "2.0.3",
3
+ "version": "2.1.0",
4
4
  "description": "A powerful, declarative interaction library for creating engaging web apps.",
5
5
  "license": "MIT",
6
6
  "main": "dist/cjs/index.js",
@@ -55,7 +55,7 @@
55
55
  "url": "https://github.com/wix/interact/issues"
56
56
  },
57
57
  "dependencies": {
58
- "@wix/motion": "^2.0.0",
58
+ "@wix/motion": "^2.1.0",
59
59
  "fastdom": "^1.0.12",
60
60
  "fizban": "^0.7.2",
61
61
  "kuliso": "^0.4.13"