ripple 0.2.168 → 0.2.170

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.
@@ -98,4 +98,61 @@ export component Test() {
98
98
  expect(css).toContain('@keyframes foo');
99
99
  expect(css).toContain('@keyframes bar');
100
100
  });
101
+
102
+ it('handles animation property referencing keyframes (not marking as unused)', () => {
103
+ const source = `
104
+ export component Parent() {
105
+ <div class="parent">
106
+ <Child />
107
+ </div>
108
+
109
+ <style>
110
+ /* Scoped keyframe - only usable within Parent */
111
+ @keyframes slideIn {
112
+ from { transform: translateX(-100%); }
113
+ to { transform: translateX(0); }
114
+ }
115
+
116
+ /* Global keyframe - usable in any component */
117
+ @keyframes -global-fadeIn {
118
+ 0% { opacity: 0; }
119
+ 100% { opacity: 1; }
120
+ }
121
+
122
+ .parent {
123
+ animation: slideIn 1s;
124
+ }
125
+ </style>
126
+ }
127
+
128
+ component Child() {
129
+ <div class="child">{'Child content'}</div>
130
+
131
+ <style>
132
+ .child {
133
+ animation: fadeIn 3s; /* Uses global fadeIn from Parent */
134
+ }
135
+ </style>
136
+ }`;
137
+ const { css } = compile(source, 'test.ripple');
138
+
139
+ // Parent should have scoped slideIn and global fadeIn
140
+ expect(css).toMatch(/@keyframes ripple-[a-z0-9]+-slideIn/);
141
+ expect(css).toContain('@keyframes fadeIn');
142
+ expect(css).not.toContain('-global-fadeIn');
143
+
144
+ // Parent .parent should reference scoped slideIn
145
+ expect(css).toMatch(
146
+ /\.parent\.ripple-[a-z0-9]+\s*{[\s\S]*?animation:\s*ripple-[a-z0-9]+-slideIn 1s/,
147
+ );
148
+
149
+ // .parent should NOT be marked as unused
150
+ expect(css).not.toContain('(unused) .parent');
151
+
152
+ // Child .child should reference global fadeIn
153
+ expect(css).toMatch(/\.child\.ripple-[a-z0-9]+\s*{[\s\S]*?animation:\s*fadeIn 3s/);
154
+
155
+ // .child should NOT be marked as unused
156
+ expect(css).not.toContain('(unused) .child');
157
+ });
101
158
  });
@@ -18,7 +18,7 @@ describe('use value()', () => {
18
18
 
19
19
  const input = container.querySelector('input') as HTMLInputElement;
20
20
  input.value = 'Hello';
21
- input.dispatchEvent(new Event('input'));
21
+ input.dispatchEvent(new Event('input', { bubbles: true }));
22
22
  flushSync();
23
23
  expect(input.value).toBe('Hello');
24
24
  expect(logs).toEqual(['text changed', '', 'text changed', 'Hello']);
@@ -42,7 +42,7 @@ describe('use value()', () => {
42
42
  const input = container.querySelector('input') as HTMLInputElement;
43
43
  expect(input.value).toBe('foo');
44
44
  input.value = 'Hello';
45
- input.dispatchEvent(new Event('input'));
45
+ input.dispatchEvent(new Event('input', { bubbles: true }));
46
46
  flushSync();
47
47
  expect(input.value).toBe('Hello');
48
48
  expect(logs).toEqual(['text changed', 'foo', 'text changed', 'Hello']);
@@ -65,7 +65,7 @@ describe('use value()', () => {
65
65
 
66
66
  const input = container.querySelector('input') as HTMLInputElement;
67
67
  input.checked = true;
68
- input.dispatchEvent(new Event('change'));
68
+ input.dispatchEvent(new Event('change', { bubbles: true }));
69
69
  flushSync();
70
70
 
71
71
  expect(input.checked).toBe(true);
@@ -94,7 +94,7 @@ describe('use value()', () => {
94
94
 
95
95
  const select = container.querySelector('select') as HTMLSelectElement;
96
96
  select.value = '3';
97
- select.dispatchEvent(new Event('change'));
97
+ select.dispatchEvent(new Event('change', { bubbles: true }));
98
98
  flushSync();
99
99
 
100
100
  expect(select.value).toBe('3');
@@ -89,7 +89,7 @@ describe('MediaQuery', () => {
89
89
  <Child />
90
90
  }
91
91
 
92
- <button onClick={() => @show = !@show}>{'Toggle Child'}</button>
92
+ <button onClick={() => (@show = !@show)}>{'Toggle Child'}</button>
93
93
  }
94
94
 
95
95
  component Child() {
@@ -1,37 +1,39 @@
1
+ /** @import { AddEventObject } from '#public'*/
2
+
1
3
  import { describe, it, expect } from 'vitest';
2
4
  import {
3
- is_delegated,
5
+ is_non_delegated,
4
6
  is_event_attribute,
5
- is_capture_event,
6
7
  get_attribute_event_name,
7
- is_passive_event
8
+ is_passive_event,
9
+ is_capture_event,
8
10
  } from '../../src/utils/events.js';
9
11
 
10
12
  describe('events utility', () => {
11
- describe('is_delegated', () => {
12
- it('should return true for delegated events', () => {
13
- expect(is_delegated('click')).toBe(true);
14
- expect(is_delegated('input')).toBe(true);
15
- expect(is_delegated('change')).toBe(true);
16
- expect(is_delegated('mousedown')).toBe(true);
17
- expect(is_delegated('keydown')).toBe(true);
18
- expect(is_delegated('pointerdown')).toBe(true);
19
- expect(is_delegated('touchstart')).toBe(true);
20
- expect(is_delegated('focusin')).toBe(true);
21
- expect(is_delegated('focusout')).toBe(true);
22
- });
23
-
24
- it('should return false for non-delegated events', () => {
25
- expect(is_delegated('focus')).toBe(false);
26
- expect(is_delegated('blur')).toBe(false);
27
- expect(is_delegated('scroll')).toBe(false);
28
- expect(is_delegated('load')).toBe(false);
29
- expect(is_delegated('resize')).toBe(false);
30
- });
31
-
32
- it('should be case-sensitive', () => {
33
- expect(is_delegated('Click')).toBe(false);
34
- expect(is_delegated('CLICK')).toBe(false);
13
+ describe('is event delegated', () => {
14
+ it('should confirm delegated events', () => {
15
+ expect(is_non_delegated('click')).toBe(false);
16
+ expect(is_non_delegated('input')).toBe(false);
17
+ expect(is_non_delegated('change')).toBe(false);
18
+ expect(is_non_delegated('mousedown')).toBe(false);
19
+ expect(is_non_delegated('keydown')).toBe(false);
20
+ expect(is_non_delegated('pointerdown')).toBe(false);
21
+ expect(is_non_delegated('touchstart')).toBe(false);
22
+ expect(is_non_delegated('focusin')).toBe(false);
23
+ expect(is_non_delegated('focusout')).toBe(false);
24
+ });
25
+
26
+ it('should confirm non-delegated events', () => {
27
+ expect(is_non_delegated('focus')).toBe(true);
28
+ expect(is_non_delegated('blur')).toBe(true);
29
+ expect(is_non_delegated('scroll')).toBe(true);
30
+ expect(is_non_delegated('load')).toBe(true);
31
+ expect(is_non_delegated('resize')).toBe(true);
32
+ });
33
+
34
+ it('should confirm that events with any capital letters are delegated', () => {
35
+ expect(is_non_delegated('Click')).toBe(false);
36
+ expect(is_non_delegated('CLICK')).toBe(false);
35
37
  });
36
38
  });
37
39
 
@@ -74,6 +76,32 @@ describe('events utility', () => {
74
76
  });
75
77
  });
76
78
 
79
+ describe('get_attribute_event_name', () => {
80
+ it('should convert event attribute names to lowercase and strip "on" prefix', () => {
81
+ const fn = () => {};
82
+ expect(get_attribute_event_name('onClick', fn)).toBe('click');
83
+ expect(get_attribute_event_name('onInput', fn)).toBe('input');
84
+ expect(get_attribute_event_name('onMouseDown', fn)).toBe('mousedown');
85
+ expect(get_attribute_event_name('onKeyPress', fn)).toBe('keypress');
86
+ expect(get_attribute_event_name('onChange', fn)).toBe('change');
87
+ expect(get_attribute_event_name('onFocus', fn)).toBe('focus');
88
+ });
89
+
90
+ it('should keep event attribute names letter case and strip "on" prefix', () => {
91
+ /** @type AddEventObject */
92
+ const customHandler = { handleEvent: () => {} };
93
+ expect(get_attribute_event_name('onClick', { ...customHandler, customName: 'Click' })).toBe(
94
+ 'Click',
95
+ );
96
+ expect(get_attribute_event_name('onInput', { ...customHandler, customName: 'Input' })).toBe(
97
+ 'Input',
98
+ );
99
+ expect(
100
+ get_attribute_event_name('onMouseDown', { ...customHandler, customName: 'MouseDown' }),
101
+ ).toBe('MouseDown');
102
+ });
103
+ });
104
+
77
105
  describe('is_capture_event', () => {
78
106
  it('should return true for capture events', () => {
79
107
  expect(is_capture_event('clickCapture')).toBe(true);
@@ -104,25 +132,6 @@ describe('events utility', () => {
104
132
  expect(is_capture_event('keypressCapture')).toBe(true);
105
133
  expect(is_capture_event('clickcapture')).toBe(false);
106
134
  expect(is_capture_event('keypresscapture')).toBe(false);
107
- })
108
- });
109
-
110
- describe('get_attribute_event_name', () => {
111
- it('should convert event attribute names to lowercase and strip "on" prefix', () => {
112
- expect(get_attribute_event_name('onClick')).toBe('click');
113
- expect(get_attribute_event_name('onInput')).toBe('input');
114
- expect(get_attribute_event_name('onMouseDown')).toBe('mousedown');
115
- expect(get_attribute_event_name('onKeyPress')).toBe('keypress');
116
- expect(get_attribute_event_name('onChange')).toBe('change');
117
- expect(get_attribute_event_name('onFocus')).toBe('focus');
118
- });
119
-
120
- it('should handle capture events and strip both "on" and "Capture"', () => {
121
- expect(get_attribute_event_name('onClickCapture')).toBe('click');
122
- expect(get_attribute_event_name('onMouseDownCapture')).toBe('mousedown');
123
- expect(get_attribute_event_name('onMouseDownCapture')).toBe('mousedown');
124
- expect(get_attribute_event_name('onGotPointerCapture')).toBe('gotpointercapture');
125
- expect(get_attribute_event_name('onLostPointerCapture')).toBe('lostpointercapture');
126
135
  });
127
136
  });
128
137
 
@@ -130,6 +139,8 @@ describe('events utility', () => {
130
139
  it('should return true for passive events', () => {
131
140
  expect(is_passive_event('touchstart')).toBe(true);
132
141
  expect(is_passive_event('touchmove')).toBe(true);
142
+ expect(is_passive_event('wheel')).toBe(true);
143
+ expect(is_passive_event('mousewheel')).toBe(true);
133
144
  });
134
145
 
135
146
  it('should return false for non-passive events', () => {
package/types/index.d.ts CHANGED
@@ -137,39 +137,49 @@ export declare function trackSplit<V extends Props, const K extends readonly (ke
137
137
  splitKeys: K,
138
138
  ): SplitResult<V, K>;
139
139
 
140
+ export interface AddEventOptions extends ExtendedEventOptions {
141
+ customName?: string;
142
+ }
143
+
144
+ export interface AddEventObject extends AddEventOptions, EventListenerObject {}
145
+
146
+ export interface ExtendedEventOptions extends AddEventListenerOptions, EventListenerOptions {
147
+ delegated?: boolean;
148
+ }
149
+
140
150
  export function on<Type extends keyof WindowEventMap>(
141
151
  window: Window,
142
152
  type: Type,
143
153
  handler: (this: Window, event: WindowEventMap[Type]) => any,
144
- options?: AddEventListenerOptions | undefined,
154
+ options?: ExtendedEventOptions | undefined,
145
155
  ): () => void;
146
156
 
147
157
  export function on<Type extends keyof DocumentEventMap>(
148
158
  document: Document,
149
159
  type: Type,
150
160
  handler: (this: Document, event: DocumentEventMap[Type]) => any,
151
- options?: AddEventListenerOptions | undefined,
161
+ options?: ExtendedEventOptions | undefined,
152
162
  ): () => void;
153
163
 
154
164
  export function on<Element extends HTMLElement, Type extends keyof HTMLElementEventMap>(
155
165
  element: Element,
156
166
  type: Type,
157
167
  handler: (this: Element, event: HTMLElementEventMap[Type]) => any,
158
- options?: AddEventListenerOptions | undefined,
168
+ options?: ExtendedEventOptions | undefined,
159
169
  ): () => void;
160
170
 
161
171
  export function on<Element extends MediaQueryList, Type extends keyof MediaQueryListEventMap>(
162
172
  element: Element,
163
173
  type: Type,
164
174
  handler: (this: Element, event: MediaQueryListEventMap[Type]) => any,
165
- options?: AddEventListenerOptions | undefined,
175
+ options?: ExtendedEventOptions | undefined,
166
176
  ): () => void;
167
177
 
168
178
  export function on(
169
179
  element: EventTarget,
170
180
  type: string,
171
181
  handler: EventListener,
172
- options?: AddEventListenerOptions | undefined,
182
+ options?: ExtendedEventOptions | undefined,
173
183
  ): () => void;
174
184
 
175
185
  export type TrackedObjectShallow<T> = {