@jasonshimmy/custom-elements-runtime 1.1.0 → 1.1.2

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/dist/index.d.ts CHANGED
@@ -16,3 +16,4 @@ export * from "./directive-enhancements";
16
16
  export * from "./event-bus";
17
17
  export * from "./store";
18
18
  export * from "./router";
19
+ export * from "./transitions";
@@ -20,18 +20,19 @@ export declare function createElementClass<S extends object, C extends object, P
20
20
  * return html`<h1>Hello World</h1>`;
21
21
  * });
22
22
  *
23
- * // With props only
24
- * component('with-props', ({ message = 'Hello' }) => {
23
+ * // With props using useProps() hook
24
+ * component('with-props', () => {
25
+ * const { message } = useProps({ message: 'Hello' });
25
26
  * return html`<div>${message}</div>`;
26
27
  * });
27
28
  *
28
- * // With props and hooks
29
- * component('my-switch', ({
30
- * modelValue = false,
31
- * label = ''
32
- * }, { emit, onConnected, onDisconnected }) => {
33
- * onConnected(() => console.log('Switch connected!'));
34
- * onDisconnected(() => console.log('Switch disconnected!'));
29
+ * // With props and lifecycle hooks
30
+ * component('my-switch', () => {
31
+ * const { modelValue, label } = useProps({ modelValue: false, label: '' });
32
+ * const emit = useEmit();
33
+ *
34
+ * useOnConnected(() => console.log('Switch connected!'));
35
+ * useOnDisconnected(() => console.log('Switch disconnected!'));
35
36
  *
36
37
  * return html`
37
38
  * <label>
@@ -47,4 +48,3 @@ export declare function createElementClass<S extends object, C extends object, P
47
48
  * ```
48
49
  */
49
50
  export declare function component(tag: string, renderFn: () => VNode | VNode[] | Promise<VNode | VNode[]>): void;
50
- export declare function component<TProps extends Record<string, any> = {}>(tag: string, renderFn: (props: TProps) => VNode | VNode[] | Promise<VNode | VNode[]>): void;
@@ -31,28 +31,14 @@ declare class EventManager {
31
31
  * Get the number of tracked event listeners for an element
32
32
  */
33
33
  static getListenerCount(element: HTMLElement): number;
34
- }
35
- /**
36
- * Enhanced event listener tracker that stores more metadata
37
- * for better debugging and cleanup
38
- */
39
- interface EventListenerMetadata {
40
- event: string;
41
- handler: EventListener;
42
- options?: AddEventListenerOptions;
43
- cleanup: () => void;
44
- addedAt: number;
45
- }
46
- declare class DetailedEventManager {
47
- private static listeners;
48
- static addListener(element: HTMLElement, event: string, handler: EventListener, options?: AddEventListenerOptions): void;
49
- static removeListener(element: HTMLElement, event: string, handler: EventListener, options?: EventListenerOptions): boolean;
50
- static cleanup(element: HTMLElement): void;
51
- static getListenerInfo(element: HTMLElement): EventListenerMetadata[];
52
- static findStaleListeners(_maxAge?: number): Array<{
53
- element: HTMLElement;
54
- listeners: EventListenerMetadata[];
34
+ /**
35
+ * Return listener metadata stored for the element (test/debug only)
36
+ */
37
+ static getListenerInfo(element: HTMLElement): Array<{
38
+ event: string;
39
+ handler?: Function;
40
+ wrapper?: Function;
41
+ options?: any;
55
42
  }>;
56
43
  }
57
- export { EventManager, DetailedEventManager };
58
- export type { EventListenerMetadata };
44
+ export { EventManager };
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Safe execution helper - silently ignores errors
3
+ */
4
+ export declare const safe: (fn: () => void) => void;
1
5
  /**
2
6
  * Convert camelCase to kebab-case with caching
3
7
  */
@@ -18,7 +18,8 @@ export declare function clearCurrentComponentContext(): void;
18
18
  *
19
19
  * @example
20
20
  * ```ts
21
- * component('my-button', ({ label = 'Click me' }) => {
21
+ * component('my-button', () => {
22
+ * const { label } = useProps({ label: 'Click me' });
22
23
  * const emit = useEmit();
23
24
  *
24
25
  * return html`
@@ -121,7 +122,9 @@ export declare function useProps<T extends Record<string, any>>(defaults: T): T;
121
122
  * ```ts
122
123
  * import { css } from '@lib/style';
123
124
  *
124
- * component('my-component', ({ theme = 'light' }) => {
125
+ * component('my-component', () => {
126
+ * const { theme } = useProps({ theme: 'light' });
127
+ *
125
128
  * useStyle(() => css`
126
129
  * :host {
127
130
  * background: ${theme === 'light' ? 'white' : 'black'};
@@ -3,12 +3,9 @@
3
3
  */
4
4
  declare class ReactiveSystem {
5
5
  private currentComponent;
6
- private componentDependencies;
7
- private componentRenderFunctions;
6
+ private componentData;
8
7
  private stateStorage;
9
- private stateIndexCounter;
10
8
  private trackingDisabled;
11
- private lastWarningTime;
12
9
  /**
13
10
  * Set the current component being rendered for dependency tracking
14
11
  */
@@ -1,7 +1,3 @@
1
- /**
2
- * Update Scheduler for batching DOM updates
3
- * Prevents excessive re-renders and improves performance
4
- */
5
1
  declare class UpdateScheduler {
6
2
  private pendingUpdates;
7
3
  private isFlushScheduled;
@@ -25,6 +25,7 @@ export declare const containerVariants: MediaVariantMap;
25
25
  export declare const responsiveOrder: string[];
26
26
  export declare const containerOrder: string[];
27
27
  export declare function parseSpacing(className: string): string | null;
28
+ export declare function parseSpaceUtility(className: string): string | null;
28
29
  export declare function hexToRgb(hex: string): string;
29
30
  export declare function parseColorClass(className: string): string | null;
30
31
  export declare function parseOpacityModifier(className: string): {
@@ -33,7 +34,7 @@ export declare function parseOpacityModifier(className: string): {
33
34
  };
34
35
  export declare function parseColorWithOpacity(className: string): string | null;
35
36
  /**
36
- * Parse gradient color stop utilities like from-red-500, to-blue-600, via-green-400
37
+ * Parse gradient color stop utilities like from-error-500, to-primary-600, via-success-400
37
38
  */
38
39
  export declare function parseGradientColorStop(className: string): string | null;
39
40
  export declare function parseOpacity(className: string): string | null;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Perform enter transition on an element
3
+ */
4
+ export declare function performEnterTransition(el: HTMLElement, transitionMeta: any): Promise<void>;
5
+ /**
6
+ * Perform leave transition on an element
7
+ */
8
+ export declare function performLeaveTransition(el: HTMLElement, transitionMeta: any): Promise<void>;
9
+ /**
10
+ * Cancel ongoing transition
11
+ */
12
+ export declare function cancelTransition(el: HTMLElement, isEnter: boolean, transitionMeta: any): void;
@@ -17,6 +17,8 @@ export interface VNode {
17
17
  reactiveRef?: any;
18
18
  /** Compiler-provided hint: whether this VNode represents a custom element (contains a dash) */
19
19
  isCustomElement?: boolean;
20
+ /** Transition group metadata */
21
+ _transitionGroup?: any;
20
22
  };
21
23
  children?: VNode[] | string;
22
24
  }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Helper functions for model binding updates in vdom.ts
3
+ * Extracted to reduce code duplication and improve maintainability
4
+ */
5
+ /**
6
+ * Check if two values have changed, handling arrays specially
7
+ */
8
+ export declare function hasValueChanged(newValue: any, currentValue: any): boolean;
9
+ /**
10
+ * Update state value (reactive or path-based)
11
+ */
12
+ export declare function updateStateValue(isReactive: boolean, value: any, newValue: any, context: any, arg?: string): void;
13
+ /**
14
+ * Trigger render and watchers after state update
15
+ */
16
+ export declare function triggerStateUpdate(context: any, isReactive: boolean, value: any, newValue: any): void;
17
+ /**
18
+ * Emit custom update events (both kebab-case and camelCase)
19
+ */
20
+ export declare function emitUpdateEvents(target: HTMLElement, propName: string, newValue: any): void;
21
+ /**
22
+ * Update element properties and attributes to sync with state
23
+ */
24
+ export declare function syncElementWithState(target: any, propName: string, propValue: any, isReactive: boolean): void;
25
+ /**
26
+ * Get current state value (reactive or path-based)
27
+ */
28
+ export declare function getCurrentStateValue(isReactive: boolean, value: any, context: any, arg?: string): any;
@@ -31,7 +31,7 @@ export declare function processModelDirective(value: string | any, modifiers: st
31
31
  * @param context
32
32
  * @returns
33
33
  */
34
- export declare function processBindDirective(value: any, props: Record<string, any>, attrs: Record<string, any>, context?: any): void;
34
+ export declare function processBindDirective(value: any, props: Record<string, any>, attrs: Record<string, any>, context?: any, el?: HTMLElement): void;
35
35
  /**
36
36
  * Process :show directive for conditional display
37
37
  * @param value
@@ -0,0 +1,261 @@
1
+ /**
2
+ * Transitions Module
3
+ * Vue-like transition system integrated with JIT CSS
4
+ * Provides Transition and TransitionGroup components for enter/leave animations
5
+ */
6
+ import type { VNode } from './runtime/types';
7
+ /**
8
+ * Transition class names for different animation phases
9
+ * All values should be JIT CSS utility classes
10
+ */
11
+ export interface TransitionClasses {
12
+ /** Classes applied at the start of enter transition */
13
+ enterFrom?: string;
14
+ /** Classes applied during entire enter transition */
15
+ enterActive?: string;
16
+ /** Classes applied at the end of enter transition */
17
+ enterTo?: string;
18
+ /** Classes applied at the start of leave transition */
19
+ leaveFrom?: string;
20
+ /** Classes applied during entire leave transition */
21
+ leaveActive?: string;
22
+ /** Classes applied at the end of leave transition */
23
+ leaveTo?: string;
24
+ }
25
+ /**
26
+ * Lifecycle hooks for transition events
27
+ */
28
+ export interface TransitionHooks {
29
+ /** Called before enter transition starts */
30
+ onBeforeEnter?: (el: HTMLElement) => void;
31
+ /** Called during enter transition (with done callback for manual control) */
32
+ onEnter?: (el: HTMLElement, done: () => void) => void;
33
+ /** Called after enter transition completes */
34
+ onAfterEnter?: (el: HTMLElement) => void;
35
+ /** Called if enter transition is cancelled */
36
+ onEnterCancelled?: (el: HTMLElement) => void;
37
+ /** Called before leave transition starts */
38
+ onBeforeLeave?: (el: HTMLElement) => void;
39
+ /** Called during leave transition (with done callback for manual control) */
40
+ onLeave?: (el: HTMLElement, done: () => void) => void;
41
+ /** Called after leave transition completes */
42
+ onAfterLeave?: (el: HTMLElement) => void;
43
+ /** Called if leave transition is cancelled */
44
+ onLeaveCancelled?: (el: HTMLElement) => void;
45
+ }
46
+ /**
47
+ * Options for Transition component
48
+ */
49
+ export interface TransitionOptions extends TransitionClasses, TransitionHooks {
50
+ /** Preset name (fade, slide-right, scale, etc.) */
51
+ preset?: keyof typeof transitionPresets;
52
+ /** Whether to show the content */
53
+ show: boolean;
54
+ /** Transition mode: default, out-in (leave before enter), in-out (enter before leave) */
55
+ mode?: 'default' | 'out-in' | 'in-out';
56
+ /** Custom duration override (ms) */
57
+ duration?: number | {
58
+ enter: number;
59
+ leave: number;
60
+ };
61
+ /** Whether to apply transition on initial render */
62
+ appear?: boolean;
63
+ /** Whether to use CSS transitions (true) or JS-only hooks (false) */
64
+ css?: boolean;
65
+ /** Optional name for debugging */
66
+ name?: string;
67
+ }
68
+ /**
69
+ * Options for TransitionGroup component
70
+ */
71
+ export interface TransitionGroupOptions extends Omit<TransitionOptions, 'show'> {
72
+ /** HTML tag for the wrapper element */
73
+ tag?: string;
74
+ /** Class applied during move transitions (when items reorder) */
75
+ moveClass?: string;
76
+ /** Whether to show the group (defaults to true for TransitionGroup) */
77
+ show?: boolean;
78
+ /** CSS classes to apply to the wrapper element (e.g., 'flex gap-4' or 'grid grid-cols-3') */
79
+ class?: string;
80
+ /** Inline styles to apply to the wrapper element */
81
+ style?: string | Record<string, string>;
82
+ }
83
+ /**
84
+ * Pre-defined transition presets using JIT CSS classes
85
+ * Users can reference these by name or define custom classes
86
+ */
87
+ export declare const transitionPresets: {
88
+ /** Simple fade in/out */
89
+ readonly fade: {
90
+ readonly enterFrom: "opacity-0";
91
+ readonly enterActive: "transition-opacity duration-300 ease-out";
92
+ readonly enterTo: "opacity-100";
93
+ readonly leaveFrom: "opacity-100";
94
+ readonly leaveActive: "transition-opacity duration-200 ease-in";
95
+ readonly leaveTo: "opacity-0";
96
+ };
97
+ /** Slide in from right */
98
+ readonly 'slide-right': {
99
+ readonly enterFrom: "translate-x-[100%] opacity-0";
100
+ readonly enterActive: "transition-all duration-300 ease-out";
101
+ readonly enterTo: "translate-x-[0%] opacity-100";
102
+ readonly leaveFrom: "translate-x-[0%] opacity-100";
103
+ readonly leaveActive: "transition-all duration-200 ease-in";
104
+ readonly leaveTo: "translate-x-[100%] opacity-0";
105
+ };
106
+ /** Slide in from left */
107
+ readonly 'slide-left': {
108
+ readonly enterFrom: "translate-x-[-100%] opacity-0";
109
+ readonly enterActive: "transition-all duration-300 ease-out";
110
+ readonly enterTo: "translate-x-[0%] opacity-100";
111
+ readonly leaveFrom: "translate-x-[0%] opacity-100";
112
+ readonly leaveActive: "transition-all duration-200 ease-in";
113
+ readonly leaveTo: "translate-x-[-100%] opacity-0";
114
+ };
115
+ /** Slide up from bottom */
116
+ readonly 'slide-up': {
117
+ readonly enterFrom: "translate-y-[100%] opacity-0";
118
+ readonly enterActive: "transition-all duration-300 ease-out";
119
+ readonly enterTo: "translate-y-[0%] opacity-100";
120
+ readonly leaveFrom: "translate-y-[0%] opacity-100";
121
+ readonly leaveActive: "transition-all duration-200 ease-in";
122
+ readonly leaveTo: "translate-y-[100%] opacity-0";
123
+ };
124
+ /** Slide down from top */
125
+ readonly 'slide-down': {
126
+ readonly enterFrom: "translate-y-[-100%] opacity-0";
127
+ readonly enterActive: "transition-all duration-300 ease-out";
128
+ readonly enterTo: "translate-y-[0%] opacity-100";
129
+ readonly leaveFrom: "translate-y-[0%] opacity-100";
130
+ readonly leaveActive: "transition-all duration-200 ease-in";
131
+ readonly leaveTo: "translate-y-[-100%] opacity-0";
132
+ };
133
+ /** Scale up from center */
134
+ readonly scale: {
135
+ readonly enterFrom: "scale-95 opacity-0";
136
+ readonly enterActive: "transition-all duration-200 ease-out";
137
+ readonly enterTo: "scale-100 opacity-100";
138
+ readonly leaveFrom: "scale-100 opacity-100";
139
+ readonly leaveActive: "transition-all duration-150 ease-in";
140
+ readonly leaveTo: "scale-95 opacity-0";
141
+ };
142
+ /** Scale down to center */
143
+ readonly 'scale-down': {
144
+ readonly enterFrom: "scale-105 opacity-0";
145
+ readonly enterActive: "transition-all duration-200 ease-out";
146
+ readonly enterTo: "scale-100 opacity-100";
147
+ readonly leaveFrom: "scale-100 opacity-100";
148
+ readonly leaveActive: "transition-all duration-150 ease-in";
149
+ readonly leaveTo: "scale-105 opacity-0";
150
+ };
151
+ /** Bounce effect */
152
+ readonly bounce: {
153
+ readonly enterFrom: "scale-0 opacity-0";
154
+ readonly enterActive: "transition-all duration-500 ease-out";
155
+ readonly enterTo: "scale-100 opacity-100";
156
+ readonly leaveFrom: "scale-100 opacity-100";
157
+ readonly leaveActive: "transition-all duration-200 ease-in";
158
+ readonly leaveTo: "scale-0 opacity-0";
159
+ };
160
+ /** Zoom and fade */
161
+ readonly zoom: {
162
+ readonly enterFrom: "scale-0 opacity-0";
163
+ readonly enterActive: "transition-all duration-300 ease-out";
164
+ readonly enterTo: "scale-100 opacity-100";
165
+ readonly leaveFrom: "scale-100 opacity-100";
166
+ readonly leaveActive: "transition-all duration-200 ease-in";
167
+ readonly leaveTo: "scale-0 opacity-0";
168
+ };
169
+ /** Flip in */
170
+ readonly flip: {
171
+ readonly enterFrom: "rotate-[90deg] opacity-0";
172
+ readonly enterActive: "transition-all duration-400 ease-out";
173
+ readonly enterTo: "rotate-[0deg] opacity-100";
174
+ readonly leaveFrom: "rotate-[0deg] opacity-100";
175
+ readonly leaveActive: "transition-all duration-300 ease-in";
176
+ readonly leaveTo: "rotate-[90deg] opacity-0";
177
+ };
178
+ };
179
+ /**
180
+ * Transition component - wraps content with enter/leave animations
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * // Using a preset
185
+ * ${Transition({ preset: 'fade', show: isVisible.value }, html`<div>Content</div>`)}
186
+ *
187
+ * // Using custom JIT classes
188
+ * ${Transition({
189
+ * show: isVisible.value,
190
+ * enterFrom: 'opacity-0 scale-95',
191
+ * enterActive: 'transition-all duration-300',
192
+ * enterTo: 'opacity-100 scale-100',
193
+ * leaveFrom: 'opacity-100 scale-100',
194
+ * leaveActive: 'transition-all duration-200',
195
+ * leaveTo: 'opacity-0 scale-95'
196
+ * }, html`<div>Content</div>`)}
197
+ *
198
+ * // With lifecycle hooks
199
+ * ${Transition({
200
+ * preset: 'slide-right',
201
+ * show: isVisible.value,
202
+ * onAfterEnter: (el) => console.log('Entered!'),
203
+ * onAfterLeave: (el) => console.log('Left!')
204
+ * }, html`<div>Content</div>`)}
205
+ * ```
206
+ */
207
+ export declare function Transition(options: TransitionOptions, content: VNode | VNode[]): VNode;
208
+ /**
209
+ * TransitionGroup component - animates lists with enter/leave/move transitions
210
+ *
211
+ * @example
212
+ * ```ts
213
+ * // Basic usage
214
+ * ${TransitionGroup({
215
+ * preset: 'slide-right',
216
+ * tag: 'ul',
217
+ * moveClass: 'transition-transform duration-300'
218
+ * }, each(items.value, (item) => html`
219
+ * <li key="${item.id}">${item.text}</li>
220
+ * `))}
221
+ *
222
+ * // With flex layout
223
+ * ${TransitionGroup({
224
+ * preset: 'fade',
225
+ * class: 'flex gap-4 flex-wrap'
226
+ * }, each(items.value, (item) => html`
227
+ * <div key="${item.id}" class="flex-shrink-0">${item.text}</div>
228
+ * `))}
229
+ *
230
+ * // With grid layout
231
+ * ${TransitionGroup({
232
+ * preset: 'scale',
233
+ * class: 'grid grid-cols-3 gap-4'
234
+ * }, each(items.value, (item) => html`
235
+ * <div key="${item.id}">${item.text}</div>
236
+ * `))}
237
+ * ```
238
+ */
239
+ export declare function TransitionGroup(options: TransitionGroupOptions, children: VNode[]): VNode;
240
+ /**
241
+ * Helper to create custom transition presets
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * const customFade = createTransitionPreset({
246
+ * enterFrom: 'opacity-0',
247
+ * enterActive: 'transition-opacity duration-500 ease-out',
248
+ * enterTo: 'opacity-100',
249
+ * leaveFrom: 'opacity-100',
250
+ * leaveActive: 'transition-opacity duration-300 ease-in',
251
+ * leaveTo: 'opacity-0'
252
+ * });
253
+ *
254
+ * ${Transition({ ...customFade, show: visible.value }, content)}
255
+ * ```
256
+ */
257
+ export declare function createTransitionPreset(classes: TransitionClasses): TransitionClasses;
258
+ /**
259
+ * Get the global transition stylesheet (creates it if needed)
260
+ */
261
+ export declare function getTransitionStyleSheet(): CSSStyleSheet;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jasonshimmy/custom-elements-runtime",
3
3
  "description": "A powerful, modern, and lightweight runtime for creating reactive web components with TypeScript",
4
- "version": "1.1.0",
4
+ "version": "1.1.2",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "web-components",