fusee-framework 1.1.1 → 1.2.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.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Fusée Framework 🚀
2
- **v1.1.1 — Signals-First JS Framework | Atomic Reactivity | Peak Performance**
2
+ **v1.2.0 — Signals-First JS Framework | Atomic Reactivity | Peak Performance**
3
3
 
4
- Fusée is a custom, high-performance fine-grained reactive JavaScript framework built for speed and simplicity. It features a recursive, non-greedy compiler, a signals-based reactivity engine, and a comprehensive CLI for instant application scaffolding.
4
+ Fusée is a custom, high-performance fine-grained reactive JavaScript framework built for speed and simplicity. It features a recursive, non-greedy compiler, a signals-based reactivity engine, Dependency Injection for components and a comprehensive CLI for instant application scaffolding.
5
5
 
6
6
  ---
7
7
 
@@ -0,0 +1,32 @@
1
+ export const Card = defineComponent({
2
+ props: {
3
+ title: { type: String, default: 'Default Card' }
4
+ },
5
+ setup(props) {
6
+ return {
7
+ get title() { return props.title },
8
+ template: `
9
+ <div style="border: 2px solid #333; border-radius: 8px; padding: 20px; margin: 20px 0; background: #1a1a1a;">
10
+ <div style="border-bottom: 1px solid #444; padding-bottom: 10px; margin-bottom: 15px;">
11
+ <h2 style="margin: 0; color: #fff;">{{ title }}</h2>
12
+ <slot name="header">
13
+ <p style="color: #888; font-size: 0.9rem;">Default header content</p>
14
+ </slot>
15
+ </div>
16
+
17
+ <div style="color: #ccc; line-height: 1.6;">
18
+ <slot>
19
+ <p>This is the default slot content. You can override this by passing content between component tags.</p>
20
+ </slot>
21
+ </div>
22
+
23
+ <div style="border-top: 1px solid #444; padding-top: 10px; margin-top: 15px;">
24
+ <slot name="footer">
25
+ <p style="color: #666; font-size: 0.8rem; text-align: right;">Default footer</p>
26
+ </slot>
27
+ </div>
28
+ </div>
29
+ `
30
+ }
31
+ }
32
+ })
@@ -0,0 +1,41 @@
1
+ export interface CardProps {
2
+ title?: string
3
+ }
4
+
5
+ export interface CardResult {
6
+ get title(): string
7
+ template: string
8
+ }
9
+
10
+ export const Card = defineComponent({
11
+ props: {
12
+ title: { type: String, default: 'Default Card' }
13
+ },
14
+ setup(props: CardProps): CardResult {
15
+ return {
16
+ get title(): string { return props.title || 'Default Card' },
17
+ template: `
18
+ <div style="border: 2px solid #333; border-radius: 8px; padding: 20px; margin: 20px 0; background: #1a1a1a;">
19
+ <div style="border-bottom: 1px solid #444; padding-bottom: 10px; margin-bottom: 15px;">
20
+ <h2 style="margin: 0; color: #fff;">{{ title }}</h2>
21
+ <slot name="header">
22
+ <p style="color: #888; font-size: 0.9rem;">Default header content</p>
23
+ </slot>
24
+ </div>
25
+
26
+ <div style="color: #ccc; line-height: 1.6;">
27
+ <slot>
28
+ <p>This is the default slot content. You can override this by passing content between component tags.</p>
29
+ </slot>
30
+ </div>
31
+
32
+ <div style="border-top: 1px solid #444; padding-top: 10px; margin-top: 15px;">
33
+ <slot name="footer">
34
+ <p style="color: #666; font-size: 0.8rem; text-align: right;">Default footer</p>
35
+ </slot>
36
+ </div>
37
+ </div>
38
+ `
39
+ }
40
+ }
41
+ })
@@ -1,13 +1,13 @@
1
- import { defineComponent, signal, computed, effect, batch, untrack, onMount, onUnmount } from '../../framework/index.js'
2
1
 
3
2
  export const Counter = defineComponent({
4
3
  props: {
5
4
  initialValue: { type: String, default: '0' },
6
5
  parentTitle: { type: String, default: '' }
7
6
  },
8
- setup(props) {
7
+ setup(props, { emit }) {
9
8
  const count = signal(Number(props.initialValue) || 0)
10
9
  const multiplier = signal(1)
10
+ inspect(count, multiplier)
11
11
 
12
12
  const double = computed(() => {
13
13
  console.log('[Counter] computing double...')
@@ -30,9 +30,7 @@ export const Counter = defineComponent({
30
30
  transform: `scale(${1 + (Math.abs(count()) % 3) * 0.1})`
31
31
  }))
32
32
 
33
- effect(() => {
34
- console.log(`[Counter Effect] Count: ${count()} | Step ignored in tracking: ${untrack(() => multiplier())}`)
35
- })
33
+ inspect(() => `Count: ${count()} | Step ignored in tracking: ${untrack(() => multiplier())}`)
36
34
 
37
35
  function updateMultiple() {
38
36
  batch(() => {
@@ -60,6 +58,7 @@ export const Counter = defineComponent({
60
58
  updateMultiple,
61
59
  get parentTitle() { return props.parentTitle },
62
60
  displayStyle,
61
+ emit,
63
62
  template: `
64
63
  <div class="counter" :title="counterTitle" @keydown.arrowup.window="increment" @keydown.arrowdown.window="decrement">
65
64
  <h2>Counter</h2>
@@ -1,4 +1,3 @@
1
-
2
1
  type CounterProps = {
3
2
  initialValue?: string
4
3
  parentTitle?: string
@@ -27,6 +26,7 @@ export const Counter = defineComponent({
27
26
  setup(props: CounterProps): CounterResult {
28
27
  const count = signal<number>(Number(props.initialValue) || 0)
29
28
  const multiplier = signal<number>(1)
29
+ inspect(count, multiplier)
30
30
 
31
31
  const double = computed(() => count() * 2)
32
32
 
@@ -40,9 +40,7 @@ export const Counter = defineComponent({
40
40
  `Counter: ${count()} (×${multiplier()})`
41
41
  )
42
42
 
43
- effect(() => {
44
- console.log(`[Counter Effect] Count: ${count()} | Step ignored in tracking: ${untrack(() => multiplier())}`)
45
- })
43
+ inspect(() => `[Counter] Count: ${count()} | Step ignored: ${untrack(() => multiplier())}`)
46
44
 
47
45
  function updateMultiple(): void {
48
46
  batch(() => {
@@ -1,4 +1,3 @@
1
- import { defineComponent, signal } from '../../framework/index.js'
2
1
  import { Counter } from './Counter.js'
3
2
 
4
3
  export const CounterSection = defineComponent({
@@ -6,20 +5,27 @@ export const CounterSection = defineComponent({
6
5
  pageTitle: { type: String, default: '' }
7
6
  },
8
7
  components: { Counter },
9
- setup(props) {
8
+ setup(props, { emit }) {
10
9
  const showCounter = signal(true)
11
-
10
+ inspect(showCounter)
11
+
12
12
  return {
13
13
  showCounter,
14
14
  get title() { return props.pageTitle },
15
15
  toggleCounter: () => showCounter(!showCounter()),
16
+
17
+ // 🔄 Re-emit handlers - forward events from Counter to parent
18
+ handleCounterChange: (value) => emit('counter-change', value),
19
+ handleCounterReset: () => emit('counter-reset'),
20
+ handleCustomEvent: (count, multiplier) => emit('custom-event', count, multiplier),
21
+
16
22
  template: `
17
23
  <div style="margin-top: 30px; border-top: 1px solid #333; padding-top: 20px;">
18
24
  <h3>Counter Section</h3>
19
25
  <button @click="toggleCounter" class="primary-btn" style="margin: 10px 0;">Toggle Counter (f-if)</button>
20
26
 
21
27
  <div f-if="showCounter">
22
- {{ Counter initialValue="10" :parentTitle="title" }}
28
+ {{ Counter initialValue="10" :parentTitle="title" @counter-change="handleCounterChange" @counter-reset="handleCounterReset" @custom-event="handleCustomEvent" }}
23
29
  </div>
24
30
  <div f-elif="title === 'Welcome!'">
25
31
  <p style="color: yellow;">Counter is hidden, but title is "Welcome!"</p>
@@ -0,0 +1,77 @@
1
+ export const EmitTest = defineComponent({
2
+ setup(props, { emit }) {
3
+ const clickCount = signal(0)
4
+ const lastEvent = signal('None')
5
+
6
+ inspect(clickCount, lastEvent)
7
+
8
+ return {
9
+ clickCount,
10
+ lastEvent,
11
+
12
+ // Butoane pentru test emit
13
+ emitSimple: () => {
14
+ clickCount(clickCount() + 1)
15
+ emit('simple-click')
16
+ lastEvent('simple-click')
17
+ },
18
+ emitWithData: () => {
19
+ const count = clickCount() + 1
20
+ clickCount(count)
21
+ emit('click-with-data', count, 'button-pressed', Date.now())
22
+ lastEvent(`click-with-data: ${count}`)
23
+ },
24
+ emitObject: () => {
25
+ const data = {
26
+ timestamp: Date.now(),
27
+ message: 'Hello from child!',
28
+ counter: clickCount() + 1
29
+ }
30
+ clickCount(data.counter)
31
+ emit('object-event', data)
32
+ lastEvent(`object-event: ${data.message}`)
33
+ },
34
+
35
+ template: `
36
+ <div style="background: linear-gradient(135deg, #1e3a5f 0%, #0d1b2a 100%); border: 2px solid #4da6ff; border-radius: 12px; padding: 24px; margin: 20px 0; color: #fff;">
37
+ <h3 style="margin: 0 0 16px 0; color: #4da6ff;">🧪 EMIT EVENTS TEST LAB</h3>
38
+
39
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 20px;">
40
+ <button @click="emitSimple"
41
+ style="padding: 16px; background: #10b981; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 1rem;">
42
+ 📤 EMIT SIMPLE
43
+ <small style="display: block; font-size: 0.8rem; font-weight: normal; margin-top: 4px;">No data</small>
44
+ </button>
45
+
46
+ <button @click="emitWithData"
47
+ style="padding: 16px; background: #f59e0b; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 1rem;">
48
+ 📤 EMIT WITH DATA
49
+ <small style="display: block; font-size: 0.8rem; font-weight: normal; margin-top: 4px;">count, label, timestamp</small>
50
+ </button>
51
+
52
+ <button @click="emitObject"
53
+ style="padding: 16px; background: #8b5cf6; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 1rem;">
54
+ 📤 EMIT OBJECT
55
+ <small style="display: block; font-size: 0.8rem; font-weight: normal; margin-top: 4px;">{ timestamp, message, counter }</small>
56
+ </button>
57
+ </div>
58
+
59
+ <div style="background: rgba(0,0,0,0.3); padding: 16px; border-radius: 8px; font-family: monospace;">
60
+ <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
61
+ <span style="color: #888;">Click Count:</span>
62
+ <span style="color: #4da6ff; font-size: 1.5rem; font-weight: bold;">{{ clickCount }}</span>
63
+ </div>
64
+ <div style="display: flex; justify-content: space-between;">
65
+ <span style="color: #888;">Last Event:</span>
66
+ <span style="color: #f59e0b;">{{ lastEvent }}</span>
67
+ </div>
68
+ </div>
69
+
70
+ <p style="margin: 16px 0 0 0; font-size: 0.85rem; color: #888; text-align: center;">
71
+ ✅ Check browser console for emitted events!
72
+ </p>
73
+ </div>
74
+ `
75
+ }
76
+ }
77
+ })
@@ -0,0 +1,90 @@
1
+ export interface EmitTestProps {
2
+ initialCount?: number
3
+ }
4
+
5
+ export interface EmitTestResult {
6
+ clickCount: SignalAccessor<number>
7
+ lastEvent: SignalAccessor<string>
8
+ emitSimple: () => void
9
+ emitWithData: () => void
10
+ emitObject: () => void
11
+ template: string
12
+ }
13
+
14
+ export const EmitTest = defineComponent({
15
+ setup(props: EmitTestProps, { emit }: { emit: (eventName: string, ...args: any[]) => void }): EmitTestResult {
16
+ const clickCount = signal<number>(props.initialCount ?? 0)
17
+ const lastEvent = signal<string>('None')
18
+
19
+ inspect(clickCount, lastEvent)
20
+
21
+ return {
22
+ clickCount,
23
+ lastEvent,
24
+
25
+ // Butoane pentru test emit
26
+ emitSimple: () => {
27
+ clickCount(clickCount() + 1)
28
+ emit('simple-click')
29
+ lastEvent('simple-click')
30
+ },
31
+ emitWithData: () => {
32
+ const count = clickCount() + 1
33
+ clickCount(count)
34
+ emit('click-with-data', count, 'button-pressed', Date.now())
35
+ lastEvent(`click-with-data: ${count}`)
36
+ },
37
+ emitObject: () => {
38
+ const data = {
39
+ timestamp: Date.now(),
40
+ message: 'Hello from child!',
41
+ counter: clickCount() + 1
42
+ }
43
+ clickCount(data.counter)
44
+ emit('object-event', data)
45
+ lastEvent(`object-event: ${data.message}`)
46
+ },
47
+
48
+ template: `
49
+ <div style="background: linear-gradient(135deg, #1e3a5f 0%, #0d1b2a 100%); border: 2px solid #4da6ff; border-radius: 12px; padding: 24px; margin: 20px 0; color: #fff;">
50
+ <h3 style="margin: 0 0 16px 0; color: #4da6ff;">🧪 EMIT EVENTS TEST LAB</h3>
51
+
52
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 20px;">
53
+ <button @click="emitSimple"
54
+ style="padding: 16px; background: #10b981; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 1rem;">
55
+ 📤 EMIT SIMPLE
56
+ <small style="display: block; font-size: 0.8rem; font-weight: normal; margin-top: 4px;">No data</small>
57
+ </button>
58
+
59
+ <button @click="emitWithData"
60
+ style="padding: 16px; background: #f59e0b; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 1rem;">
61
+ 📤 EMIT WITH DATA
62
+ <small style="display: block; font-size: 0.8rem; font-weight: normal; margin-top: 4px;">count, label, timestamp</small>
63
+ </button>
64
+
65
+ <button @click="emitObject"
66
+ style="padding: 16px; background: #8b5cf6; color: white; border: none; border-radius: 8px; cursor: pointer; font-weight: bold; font-size: 1rem;">
67
+ 📤 EMIT OBJECT
68
+ <small style="display: block; font-size: 0.8rem; font-weight: normal; margin-top: 4px;">{ timestamp, message, counter }</small>
69
+ </button>
70
+ </div>
71
+
72
+ <div style="background: rgba(0,0,0,0.3); padding: 16px; border-radius: 8px; font-family: monospace;">
73
+ <div style="display: flex; justify-content: space-between; margin-bottom: 8px;">
74
+ <span style="color: #888;">Click Count:</span>
75
+ <span style="color: #4da6ff; font-size: 1.5rem; font-weight: bold;">{{ clickCount }}</span>
76
+ </div>
77
+ <div style="display: flex; justify-content: space-between;">
78
+ <span style="color: #888;">Last Event:</span>
79
+ <span style="color: #f59e0b;">{{ lastEvent }}</span>
80
+ </div>
81
+ </div>
82
+
83
+ <p style="margin: 16px 0 0 0; font-size: 0.85rem; color: #888; text-align: center;">
84
+ ✅ Check browser console for emitted events!
85
+ </p>
86
+ </div>
87
+ `
88
+ }
89
+ }
90
+ })
@@ -2,6 +2,7 @@ export const UserForm = defineComponent({
2
2
  setup() {
3
3
  const userName = signal('Guest')
4
4
  const inputRef = signal(null)
5
+ inspect(userName)
5
6
 
6
7
  return {
7
8
  userName,
@@ -1,5 +1,3 @@
1
- import { defineComponent, signal, type SignalAccessor } from '../../framework/index.js'
2
-
3
1
  type UserFormResult = {
4
2
  userName: SignalAccessor<string>
5
3
  inputRef: SignalAccessor<HTMLInputElement | null>
package/app/main.ts CHANGED
@@ -1,11 +1,13 @@
1
- import { createRouter, mountOutlet } from '../framework/index.js'
1
+ import { createRouter, provide, inject } from '../framework/index.js'
2
2
  import type { Route } from '../framework/types/index.js'
3
3
  import { Home } from './routes/Home.js'
4
4
  import { About } from './routes/About.js'
5
+ import { TestFor } from './routes/TestFor.js'
5
6
 
6
7
  const routes: Route[] = [
7
8
  { path: '/', component: Home },
8
9
  { path: '/about', component: About },
10
+ { path: '/test-for', component: TestFor },
9
11
  ]
10
12
 
11
13
  createRouter(routes)
@@ -0,0 +1,44 @@
1
+ import { inject } from '../../framework/index.js'
2
+ import { GrandchildComponent } from './GrandchildComponent.js'
3
+
4
+ // ChildComponent - injects values from parent (ParentComponent)
5
+ export const ChildComponent = defineComponent({
6
+ components: { GrandchildComponent },
7
+ setup(props, { emit }) {
8
+ // Inject values provided by the parent
9
+ const count = inject('count')
10
+ const message = inject('message')
11
+
12
+ // These are NOT provided by parent, so they will be undefined
13
+ const nonExistent = inject('nonExistent')
14
+
15
+ function updateMessage() {
16
+ message('Message updated by Child!')
17
+ }
18
+
19
+ return {
20
+ count,
21
+ message,
22
+ nonExistent,
23
+ updateMessage,
24
+ template: `
25
+ <div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
26
+ <h3 style="color: #2196F3;">Child Component (direct child of Parent)</h3>
27
+ <p>I'm injecting these values from my parent:</p>
28
+ <ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
29
+ <li><strong>count:</strong> {{ count }}</li>
30
+ <li><strong>message:</strong> {{ message }}</li>
31
+ <li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
32
+ </ul>
33
+ <button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
34
+ Update Message
35
+ </button>
36
+
37
+ <div style="margin-top: 15px;">
38
+ {{ GrandchildComponent }}
39
+ </div>
40
+ </div>
41
+ `
42
+ }
43
+ }
44
+ })
@@ -0,0 +1,47 @@
1
+ import { inject, provide } from '../../framework/index.js'
2
+ import { GrandchildComponent } from './GrandchildComponent.js'
3
+
4
+ // ChildComponent - injects values from parent (ParentComponent)
5
+ export const ChildComponent = defineComponent({
6
+ components: { GrandchildComponent },
7
+ setup() {
8
+ // Inject values provided by the parent
9
+ const count = inject('count')
10
+ const message = inject('message')
11
+
12
+ // These are NOT provided by parent, so they will be undefined
13
+ const nonExistent = inject('nonExistent')
14
+
15
+ function updateMessage(): void {
16
+ const msg = message as any
17
+ if (msg) {
18
+ msg('Message updated by Child!')
19
+ }
20
+ }
21
+
22
+ return {
23
+ count,
24
+ message,
25
+ nonExistent,
26
+ updateMessage,
27
+ template: `
28
+ <div style="padding: 15px; border: 2px solid #2196F3; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
29
+ <h3 style="color: #2196F3;">Child Component (direct child of Parent)</h3>
30
+ <p>I'm injecting these values from my parent:</p>
31
+ <ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
32
+ <li><strong>count:</strong> {{ count }}</li>
33
+ <li><strong>message:</strong> {{ message }}</li>
34
+ <li><strong>nonExistent:</strong> {{ nonExistent || 'undefined' }}</li>
35
+ </ul>
36
+ <button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">
37
+ Update Message
38
+ </button>
39
+
40
+ <div style="margin-top: 15px;">
41
+ {{ GrandchildComponent }}
42
+ </div>
43
+ </div>
44
+ `
45
+ }
46
+ }
47
+ })
@@ -0,0 +1,41 @@
1
+ import { inject } from '../../framework/index.js'
2
+
3
+ // GrandchildComponent - injects values from grandparent (ParentComponent)
4
+ // This demonstrates that inject() searches the entire ancestor chain
5
+ export const GrandchildComponent = defineComponent({
6
+ setup(props, { emit }) {
7
+ // Inject from grandparent (2 levels up)
8
+ const count = inject('count')
9
+ const message = inject('message')
10
+ const userData = inject('userData')
11
+ const appVersion = inject('appVersion')
12
+
13
+ function updateMessage() {
14
+ // message is a signal, so we can update it from grandchild!
15
+ message('Updated from Grandchild!')
16
+ }
17
+
18
+ return {
19
+ count,
20
+ message,
21
+ userData,
22
+ appVersion,
23
+ updateMessage,
24
+ template: `
25
+ <div style="padding: 15px; border: 2px solid #9C27B0; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
26
+ <h4 style="color: #9C27B0;">Grandchild Component (injected from grandparent!)</h4>
27
+ <p>I can inject from any ancestor, not just my direct parent:</p>
28
+ <ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
29
+ <li><strong>count:</strong> {{ count }} <em>(from ParentComponent)</em></li>
30
+ <li><strong>message:</strong> {{ message }} <em>(from ParentComponent)</em></li>
31
+ <li><strong>userData:</strong> {{ userData.name }}, {{ userData.age }} <em>(from ParentComponent)</em></li>
32
+ <li><strong>appVersion:</strong> {{ appVersion }} <em>(from ParentComponent)</em></li>
33
+ </ul>
34
+ <button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #9C27B0; color: white; border: none; border-radius: 4px; cursor: pointer;">
35
+ Update Message from Grandchild
36
+ </button>
37
+ </div>
38
+ `
39
+ }
40
+ }
41
+ })
@@ -0,0 +1,48 @@
1
+ import { inject, provide } from '../../framework/index.js'
2
+
3
+ interface UserData {
4
+ name: string
5
+ age: number
6
+ }
7
+
8
+ // GrandchildComponent - injects values from grandparent (ParentComponent)
9
+ // This demonstrates that inject() searches the entire ancestor chain
10
+ export const GrandchildComponent = defineComponent({
11
+ setup() {
12
+ // Inject from grandparent (2 levels up)
13
+ const count = inject('count')
14
+ const message = inject('message')
15
+ const userData = inject('userData')
16
+ const appVersion = inject('appVersion')
17
+
18
+ function updateMessage(): void {
19
+ const msg = message as any
20
+ if (msg) {
21
+ msg('Updated from Grandchild!')
22
+ }
23
+ }
24
+
25
+ return {
26
+ count,
27
+ message,
28
+ userData,
29
+ appVersion,
30
+ updateMessage,
31
+ template: `
32
+ <div style="padding: 15px; border: 2px solid #9C27B0; border-radius: 8px; margin-top: 15px; background: #1a1a1a; color: #fff;">
33
+ <h4 style="color: #9C27B0;">Grandchild Component (injected from grandparent!)</h4>
34
+ <p>I can inject from any ancestor, not just my direct parent:</p>
35
+ <ul style="background: #333; padding: 12px; border-radius: 4px; color: #fff;">
36
+ <li><strong>count:</strong> {{ count }} <em>(from ParentComponent)</em></li>
37
+ <li><strong>message:</strong> {{ message }} <em>(from ParentComponent)</em></li>
38
+ <li><strong>userData:</strong> {{ userData.name }}, {{ userData.age }} <em>(from ParentComponent)</em></li>
39
+ <li><strong>appVersion:</strong> {{ appVersion }} <em>(from ParentComponent)</em></li>
40
+ </ul>
41
+ <button @click="updateMessage" style="margin-top: 10px; padding: 6px 12px; background: #9C27B0; color: white; border: none; border-radius: 4px; cursor: pointer;">
42
+ Update Message from Grandchild
43
+ </button>
44
+ </div>
45
+ `
46
+ }
47
+ }
48
+ })
@@ -0,0 +1,48 @@
1
+ import { provide } from '../../framework/index.js'
2
+ import { ChildComponent } from './ChildComponent.js'
3
+
4
+ // ParentComponent - provides values to child components
5
+ export const ParentComponent = defineComponent({
6
+ components: { ChildComponent },
7
+ setup(props, { emit }) {
8
+ const count = signal(42)
9
+ const message = signal('Hello from Parent!')
10
+ const userData = { name: 'John', age: 30 }
11
+
12
+ // Provide values to descendant components
13
+ provide('count', count)
14
+ provide('message', message)
15
+ provide('userData', userData)
16
+ provide('appVersion', '1.0.0')
17
+
18
+ function updateCount() {
19
+ count(count() + 10)
20
+ }
21
+
22
+ return {
23
+ count,
24
+ message,
25
+ userData,
26
+ updateCount,
27
+ template: `
28
+ <div style="padding: 20px; border: 2px solid #4CAF50; border-radius: 8px; margin: 20px 0; color: #000;">
29
+ <h2 style="color: #4CAF50;">Parent Component</h2>
30
+ <p>I'm providing these values to my children:</p>
31
+ <ul style="background: #333; padding: 15px; border-radius: 4px; color: #fff;">
32
+ <li><strong>count:</strong> {{ count }}</li>
33
+ <li><strong>message:</strong> {{ message }}</li>
34
+ <li><strong>userData:</strong> {{ userData.name }} ({{ userData.age }} years)</li>
35
+ <li><strong>appVersion:</strong> 1.0.0</li>
36
+ </ul>
37
+ <button @click="updateCount" style="margin-top: 10px; padding: 8px 16px; background: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">
38
+ Update Count (+10)
39
+ </button>
40
+
41
+ <div style="margin-top: 20px;">
42
+ {{ ChildComponent }}
43
+ </div>
44
+ </div>
45
+ `
46
+ }
47
+ }
48
+ })