gonia 0.0.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.
Files changed (54) hide show
  1. package/README.md +119 -0
  2. package/dist/client/hydrate.d.ts +54 -0
  3. package/dist/client/hydrate.js +445 -0
  4. package/dist/client/index.d.ts +7 -0
  5. package/dist/client/index.js +6 -0
  6. package/dist/context.d.ts +40 -0
  7. package/dist/context.js +69 -0
  8. package/dist/directives/class.d.ts +21 -0
  9. package/dist/directives/class.js +42 -0
  10. package/dist/directives/for.d.ts +29 -0
  11. package/dist/directives/for.js +265 -0
  12. package/dist/directives/html.d.ts +16 -0
  13. package/dist/directives/html.js +19 -0
  14. package/dist/directives/if.d.ts +25 -0
  15. package/dist/directives/if.js +133 -0
  16. package/dist/directives/index.d.ts +15 -0
  17. package/dist/directives/index.js +15 -0
  18. package/dist/directives/model.d.ts +27 -0
  19. package/dist/directives/model.js +134 -0
  20. package/dist/directives/on.d.ts +21 -0
  21. package/dist/directives/on.js +54 -0
  22. package/dist/directives/show.d.ts +15 -0
  23. package/dist/directives/show.js +19 -0
  24. package/dist/directives/slot.d.ts +48 -0
  25. package/dist/directives/slot.js +99 -0
  26. package/dist/directives/template.d.ts +55 -0
  27. package/dist/directives/template.js +147 -0
  28. package/dist/directives/text.d.ts +15 -0
  29. package/dist/directives/text.js +18 -0
  30. package/dist/expression.d.ts +60 -0
  31. package/dist/expression.js +96 -0
  32. package/dist/index.d.ts +19 -0
  33. package/dist/index.js +16 -0
  34. package/dist/inject.d.ts +42 -0
  35. package/dist/inject.js +63 -0
  36. package/dist/providers.d.ts +96 -0
  37. package/dist/providers.js +146 -0
  38. package/dist/reactivity.d.ts +95 -0
  39. package/dist/reactivity.js +219 -0
  40. package/dist/scope.d.ts +43 -0
  41. package/dist/scope.js +112 -0
  42. package/dist/server/index.d.ts +7 -0
  43. package/dist/server/index.js +6 -0
  44. package/dist/server/render.d.ts +61 -0
  45. package/dist/server/render.js +243 -0
  46. package/dist/templates.d.ts +92 -0
  47. package/dist/templates.js +124 -0
  48. package/dist/types.d.ts +362 -0
  49. package/dist/types.js +110 -0
  50. package/dist/vite/index.d.ts +6 -0
  51. package/dist/vite/index.js +6 -0
  52. package/dist/vite/plugin.d.ts +30 -0
  53. package/dist/vite/plugin.js +127 -0
  54. package/package.json +67 -0
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Context creation and management.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { findRoots } from './expression.js';
7
+ /**
8
+ * Create an evaluation context for directives.
9
+ *
10
+ * @remarks
11
+ * Uses {@link findRoots} to only access state keys that the expression
12
+ * actually references, enabling precise dependency tracking with the
13
+ * reactive system.
14
+ *
15
+ * Supports scoped values via `get()` for things like $component, $renderingChain.
16
+ * Create child contexts with `child()` for nested scopes.
17
+ *
18
+ * @param mode - Execution mode (server or client)
19
+ * @param state - The reactive state object
20
+ * @param scope - Optional scoped values (for context-specific data)
21
+ * @returns A new context
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * const state = reactive({ user: { name: 'Alice' } });
26
+ * const ctx = createContext(Mode.CLIENT, state);
27
+ * ctx.eval('user.name' as Expression); // 'Alice'
28
+ *
29
+ * const childCtx = ctx.child({ $component: el });
30
+ * childCtx.get('$component'); // el
31
+ * ```
32
+ */
33
+ export function createContext(mode, state, scope = {}) {
34
+ const ctx = {
35
+ mode,
36
+ eval(expr) {
37
+ const roots = findRoots(expr);
38
+ const fn = new Function(...roots, `return (${expr})`);
39
+ const values = roots.map(r => {
40
+ if (r in scope) {
41
+ return scope[r];
42
+ }
43
+ return state[r];
44
+ });
45
+ return fn(...values);
46
+ },
47
+ get(key) {
48
+ if (key in scope) {
49
+ return scope[key];
50
+ }
51
+ if (key in state) {
52
+ return state[key];
53
+ }
54
+ return undefined;
55
+ },
56
+ child(additions) {
57
+ return createContext(mode, state, { ...scope, ...additions });
58
+ }
59
+ };
60
+ return ctx;
61
+ }
62
+ /**
63
+ * Create a child context with additional bindings.
64
+ *
65
+ * @deprecated Use ctx.child() instead
66
+ */
67
+ export function createChildContext(parent, additions) {
68
+ return parent.child(additions);
69
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Dynamic class binding directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Directive } from '../types.js';
7
+ /**
8
+ * Bind classes dynamically based on an expression.
9
+ *
10
+ * @remarks
11
+ * Evaluates the expression to get a `{ className: boolean }` object.
12
+ * Classes with truthy values are added, falsy values are removed.
13
+ * Static classes from the HTML are preserved.
14
+ *
15
+ * @example
16
+ * ```html
17
+ * <div c-class="{ active: isActive, 'text-red': hasError }">
18
+ * <div c-class="{ [dynamicClass]: true }">
19
+ * ```
20
+ */
21
+ export declare const cclass: Directive<['$expr', '$element', '$eval']>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Dynamic class binding directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { directive } from '../types.js';
7
+ import { effect } from '../reactivity.js';
8
+ /**
9
+ * Bind classes dynamically based on an expression.
10
+ *
11
+ * @remarks
12
+ * Evaluates the expression to get a `{ className: boolean }` object.
13
+ * Classes with truthy values are added, falsy values are removed.
14
+ * Static classes from the HTML are preserved.
15
+ *
16
+ * @example
17
+ * ```html
18
+ * <div c-class="{ active: isActive, 'text-red': hasError }">
19
+ * <div c-class="{ [dynamicClass]: true }">
20
+ * ```
21
+ */
22
+ export const cclass = function cclass($expr, $element, $eval) {
23
+ effect(() => {
24
+ const classObj = $eval($expr);
25
+ if (classObj === null || classObj === undefined) {
26
+ return;
27
+ }
28
+ if (typeof classObj !== 'object') {
29
+ return;
30
+ }
31
+ for (const [className, shouldAdd] of Object.entries(classObj)) {
32
+ if (shouldAdd) {
33
+ $element.classList.add(className);
34
+ }
35
+ else {
36
+ $element.classList.remove(className);
37
+ }
38
+ }
39
+ });
40
+ };
41
+ cclass.$inject = ['$expr', '$element', '$eval'];
42
+ directive('c-class', cclass);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Loop/iteration directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Directive } from '../types.js';
7
+ /** Attribute used to mark elements processed by c-for */
8
+ export declare const FOR_PROCESSED_ATTR = "data-c-for-processed";
9
+ /** Attribute used to mark template content that should be skipped during SSR */
10
+ export declare const FOR_TEMPLATE_ATTR = "data-c-for-template";
11
+ /**
12
+ * Iterate over array or object items.
13
+ *
14
+ * @remarks
15
+ * Creates a copy of the template element for each item in the iterable.
16
+ * Supports arrays and objects. For arrays, provides item and index.
17
+ * For objects, provides value and key.
18
+ *
19
+ * On server: wraps template in <template> element, renders items after it.
20
+ * On client: finds <template c-for>, extracts template, sets up reactive loop.
21
+ *
22
+ * @example
23
+ * ```html
24
+ * <li c-for="item in items" c-text="item.name"></li>
25
+ * <li c-for="(item, index) in items" c-text="index + ': ' + item.name"></li>
26
+ * <div c-for="(value, key) in object" c-text="key + ': ' + value"></div>
27
+ * ```
28
+ */
29
+ export declare const cfor: Directive<['$expr', '$element', '$eval', '$state', '$mode']>;
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Loop/iteration directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { directive, DirectivePriority, Mode } from '../types.js';
7
+ import { effect, createEffectScope, createScope } from '../reactivity.js';
8
+ import { createContext } from '../context.js';
9
+ /**
10
+ * Parse a c-for expression.
11
+ *
12
+ * Supports:
13
+ * - `item in items`
14
+ * - `(item, index) in items`
15
+ * - `(value, key) in object`
16
+ */
17
+ function parseForExpression(expr) {
18
+ const trimmed = expr.trim();
19
+ // Match "(item, index) in iterable" or "item in iterable"
20
+ const match = trimmed.match(/^\(?([a-zA-Z_$][a-zA-Z0-9_$]*)(?:\s*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\)?\s+in\s+(.+)$/);
21
+ if (!match) {
22
+ return null;
23
+ }
24
+ return {
25
+ itemName: match[1],
26
+ indexName: match[2] || null,
27
+ iterableName: match[3].trim()
28
+ };
29
+ }
30
+ /** Attribute used to mark elements processed by c-for */
31
+ export const FOR_PROCESSED_ATTR = 'data-c-for-processed';
32
+ /** Attribute used to mark template content that should be skipped during SSR */
33
+ export const FOR_TEMPLATE_ATTR = 'data-c-for-template';
34
+ /**
35
+ * Process directives on a cloned element within a child scope.
36
+ */
37
+ function processClonedElement(el, parentState, scopeAdditions, mode) {
38
+ // Mark this element as processed by c-for so hydrate skips it
39
+ el.setAttribute(FOR_PROCESSED_ATTR, '');
40
+ const childScope = createScope(parentState, scopeAdditions);
41
+ const childCtx = createContext(mode, childScope);
42
+ // Process c-text directives
43
+ const textAttr = el.getAttribute('c-text');
44
+ if (textAttr) {
45
+ const value = childCtx.eval(textAttr);
46
+ el.textContent = String(value ?? '');
47
+ }
48
+ // Process c-class directives
49
+ const classAttr = el.getAttribute('c-class');
50
+ if (classAttr) {
51
+ const classObj = childCtx.eval(classAttr);
52
+ if (classObj && typeof classObj === 'object') {
53
+ for (const [className, shouldAdd] of Object.entries(classObj)) {
54
+ if (shouldAdd) {
55
+ el.classList.add(className);
56
+ }
57
+ else {
58
+ el.classList.remove(className);
59
+ }
60
+ }
61
+ }
62
+ }
63
+ // Process c-show directives
64
+ const showAttr = el.getAttribute('c-show');
65
+ if (showAttr) {
66
+ const value = childCtx.eval(showAttr);
67
+ el.style.display = value ? '' : 'none';
68
+ }
69
+ // Process c-on directives (format: "event: handler") - client only
70
+ if (mode === Mode.CLIENT) {
71
+ const onAttr = el.getAttribute('c-on');
72
+ if (onAttr) {
73
+ setupEventHandler(el, onAttr, childCtx, childScope);
74
+ }
75
+ }
76
+ // Process children recursively
77
+ for (const child of el.children) {
78
+ processClonedElement(child, childScope, {}, mode);
79
+ }
80
+ }
81
+ /**
82
+ * Set up an event handler on an element.
83
+ * Expression format: "event: handler"
84
+ */
85
+ function setupEventHandler(el, expr, ctx, state) {
86
+ const colonIdx = expr.indexOf(':');
87
+ if (colonIdx === -1) {
88
+ console.error(`Invalid c-on expression: ${expr}. Expected "event: handler"`);
89
+ return;
90
+ }
91
+ const eventName = expr.slice(0, colonIdx).trim();
92
+ const handlerExpr = expr.slice(colonIdx + 1).trim();
93
+ const handler = (event) => {
94
+ const result = ctx.eval(handlerExpr);
95
+ if (typeof result === 'function') {
96
+ result.call(state, event);
97
+ }
98
+ };
99
+ el.addEventListener(eventName, handler);
100
+ }
101
+ /**
102
+ * Render loop items (used by both server and client).
103
+ */
104
+ function renderItems(template, parent, insertAfterNode, parsed, $eval, $state, mode) {
105
+ const { itemName, indexName, iterableName } = parsed;
106
+ const iterable = $eval(iterableName);
107
+ const renderedElements = [];
108
+ if (iterable == null) {
109
+ return renderedElements;
110
+ }
111
+ let items;
112
+ if (Array.isArray(iterable)) {
113
+ items = iterable.map((item, index) => [item, index]);
114
+ }
115
+ else if (typeof iterable === 'object') {
116
+ items = Object.entries(iterable).map(([k, v]) => [v, k]);
117
+ }
118
+ else {
119
+ return renderedElements;
120
+ }
121
+ let insertAfter = insertAfterNode;
122
+ const length = items.length;
123
+ for (let i = 0; i < length; i++) {
124
+ const [value, key] = items[i];
125
+ const clone = template.cloneNode(true);
126
+ // Remove template marker from clone (it's not a template, it's a rendered item)
127
+ clone.removeAttribute(FOR_TEMPLATE_ATTR);
128
+ const scopeAdditions = {
129
+ [itemName]: value,
130
+ $index: i,
131
+ $first: i === 0,
132
+ $last: i === length - 1,
133
+ $even: i % 2 === 0,
134
+ $odd: i % 2 === 1
135
+ };
136
+ if (indexName) {
137
+ scopeAdditions[indexName] = key;
138
+ }
139
+ processClonedElement(clone, $state, scopeAdditions, mode);
140
+ if (insertAfter.nextSibling) {
141
+ parent.insertBefore(clone, insertAfter.nextSibling);
142
+ }
143
+ else {
144
+ parent.appendChild(clone);
145
+ }
146
+ renderedElements.push(clone);
147
+ insertAfter = clone;
148
+ }
149
+ return renderedElements;
150
+ }
151
+ /**
152
+ * Remove SSR-rendered items (siblings with FOR_PROCESSED_ATTR after template).
153
+ */
154
+ function removeSSRItems(templateEl) {
155
+ let sibling = templateEl.nextElementSibling;
156
+ while (sibling && sibling.hasAttribute(FOR_PROCESSED_ATTR)) {
157
+ const next = sibling.nextElementSibling;
158
+ sibling.remove();
159
+ sibling = next;
160
+ }
161
+ }
162
+ /**
163
+ * Iterate over array or object items.
164
+ *
165
+ * @remarks
166
+ * Creates a copy of the template element for each item in the iterable.
167
+ * Supports arrays and objects. For arrays, provides item and index.
168
+ * For objects, provides value and key.
169
+ *
170
+ * On server: wraps template in <template> element, renders items after it.
171
+ * On client: finds <template c-for>, extracts template, sets up reactive loop.
172
+ *
173
+ * @example
174
+ * ```html
175
+ * <li c-for="item in items" c-text="item.name"></li>
176
+ * <li c-for="(item, index) in items" c-text="index + ': ' + item.name"></li>
177
+ * <div c-for="(value, key) in object" c-text="key + ': ' + value"></div>
178
+ * ```
179
+ */
180
+ export const cfor = function cfor($expr, $element, $eval, $state, $mode) {
181
+ const parsed = parseForExpression($expr);
182
+ if (!parsed) {
183
+ console.error(`Invalid c-for expression: ${$expr}`);
184
+ return;
185
+ }
186
+ const parent = $element.parentNode;
187
+ if (!parent) {
188
+ return;
189
+ }
190
+ const isTemplateElement = $element.tagName === 'TEMPLATE';
191
+ // Server-side rendering
192
+ if ($mode === Mode.SERVER) {
193
+ // Create a <template> element to hold the original
194
+ const templateWrapper = $element.ownerDocument.createElement('template');
195
+ templateWrapper.setAttribute('c-for', $expr);
196
+ // Clone the original element (without c-for attr) into the template
197
+ const templateContent = $element.cloneNode(true);
198
+ templateContent.removeAttribute('c-for');
199
+ // Mark as template content so render loop skips it
200
+ templateContent.setAttribute(FOR_TEMPLATE_ATTR, '');
201
+ // Append directly to template element (linkedom doesn't support .content)
202
+ // Browsers will automatically move this to .content when parsing
203
+ templateWrapper.appendChild(templateContent);
204
+ // Replace original with template wrapper
205
+ parent.replaceChild(templateWrapper, $element);
206
+ // Render items after the template
207
+ renderItems(templateContent, parent, templateWrapper, parsed, $eval, $state, $mode);
208
+ return;
209
+ }
210
+ // Client-side: check if hydrating from SSR or fresh render
211
+ if (isTemplateElement) {
212
+ // Hydrating from SSR: element is <template c-for="...">
213
+ const templateWrapper = $element;
214
+ const templateContent = templateWrapper.content.firstElementChild;
215
+ if (!templateContent) {
216
+ console.error('c-for template element has no content');
217
+ return;
218
+ }
219
+ // Remove SSR-rendered items
220
+ removeSSRItems(templateWrapper);
221
+ // Set up reactive loop
222
+ let renderedElements = [];
223
+ let scope = null;
224
+ effect(() => {
225
+ if (scope) {
226
+ scope.stop();
227
+ }
228
+ for (const el of renderedElements) {
229
+ el.remove();
230
+ }
231
+ scope = createEffectScope();
232
+ scope.run(() => {
233
+ renderedElements = renderItems(templateContent, parent, templateWrapper, parsed, $eval, $state, Mode.CLIENT);
234
+ });
235
+ });
236
+ }
237
+ else {
238
+ // Fresh client render: element has c-for attribute directly
239
+ // Wrap in template element for consistency
240
+ const templateWrapper = $element.ownerDocument.createElement('template');
241
+ templateWrapper.setAttribute('c-for', $expr);
242
+ const templateContent = $element.cloneNode(true);
243
+ templateContent.removeAttribute('c-for');
244
+ templateWrapper.content.appendChild(templateContent);
245
+ parent.replaceChild(templateWrapper, $element);
246
+ // Set up reactive loop
247
+ let renderedElements = [];
248
+ let scope = null;
249
+ effect(() => {
250
+ if (scope) {
251
+ scope.stop();
252
+ }
253
+ for (const el of renderedElements) {
254
+ el.remove();
255
+ }
256
+ scope = createEffectScope();
257
+ scope.run(() => {
258
+ renderedElements = renderItems(templateContent, parent, templateWrapper, parsed, $eval, $state, Mode.CLIENT);
259
+ });
260
+ });
261
+ }
262
+ };
263
+ cfor.$inject = ['$expr', '$element', '$eval', '$state', '$mode'];
264
+ cfor.priority = DirectivePriority.STRUCTURAL;
265
+ directive('c-for', cfor);
@@ -0,0 +1,16 @@
1
+ import { Directive } from '../types.js';
2
+ /**
3
+ * Set element's innerHTML from an expression.
4
+ *
5
+ * @remarks
6
+ * Evaluates the expression and sets the element's `innerHTML`.
7
+ *
8
+ * @warning Only use with trusted content. User input can lead to XSS attacks.
9
+ *
10
+ * @example
11
+ * ```html
12
+ * <div c-html="formattedContent"></div>
13
+ * <div c-html="'<strong>' + title + '</strong>'"></div>
14
+ * ```
15
+ */
16
+ export declare const html: Directive<['$expr', '$element', '$eval']>;
@@ -0,0 +1,19 @@
1
+ import { directive } from '../types.js';
2
+ /**
3
+ * Set element's innerHTML from an expression.
4
+ *
5
+ * @remarks
6
+ * Evaluates the expression and sets the element's `innerHTML`.
7
+ *
8
+ * @warning Only use with trusted content. User input can lead to XSS attacks.
9
+ *
10
+ * @example
11
+ * ```html
12
+ * <div c-html="formattedContent"></div>
13
+ * <div c-html="'<strong>' + title + '</strong>'"></div>
14
+ * ```
15
+ */
16
+ export const html = function html($expr, $element, $eval) {
17
+ $element.innerHTML = String($eval($expr) ?? '');
18
+ };
19
+ directive('c-html', html);
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Conditional rendering directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Directive } from '../types.js';
7
+ /** Attribute used to mark elements processed by c-if */
8
+ export declare const IF_PROCESSED_ATTR = "data-c-if-processed";
9
+ /**
10
+ * Conditionally render an element.
11
+ *
12
+ * @remarks
13
+ * Unlike c-show which uses display:none, c-if completely removes
14
+ * the element from the DOM when the condition is falsy.
15
+ *
16
+ * On server: evaluates once and removes element if false.
17
+ * On client: sets up reactive effect to toggle element.
18
+ *
19
+ * @example
20
+ * ```html
21
+ * <div c-if="isLoggedIn">Welcome back!</div>
22
+ * <div c-if="items.length > 0">Has items</div>
23
+ * ```
24
+ */
25
+ export declare const cif: Directive<['$expr', '$element', '$eval', '$state', '$mode']>;
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Conditional rendering directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { directive, DirectivePriority, Mode } from '../types.js';
7
+ import { effect } from '../reactivity.js';
8
+ import { createContext } from '../context.js';
9
+ import { createScope } from '../reactivity.js';
10
+ /** Attribute used to mark elements processed by c-if */
11
+ export const IF_PROCESSED_ATTR = 'data-c-if-processed';
12
+ /**
13
+ * Process directives on a conditionally rendered element.
14
+ */
15
+ function processConditionalElement(el, parentState, mode) {
16
+ el.setAttribute(IF_PROCESSED_ATTR, '');
17
+ const childScope = createScope(parentState, {});
18
+ const childCtx = createContext(mode, childScope);
19
+ // Process c-text directives
20
+ const textAttr = el.getAttribute('c-text');
21
+ if (textAttr) {
22
+ const value = childCtx.eval(textAttr);
23
+ el.textContent = String(value ?? '');
24
+ }
25
+ // Process c-class directives
26
+ const classAttr = el.getAttribute('c-class');
27
+ if (classAttr) {
28
+ const classObj = childCtx.eval(classAttr);
29
+ if (classObj && typeof classObj === 'object') {
30
+ for (const [className, shouldAdd] of Object.entries(classObj)) {
31
+ if (shouldAdd) {
32
+ el.classList.add(className);
33
+ }
34
+ else {
35
+ el.classList.remove(className);
36
+ }
37
+ }
38
+ }
39
+ }
40
+ // Process c-show directives
41
+ const showAttr = el.getAttribute('c-show');
42
+ if (showAttr) {
43
+ const value = childCtx.eval(showAttr);
44
+ el.style.display = value ? '' : 'none';
45
+ }
46
+ // Process c-on directives (format: "event: handler") - client only
47
+ if (mode === Mode.CLIENT) {
48
+ const onAttr = el.getAttribute('c-on');
49
+ if (onAttr) {
50
+ const colonIdx = onAttr.indexOf(':');
51
+ if (colonIdx !== -1) {
52
+ const eventName = onAttr.slice(0, colonIdx).trim();
53
+ const handlerExpr = onAttr.slice(colonIdx + 1).trim();
54
+ el.addEventListener(eventName, (event) => {
55
+ const result = childCtx.eval(handlerExpr);
56
+ if (typeof result === 'function') {
57
+ result.call(childScope, event);
58
+ }
59
+ });
60
+ }
61
+ }
62
+ }
63
+ // Process children recursively
64
+ for (const child of el.children) {
65
+ processConditionalElement(child, childScope, mode);
66
+ }
67
+ }
68
+ /**
69
+ * Conditionally render an element.
70
+ *
71
+ * @remarks
72
+ * Unlike c-show which uses display:none, c-if completely removes
73
+ * the element from the DOM when the condition is falsy.
74
+ *
75
+ * On server: evaluates once and removes element if false.
76
+ * On client: sets up reactive effect to toggle element.
77
+ *
78
+ * @example
79
+ * ```html
80
+ * <div c-if="isLoggedIn">Welcome back!</div>
81
+ * <div c-if="items.length > 0">Has items</div>
82
+ * ```
83
+ */
84
+ export const cif = function cif($expr, $element, $eval, $state, $mode) {
85
+ const parent = $element.parentNode;
86
+ if (!parent) {
87
+ return;
88
+ }
89
+ // Server-side: evaluate once and remove if false
90
+ if ($mode === Mode.SERVER) {
91
+ const condition = $eval($expr);
92
+ if (!condition) {
93
+ $element.remove();
94
+ }
95
+ else {
96
+ // Process child directives
97
+ $element.removeAttribute('c-if');
98
+ processConditionalElement($element, $state, $mode);
99
+ }
100
+ return;
101
+ }
102
+ // Client-side: set up reactive effect
103
+ const placeholder = $element.ownerDocument.createComment(` c-if: ${$expr} `);
104
+ parent.insertBefore(placeholder, $element);
105
+ const template = $element.cloneNode(true);
106
+ template.removeAttribute('c-if');
107
+ $element.remove();
108
+ let renderedElement = null;
109
+ effect(() => {
110
+ const condition = $eval($expr);
111
+ if (condition) {
112
+ if (!renderedElement) {
113
+ renderedElement = template.cloneNode(true);
114
+ processConditionalElement(renderedElement, $state, Mode.CLIENT);
115
+ if (placeholder.nextSibling) {
116
+ parent.insertBefore(renderedElement, placeholder.nextSibling);
117
+ }
118
+ else {
119
+ parent.appendChild(renderedElement);
120
+ }
121
+ }
122
+ }
123
+ else {
124
+ if (renderedElement) {
125
+ renderedElement.remove();
126
+ renderedElement = null;
127
+ }
128
+ }
129
+ });
130
+ };
131
+ cif.$inject = ['$expr', '$element', '$eval', '$state', '$mode'];
132
+ cif.priority = DirectivePriority.STRUCTURAL;
133
+ directive('c-if', cif);
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Built-in directives.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { text } from './text.js';
7
+ export { html } from './html.js';
8
+ export { show } from './show.js';
9
+ export { template } from './template.js';
10
+ export { slot, processNativeSlot } from './slot.js';
11
+ export { cclass } from './class.js';
12
+ export { model } from './model.js';
13
+ export { on } from './on.js';
14
+ export { cfor } from './for.js';
15
+ export { cif } from './if.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Built-in directives.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export { text } from './text.js';
7
+ export { html } from './html.js';
8
+ export { show } from './show.js';
9
+ export { template } from './template.js';
10
+ export { slot, processNativeSlot } from './slot.js';
11
+ export { cclass } from './class.js';
12
+ export { model } from './model.js';
13
+ export { on } from './on.js';
14
+ export { cfor } from './for.js';
15
+ export { cif } from './if.js';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Two-way binding directive.
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ import { Directive } from '../types.js';
7
+ /**
8
+ * Bind form element value to state with two-way data binding.
9
+ *
10
+ * @remarks
11
+ * Updates the element value when state changes, and updates state
12
+ * when the user modifies the element. Handles different input types:
13
+ * - text/textarea: binds to value, uses input event
14
+ * - checkbox: binds to checked, uses change event
15
+ * - radio: binds to checked, uses change event
16
+ * - select: binds to value, uses change event
17
+ * - number: binds to value (as number), uses input event
18
+ *
19
+ * @example
20
+ * ```html
21
+ * <input c-model="name">
22
+ * <input type="checkbox" c-model="isActive">
23
+ * <select c-model="selectedOption">
24
+ * <textarea c-model="description"></textarea>
25
+ * ```
26
+ */
27
+ export declare const model: Directive<['$expr', '$element', '$eval', '$rootState']>;