@jasonshimmy/custom-elements-runtime 1.0.10 → 1.1.1

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
@@ -5,7 +5,7 @@
5
5
  * No external dependencies. Mobile-first, secure, and developer friendly.
6
6
  */
7
7
  export { component } from "./runtime/component";
8
- export { useEmit, useOnConnected, useOnDisconnected, useOnAttributeChanged, useOnError, useStyle } from "./runtime/hooks";
8
+ export { useEmit, useOnConnected, useOnDisconnected, useOnAttributeChanged, useOnError, useStyle, useProps } from "./runtime/hooks";
9
9
  export { ref, computed, watch } from "./runtime/reactive";
10
10
  export { html } from "./runtime/template-compiler";
11
11
  export { css } from "./runtime/style";
@@ -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`
@@ -90,6 +91,29 @@ export declare function useOnAttributeChanged(callback: (name: string, oldValue:
90
91
  * ```
91
92
  */
92
93
  export declare function useOnError(callback: (error: Error) => void): void;
94
+ /**
95
+ * Register prop defaults for the component. Can be called during render.
96
+ * Stores the prop defaults on `context._hookCallbacks.props` so the runtime
97
+ * can pick them up when building the component config.
98
+ *
99
+ * Example:
100
+ * ```ts
101
+ * component('my-comp', () => {
102
+ * useProps({ modelValue: false, label: 'Hello' });
103
+ * return html`<div/>`;
104
+ * });
105
+ * ```
106
+ */
107
+ export declare function useProps<T extends Record<string, any>>(defaults: T): T;
108
+ /**
109
+ * Register prop defaults and return a stable props object for use inside render.
110
+ * The returned object reads values from the current component context at render
111
+ * time and falls back to the provided defaults. This keeps prop access stable
112
+ * in production builds and avoids reliance on parsing the render function.
113
+ *
114
+ * Must be called during render. Example:
115
+ * const props = useProps({ modelValue: false });
116
+ */
93
117
  /**
94
118
  * Register a style function that will be called during each render
95
119
  * to provide reactive styles for the component
@@ -98,7 +122,9 @@ export declare function useOnError(callback: (error: Error) => void): void;
98
122
  * ```ts
99
123
  * import { css } from '@lib/style';
100
124
  *
101
- * component('my-component', ({ theme = 'light' }) => {
125
+ * component('my-component', () => {
126
+ * const { theme } = useProps({ theme: 'light' });
127
+ *
102
128
  * useStyle(() => css`
103
129
  * :host {
104
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;
@@ -33,7 +33,7 @@ export declare function parseOpacityModifier(className: string): {
33
33
  };
34
34
  export declare function parseColorWithOpacity(className: string): string | null;
35
35
  /**
36
- * Parse gradient color stop utilities like from-red-500, to-blue-600, via-green-400
36
+ * Parse gradient color stop utilities like from-error-500, to-primary-600, via-success-400
37
37
  */
38
38
  export declare function parseGradientColorStop(className: string): string | null;
39
39
  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,240 @@
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
+ }
79
+ /**
80
+ * Pre-defined transition presets using JIT CSS classes
81
+ * Users can reference these by name or define custom classes
82
+ */
83
+ export declare const transitionPresets: {
84
+ /** Simple fade in/out */
85
+ readonly fade: {
86
+ readonly enterFrom: "opacity-0";
87
+ readonly enterActive: "transition-opacity duration-300 ease-out";
88
+ readonly enterTo: "opacity-100";
89
+ readonly leaveFrom: "opacity-100";
90
+ readonly leaveActive: "transition-opacity duration-200 ease-in";
91
+ readonly leaveTo: "opacity-0";
92
+ };
93
+ /** Slide in from right */
94
+ readonly 'slide-right': {
95
+ readonly enterFrom: "translate-x-[100%] opacity-0";
96
+ readonly enterActive: "transition-all duration-300 ease-out";
97
+ readonly enterTo: "translate-x-[0%] opacity-100";
98
+ readonly leaveFrom: "translate-x-[0%] opacity-100";
99
+ readonly leaveActive: "transition-all duration-200 ease-in";
100
+ readonly leaveTo: "translate-x-[100%] opacity-0";
101
+ };
102
+ /** Slide in from left */
103
+ readonly 'slide-left': {
104
+ readonly enterFrom: "translate-x-[-100%] opacity-0";
105
+ readonly enterActive: "transition-all duration-300 ease-out";
106
+ readonly enterTo: "translate-x-[0%] opacity-100";
107
+ readonly leaveFrom: "translate-x-[0%] opacity-100";
108
+ readonly leaveActive: "transition-all duration-200 ease-in";
109
+ readonly leaveTo: "translate-x-[-100%] opacity-0";
110
+ };
111
+ /** Slide up from bottom */
112
+ readonly 'slide-up': {
113
+ readonly enterFrom: "translate-y-[100%] opacity-0";
114
+ readonly enterActive: "transition-all duration-300 ease-out";
115
+ readonly enterTo: "translate-y-[0%] opacity-100";
116
+ readonly leaveFrom: "translate-y-[0%] opacity-100";
117
+ readonly leaveActive: "transition-all duration-200 ease-in";
118
+ readonly leaveTo: "translate-y-[100%] opacity-0";
119
+ };
120
+ /** Slide down from top */
121
+ readonly 'slide-down': {
122
+ readonly enterFrom: "translate-y-[-100%] opacity-0";
123
+ readonly enterActive: "transition-all duration-300 ease-out";
124
+ readonly enterTo: "translate-y-[0%] opacity-100";
125
+ readonly leaveFrom: "translate-y-[0%] opacity-100";
126
+ readonly leaveActive: "transition-all duration-200 ease-in";
127
+ readonly leaveTo: "translate-y-[-100%] opacity-0";
128
+ };
129
+ /** Scale up from center */
130
+ readonly scale: {
131
+ readonly enterFrom: "scale-95 opacity-0";
132
+ readonly enterActive: "transition-all duration-200 ease-out";
133
+ readonly enterTo: "scale-100 opacity-100";
134
+ readonly leaveFrom: "scale-100 opacity-100";
135
+ readonly leaveActive: "transition-all duration-150 ease-in";
136
+ readonly leaveTo: "scale-95 opacity-0";
137
+ };
138
+ /** Scale down to center */
139
+ readonly 'scale-down': {
140
+ readonly enterFrom: "scale-105 opacity-0";
141
+ readonly enterActive: "transition-all duration-200 ease-out";
142
+ readonly enterTo: "scale-100 opacity-100";
143
+ readonly leaveFrom: "scale-100 opacity-100";
144
+ readonly leaveActive: "transition-all duration-150 ease-in";
145
+ readonly leaveTo: "scale-105 opacity-0";
146
+ };
147
+ /** Bounce effect */
148
+ readonly bounce: {
149
+ readonly enterFrom: "scale-0 opacity-0";
150
+ readonly enterActive: "transition-all duration-500 ease-out";
151
+ readonly enterTo: "scale-100 opacity-100";
152
+ readonly leaveFrom: "scale-100 opacity-100";
153
+ readonly leaveActive: "transition-all duration-200 ease-in";
154
+ readonly leaveTo: "scale-0 opacity-0";
155
+ };
156
+ /** Zoom and fade */
157
+ readonly zoom: {
158
+ readonly enterFrom: "scale-0 opacity-0";
159
+ readonly enterActive: "transition-all duration-300 ease-out";
160
+ readonly enterTo: "scale-100 opacity-100";
161
+ readonly leaveFrom: "scale-100 opacity-100";
162
+ readonly leaveActive: "transition-all duration-200 ease-in";
163
+ readonly leaveTo: "scale-0 opacity-0";
164
+ };
165
+ /** Flip in */
166
+ readonly flip: {
167
+ readonly enterFrom: "rotate-[90deg] opacity-0";
168
+ readonly enterActive: "transition-all duration-400 ease-out";
169
+ readonly enterTo: "rotate-[0deg] opacity-100";
170
+ readonly leaveFrom: "rotate-[0deg] opacity-100";
171
+ readonly leaveActive: "transition-all duration-300 ease-in";
172
+ readonly leaveTo: "rotate-[90deg] opacity-0";
173
+ };
174
+ };
175
+ /**
176
+ * Transition component - wraps content with enter/leave animations
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * // Using a preset
181
+ * ${Transition({ preset: 'fade', show: isVisible.value }, html`<div>Content</div>`)}
182
+ *
183
+ * // Using custom JIT classes
184
+ * ${Transition({
185
+ * show: isVisible.value,
186
+ * enterFrom: 'opacity-0 scale-95',
187
+ * enterActive: 'transition-all duration-300',
188
+ * enterTo: 'opacity-100 scale-100',
189
+ * leaveFrom: 'opacity-100 scale-100',
190
+ * leaveActive: 'transition-all duration-200',
191
+ * leaveTo: 'opacity-0 scale-95'
192
+ * }, html`<div>Content</div>`)}
193
+ *
194
+ * // With lifecycle hooks
195
+ * ${Transition({
196
+ * preset: 'slide-right',
197
+ * show: isVisible.value,
198
+ * onAfterEnter: (el) => console.log('Entered!'),
199
+ * onAfterLeave: (el) => console.log('Left!')
200
+ * }, html`<div>Content</div>`)}
201
+ * ```
202
+ */
203
+ export declare function Transition(options: TransitionOptions, content: VNode | VNode[]): VNode;
204
+ /**
205
+ * TransitionGroup component - animates lists with enter/leave/move transitions
206
+ *
207
+ * @example
208
+ * ```ts
209
+ * ${TransitionGroup({
210
+ * preset: 'slide-right',
211
+ * tag: 'ul',
212
+ * moveClass: 'transition-transform duration-300'
213
+ * }, each(items.value, (item) => html`
214
+ * <li key="${item.id}">${item.text}</li>
215
+ * `))}
216
+ * ```
217
+ */
218
+ export declare function TransitionGroup(options: TransitionGroupOptions, children: VNode[]): VNode;
219
+ /**
220
+ * Helper to create custom transition presets
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * const customFade = createTransitionPreset({
225
+ * enterFrom: 'opacity-0',
226
+ * enterActive: 'transition-opacity duration-500 ease-out',
227
+ * enterTo: 'opacity-100',
228
+ * leaveFrom: 'opacity-100',
229
+ * leaveActive: 'transition-opacity duration-300 ease-in',
230
+ * leaveTo: 'opacity-0'
231
+ * });
232
+ *
233
+ * ${Transition({ ...customFade, show: visible.value }, content)}
234
+ * ```
235
+ */
236
+ export declare function createTransitionPreset(classes: TransitionClasses): TransitionClasses;
237
+ /**
238
+ * Get the global transition stylesheet (creates it if needed)
239
+ */
240
+ 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.0.10",
4
+ "version": "1.1.1",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "web-components",