From 8eeb5fbd4446a55dc351b786cbec473911beff0e Mon Sep 17 00:00:00 2001 From: Sean Lynch Date: Mon, 12 Oct 2026 10:56:51 -0400 Subject: [PATCH] feat: support custom annotation label and mark snippets --- .changeset/annotation-snippets.md | 5 + docs/src/content/components/AnnotationLine.md | 14 ++ .../src/content/components/AnnotationPoint.md | 6 + .../src/content/components/AnnotationRange.md | 8 ++ .../AnnotationLine/custom-label.svelte | 31 +++++ .../AnnotationLine/custom-line.svelte | 22 +++ .../AnnotationPoint/custom-marker.svelte | 43 ++++++ .../AnnotationRange/custom-label.svelte | 39 ++++++ .../AnnotationLine/AnnotationLine.base.svelte | 41 +++--- .../AnnotationLine.shared.svelte.ts | 17 ++- .../AnnotationPoint.base.svelte | 56 +++++--- .../AnnotationPoint.shared.svelte.ts | 18 ++- .../AnnotationRange.base.svelte | 38 ++++-- .../AnnotationRange.shared.svelte.ts | 18 ++- .../lib/components/Annotations.svelte.test.ts | 127 ++++++++++++++++++ 15 files changed, 425 insertions(+), 58 deletions(-) create mode 100644 .changeset/annotation-snippets.md create mode 100644 docs/src/examples/components/AnnotationLine/custom-label.svelte create mode 100644 docs/src/examples/components/AnnotationLine/custom-line.svelte create mode 100644 docs/src/examples/components/AnnotationPoint/custom-marker.svelte create mode 100644 docs/src/examples/components/AnnotationRange/custom-label.svelte create mode 100644 packages/layerchart/src/lib/components/Annotations.svelte.test.ts diff --git a/.changeset/annotation-snippets.md b/.changeset/annotation-snippets.md new file mode 100644 index 0000000000..6fdcd7ae0f --- /dev/null +++ b/.changeset/annotation-snippets.md @@ -0,0 +1,5 @@ +--- +'layerchart': minor +--- + +feat(AnnotationLine|AnnotationPoint|AnnotationRange): Accept a `label` snippet and a mark snippet (`line`, `circle`, `rect`) to render custom markup diff --git a/docs/src/content/components/AnnotationLine.md b/docs/src/content/components/AnnotationLine.md index b610fcbec6..ecdd118a0a 100644 --- a/docs/src/content/components/AnnotationLine.md +++ b/docs/src/content/components/AnnotationLine.md @@ -8,3 +8,17 @@ related: [AnnotationPoint, AnnotationRange] ## Usage :example{ name="horizontal" showCode } + +## Customization + +### Custom label + +Pass a `label` snippet to render your own markup. It receives the resolved label `props` (`x`, `y`, `textAnchor`, `verticalAnchor`, …) to position it with. + +:example{ name="custom-label" showCode } + +### Custom line + +Pass a `line` snippet to draw the line yourself. It receives the resolved `x1`/`y1`/`x2`/`y2` (and `props.line`) to spread. + +:example{ name="custom-line" showCode } diff --git a/docs/src/content/components/AnnotationPoint.md b/docs/src/content/components/AnnotationPoint.md index 62b91b1186..561bad2767 100644 --- a/docs/src/content/components/AnnotationPoint.md +++ b/docs/src/content/components/AnnotationPoint.md @@ -15,6 +15,12 @@ Pass `link` (or `link={{ type: 'swoop', ... }}` etc.) to draw a `` from the ring edge to the label. :example{ name="link-callouts" } +### Custom marker + +Pass a `circle` snippet to draw your own marker, and a `label` snippet for your own label. Each receives the resolved `props` to spread. + +:example{ name="custom-marker" showCode } + ### Geo Inside a geo ``, `x`/`y` are interpreted as `[lon, lat]` and projected directly. diff --git a/docs/src/content/components/AnnotationRange.md b/docs/src/content/components/AnnotationRange.md index 63e43421a5..a64d62d834 100644 --- a/docs/src/content/components/AnnotationRange.md +++ b/docs/src/content/components/AnnotationRange.md @@ -8,3 +8,11 @@ related: [AnnotationLine, AnnotationPoint] ## Usage :example{ name="vertical-with-pattern-range" showCode } + +## Customization + +### Custom label + +Pass a `label` snippet to render your own markup (and a `rect` snippet to draw the range yourself). Each receives the resolved `props` to position it with. + +:example{ name="custom-label" showCode } diff --git a/docs/src/examples/components/AnnotationLine/custom-label.svelte b/docs/src/examples/components/AnnotationLine/custom-label.svelte new file mode 100644 index 0000000000..9d035229d6 --- /dev/null +++ b/docs/src/examples/components/AnnotationLine/custom-label.svelte @@ -0,0 +1,31 @@ + + + + + + {#snippet aboveContext()} + + + {#snippet label({ props })} +
+ Max · $500 +
+ {/snippet} +
+
+ {/snippet} +
diff --git a/docs/src/examples/components/AnnotationLine/custom-line.svelte b/docs/src/examples/components/AnnotationLine/custom-line.svelte new file mode 100644 index 0000000000..960199e33f --- /dev/null +++ b/docs/src/examples/components/AnnotationLine/custom-line.svelte @@ -0,0 +1,22 @@ + + + + + + {#snippet aboveMarks()} + + {#snippet line({ props })} + + + + {/snippet} + + {/snippet} + diff --git a/docs/src/examples/components/AnnotationPoint/custom-marker.svelte b/docs/src/examples/components/AnnotationPoint/custom-marker.svelte new file mode 100644 index 0000000000..61da004822 --- /dev/null +++ b/docs/src/examples/components/AnnotationPoint/custom-marker.svelte @@ -0,0 +1,43 @@ + + + + + + {#snippet aboveMarks()} + {#each annotations as annotation} + + {#snippet circle({ props })} + + + {/snippet} + + {/each} + {/snippet} + diff --git a/docs/src/examples/components/AnnotationRange/custom-label.svelte b/docs/src/examples/components/AnnotationRange/custom-label.svelte new file mode 100644 index 0000000000..b88d1e92e1 --- /dev/null +++ b/docs/src/examples/components/AnnotationRange/custom-label.svelte @@ -0,0 +1,39 @@ + + + + + + {#snippet belowContext()} + + + {#snippet label({ props })} +
+ Fiscal 2010 +
+ {/snippet} +
+
+ {/snippet} +
diff --git a/packages/layerchart/src/lib/components/AnnotationLine/AnnotationLine.base.svelte b/packages/layerchart/src/lib/components/AnnotationLine/AnnotationLine.base.svelte index 14e52056ee..bc75e18d0f 100644 --- a/packages/layerchart/src/lib/components/AnnotationLine/AnnotationLine.base.svelte +++ b/packages/layerchart/src/lib/components/AnnotationLine/AnnotationLine.base.svelte @@ -13,6 +13,8 @@ - - -{#if label} - +{#if lineSnippet} + {@render lineSnippet({ props: resolvedLineProps })} +{:else} + +{/if} + +{#if typeof label === 'function'} + {@render label({ props: resolvedLabelProps })} +{:else if label} + {/if}