From 6f2d0cf764ef2cd4f8016249fbebc04c64163c15 Mon Sep 17 00:00:00 2001
From: ComputerGuy <63362464+Ocean-OS@users.noreply.github.com>
Date: Tue, 17 Jun 2025 02:35:32 -0700
Subject: [PATCH] init `partial` function and documentation
---
.../06-runtime/04-imperative-component-api.md | 43 ++++++++++++++
packages/svelte/src/index-client.js | 56 ++++++++++++++++++-
packages/svelte/src/index-server.js | 2 +
3 files changed, 100 insertions(+), 1 deletion(-)
diff --git a/documentation/docs/06-runtime/04-imperative-component-api.md b/documentation/docs/06-runtime/04-imperative-component-api.md
index 16a2c8151c..1e077c9f6d 100644
--- a/documentation/docs/06-runtime/04-imperative-component-api.md
+++ b/documentation/docs/06-runtime/04-imperative-component-api.md
@@ -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
+
+
+
+```
+There's some clear repetition here; the `greeting` prop has the same value twice. Using `partial` we can make this much more concise:
+```svelte
+
+
+
+```
+Snippets can be used with partial components easily:
+```svelte
+
+{#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}
+
+
+```
\ No newline at end of file
diff --git a/packages/svelte/src/index-client.js b/packages/svelte/src/index-client.js
index efd5628ae9..9aa8ac2557 100644
--- a/packages/svelte/src/index-client.js
+++ b/packages/svelte/src/index-client.js
@@ -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
+ *
+ *
+ *
+ *
+ * ```
+ * @template {Record} Props
+ * @param {Component} component
+ * @param {Partial} props
+ * @returns {Component>}
+ */
+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
diff --git a/packages/svelte/src/index-server.js b/packages/svelte/src/index-server.js
index 0f1aff8f5a..b5f2ae5120 100644
--- a/packages/svelte/src/index-server.js
+++ b/packages/svelte/src/index-server.js
@@ -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';