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';