Skip to content

Commit 6f2d0cf

Browse files
committed
init partial function and documentation
1 parent 7e58885 commit 6f2d0cf

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

documentation/docs/06-runtime/04-imperative-component-api.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,46 @@ const app = hydrate(App, {
8181
```
8282

8383
As with `mount`, effects will not run during `hydrate` — use `flushSync()` immediately afterwards if you need them to.
84+
85+
## `partial`
86+
87+
`partial` lets you create *partial components* — components composed of other components.
88+
To best explain partial components, here's an example without them:
89+
```svelte
90+
<script>
91+
import Greeter from './greeter.svelte';
92+
</script>
93+
<Greeter greeting="Hello" name="world" />
94+
<Greeter greeting="Hello" name="Earth" />
95+
```
96+
There's some clear repetition here; the `greeting` prop has the same value twice. Using `partial` we can make this much more concise:
97+
```svelte
98+
<script>
99+
import { partial } from 'svelte';
100+
import Greeter from './greeter.svelte';
101+
const Hello = partial(Greeter, { greeting: 'Hello' });
102+
</script>
103+
<Hello name="world" />
104+
<Hello name="Earth" />
105+
```
106+
Snippets can be used with partial components easily:
107+
```svelte
108+
<script>
109+
import { partial } from 'svelte';
110+
import Paragraph from './paragraph.svelte';
111+
const Example = partial(Paragraph, { example });
112+
</script>
113+
{#snippet example()}
114+
According to all known laws
115+
of aviation,
116+
there is no way a bee
117+
should be able to fly.
118+
Its wings are too small to get
119+
its fat little body off the ground.
120+
The bee, of course, flies anyway
121+
because bees don't care
122+
what humans think is impossible.
123+
{/snippet}
124+
<Example />
125+
<Example />
126+
```

packages/svelte/src/index-client.js

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/** @import { ComponentContext, ComponentContextLegacy } from '#client' */
2-
/** @import { EventDispatcher } from './index.js' */
2+
/** @import { Component, EventDispatcher } from './index.js' */
33
/** @import { NotFunction } from './internal/types.js' */
44
import { untrack } from './internal/client/runtime.js';
55
import { is_array } from './internal/shared/utils.js';
@@ -90,6 +90,60 @@ export function onDestroy(fn) {
9090
onMount(() => () => untrack(fn));
9191
}
9292

93+
/**
94+
* Creates a partial component with the specified props.
95+
* Example:
96+
* ```svelte
97+
* <script>
98+
* import { partial } from 'svelte';
99+
* import Greeter from './greeter.svelte';
100+
*
101+
* const Hello = partial(Greeter, { greeting: 'Hello' });
102+
* </script>
103+
*
104+
* <Hello name="world" />
105+
* <Hello name="Earth" />
106+
* ```
107+
* @template {Record<PropertyKey, any>} Props
108+
* @param {Component<Props>} component
109+
* @param {Partial<Props>} props
110+
* @returns {Component<Partial<Props>>}
111+
*/
112+
export function partial(component, props) {
113+
const initial_props = props;
114+
return function (anchor, props) {
115+
const merged = new Proxy(/** @type {Props} */ ({}), {
116+
get(target, prop) {
117+
if (prop in props) {
118+
return props[prop];
119+
}
120+
return initial_props[prop];
121+
},
122+
set(target, prop, value) {
123+
if (prop in props) {
124+
//@ts-expect-error don't know why this won't work
125+
props[prop] = value;
126+
}
127+
if (prop in initial_props) {
128+
// @ts-expect-error ditto
129+
initial_props[prop] = value;
130+
}
131+
return true;
132+
},
133+
ownKeys() {
134+
const keys = Reflect.ownKeys(props);
135+
for (const key of Reflect.ownKeys(initial_props)) {
136+
if (!keys.includes(key)) {
137+
keys.push(key);
138+
}
139+
}
140+
return keys;
141+
}
142+
});
143+
component(anchor, merged);
144+
};
145+
}
146+
93147
/**
94148
* @template [T=any]
95149
* @param {string} type

packages/svelte/src/index-server.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export function unmount() {
3333
e.lifecycle_function_unavailable('unmount');
3434
}
3535

36+
export { partial } from './index-client.js';
37+
3638
export async function tick() {}
3739

3840
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';

0 commit comments

Comments
 (0)