init `partial` function and documentation

partial-components
ComputerGuy 3 months ago
parent 7e588857c2
commit 6f2d0cf764

@ -81,3 +81,46 @@ const app = hydrate(App, {
```
As with `mount`, effects will not run during `hydrate` — use `flushSync()` immediately afterwards if you need them to.
## `partial`
`partial` lets you create *partial components* — components composed of other components.
To best explain partial components, here's an example without them:
```svelte
<script>
import Greeter from './greeter.svelte';
</script>
<Greeter greeting="Hello" name="world" />
<Greeter greeting="Hello" name="Earth" />
```
There's some clear repetition here; the `greeting` prop has the same value twice. Using `partial` we can make this much more concise:
```svelte
<script>
import { partial } from 'svelte';
import Greeter from './greeter.svelte';
const Hello = partial(Greeter, { greeting: 'Hello' });
</script>
<Hello name="world" />
<Hello name="Earth" />
```
Snippets can be used with partial components easily:
```svelte
<script>
import { partial } from 'svelte';
import Paragraph from './paragraph.svelte';
const Example = partial(Paragraph, { example });
</script>
{#snippet example()}
According to all known laws
of aviation,
there is no way a bee
should be able to fly.
Its wings are too small to get
its fat little body off the ground.
The bee, of course, flies anyway
because bees don't care
what humans think is impossible.
{/snippet}
<Example />
<Example />
```

@ -1,5 +1,5 @@
/** @import { ComponentContext, ComponentContextLegacy } from '#client' */
/** @import { EventDispatcher } from './index.js' */
/** @import { Component, EventDispatcher } from './index.js' */
/** @import { NotFunction } from './internal/types.js' */
import { untrack } from './internal/client/runtime.js';
import { is_array } from './internal/shared/utils.js';
@ -90,6 +90,60 @@ export function onDestroy(fn) {
onMount(() => () => untrack(fn));
}
/**
* Creates a partial component with the specified props.
* Example:
* ```svelte
* <script>
* import { partial } from 'svelte';
* import Greeter from './greeter.svelte';
*
* const Hello = partial(Greeter, { greeting: 'Hello' });
* </script>
*
* <Hello name="world" />
* <Hello name="Earth" />
* ```
* @template {Record<PropertyKey, any>} Props
* @param {Component<Props>} component
* @param {Partial<Props>} props
* @returns {Component<Partial<Props>>}
*/
export function partial(component, props) {
const initial_props = props;
return function (anchor, props) {
const merged = new Proxy(/** @type {Props} */ ({}), {
get(target, prop) {
if (prop in props) {
return props[prop];
}
return initial_props[prop];
},
set(target, prop, value) {
if (prop in props) {
//@ts-expect-error don't know why this won't work
props[prop] = value;
}
if (prop in initial_props) {
// @ts-expect-error ditto
initial_props[prop] = value;
}
return true;
},
ownKeys() {
const keys = Reflect.ownKeys(props);
for (const key of Reflect.ownKeys(initial_props)) {
if (!keys.includes(key)) {
keys.push(key);
}
}
return keys;
}
});
component(anchor, merged);
};
}
/**
* @template [T=any]
* @param {string} type

@ -33,6 +33,8 @@ export function unmount() {
e.lifecycle_function_unavailable('unmount');
}
export { partial } from './index-client.js';
export async function tick() {}
export { getAllContexts, getContext, hasContext, setContext } from './internal/server/context.js';

Loading…
Cancel
Save