Skip to content

Commit a5641ee

Browse files
committed
feat: add addon snippet to textarea for Clipboard
1 parent ea574f3 commit a5641ee

File tree

4 files changed

+55
-13
lines changed

4 files changed

+55
-13
lines changed

src/lib/forms/textarea/Textarea.svelte

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22
import { textarea } from ".";
33
import { type TextareaProps, CloseButton, cn } from "$lib";
44
5-
let { header, footer, value = $bindable(), elementRef = $bindable(), divClass, innerClass, headerClass, footerClass, disabled, class: className, cols, clearable, clearableSvgClass, clearableColor = "none", clearableClass, clearableOnClick, textareaClass, ...restProps }: TextareaProps = $props();
5+
let { header, footer, addon, value = $bindable(), elementRef = $bindable(), divClass, innerClass, headerClass, footerClass, addonClass, disabled, class: className, cols, clearable, clearableSvgClass, clearableColor = "none", clearableClass, clearableOnClick, textareaClass, ...restProps }: TextareaProps = $props();
66
77
let hasHeader = $derived(!!header);
88
let hasFooter = $derived(!!footer);
9-
let wrapped: boolean = $derived(hasHeader || hasFooter);
9+
let hasAddon = $derived(!!addon);
10+
let wrapped: boolean = $derived(hasHeader || hasFooter || hasAddon);
1011
11-
const { divWrapper, base, wrapper, innerWrapper, headerCls, footerCls, clearbtn } = $derived(textarea({ wrapped, hasHeader, hasFooter, cols: !!cols }));
12+
const { divWrapper, base, wrapper, innerWrapper, headerCls, footerCls, addonCls, clearbtn } = $derived(textarea({ wrapped, hasHeader, hasFooter, cols: !!cols }));
1213
1314
const clearAll = () => {
1415
if (elementRef) {
@@ -30,6 +31,11 @@
3031
</div>
3132
{/if}
3233
<div class={cn(innerWrapper(), innerClass)}>
34+
{#if addon}
35+
<div class={cn(addonCls(), addonClass)}>
36+
{@render addon()}
37+
</div>
38+
{/if}
3339
<textarea bind:value bind:this={elementRef} {disabled} {...restProps} class={cn(base(), textareaClass)}></textarea>
3440
</div>
3541
{#if footer}

src/lib/forms/textarea/theme.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export const textarea = tv({
1010
innerWrapper: "py-2 px-4 bg-white dark:bg-gray-800",
1111
headerCls: "py-2 px-3 border-gray-200 dark:border-gray-500",
1212
footerCls: "py-2 px-3 border-gray-200 dark:border-gray-500",
13+
addonCls: "absolute top-2 right-2 z-10",
1314
clearbtn: "absolute right-2 top-5 -translate-y-1/2 text-gray-400 hover:text-black"
1415
},
1516
variants: {

src/lib/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,13 +913,15 @@ export interface TimepickerProps {
913913
export interface TextareaProps extends HTMLTextareaAttributes {
914914
header?: Snippet;
915915
footer?: Snippet;
916+
addon?: Snippet;
916917
value?: string;
917918
elementRef?: HTMLTextAreaElement;
918919
wrapped?: boolean;
919920
divClass?: ClassValue;
920921
innerClass?: ClassValue;
921922
headerClass?: ClassValue;
922923
footerClass?: ClassValue;
924+
addonClass?: ClassValue;
923925
cols?: number;
924926
clearable?: boolean;
925927
clearableSvgClass?: ClassValue;

src/routes/docs/components/clipboard.md

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ Notice the different style of monitoring the `success` state by using the parame
6363
let value = $state("npm install flowbite");
6464
</script>
6565
66-
<div>
67-
<Input bind:value class="w-64">
66+
<div class="w-64">
67+
<Input bind:value>
6868
{#snippet right()}
6969
<Clipboard bind:value embedded>
7070
{#snippet children(success)}
@@ -89,8 +89,8 @@ Use this example to show a copy button inside the input field with a text label
8989
let value = $state("npm install flowbite");
9090
</script>
9191
92-
<div>
93-
<Input bind:value class="w-64 text-sm">
92+
<div class="w-64">
93+
<Input bind:value class="text-sm">
9494
{#snippet right()}
9595
<Clipboard size="xs" color="alternative" bind:value class="-mr-1 w-20 focus:ring-0">
9696
{#snippet children(success)}
@@ -163,20 +163,24 @@ Use this example to copy a shortened URL to the clipboard by clicking on a butto
163163
This example can be used to copy and paste code inside a `<pre>` and `<code>` block by clicking on a button with an icon position inside the block and also show a tooltip with a message when the text has been copied.
164164

165165
```svelte example class="flex justify-center items-center gap-2 h-96"
166-
<script>
167-
import { Clipboard, Input, Label, Helper, Button } from "flowbite-svelte";
166+
<script lang="ts">
167+
import { Clipboard, Label, Helper } from "flowbite-svelte";
168168
import { CheckOutline, ClipboardCleanSolid } from "flowbite-svelte-icons";
169169
170170
let value = $state("");
171171
let success = $state(false);
172172
173-
function onclick(ev) {
174-
value = ev.target.ownerDocument.querySelector("#code-block").innerText;
173+
function onclick(ev: MouseEvent): void {
174+
const target = ev.target as HTMLElement;
175+
const codeBlock = target.ownerDocument.querySelector("#code-block");
176+
if (codeBlock) {
177+
value = codeBlock.textContent || "";
178+
}
175179
}
176180
</script>
177181
178182
<div class="w-full max-w-lg space-y-1">
179-
<Label>Card example URL shortener:</Label>
183+
<Label>Copy source code block:</Label>
180184
<div class="relative h-64 rounded-lg bg-gray-50 p-4 dark:bg-gray-700">
181185
<div class="max-h-full overflow-scroll">
182186
<pre><code id="code-block" class="text-sm whitespace-pre text-gray-500 dark:text-gray-400">
@@ -186,7 +190,7 @@ This example can be used to copy and paste code inside a `<pre>` and `<code>` bl
186190
&#x3C;Button color="primary"&#x3E;Generate&#x3C;/Button&#x3E;
187191
&#x3C;Input id="url-shortener" bind:value readonly disabled class="w-64" /&#x3E;
188192
&#x3C;Clipboard bind:value&#x3E;
189-
{#snippet children(success)}
193+
{#snippet children(success: boolean)}
190194
&#x3C;Tooltip class="whitespace-nowrap"&#x3E;{success ? "Copied" : "Copy link"}&#x3C;/Tooltip&#x3E;
191195
{#if success}&#x3C;CheckOutline /&#x3E;{:else}&#x3C;ClipboardCleanSolid /&#x3E;{/if}
192196
{/snippet}
@@ -271,3 +275,32 @@ Use this example to show multiple input field elements that have the copy to cli
271275
</form>
272276
</Card>
273277
```
278+
279+
## Copy Textarea
280+
281+
Add a `Clipboard` to your `Textarea` using the `addon` snippet. The button appears in the top-right corner when there's content to copy.
282+
283+
```svelte example
284+
<script lang="ts">
285+
import { Clipboard, Textarea } from "flowbite-svelte";
286+
import { CheckOutline, ClipboardCleanSolid } from "flowbite-svelte-icons";
287+
288+
let value = $state("");
289+
let success = $state(false);
290+
291+
</script>
292+
293+
<Textarea id="textarea-id" placeholder="Your message" rows={4} name="message" bind:value={value}>
294+
{#snippet addon()}
295+
{#if value.length>0}
296+
<Clipboard color={success ? "alternative" : "light"} bind:value bind:success size="sm" class="absolute end-2 top-2 h-8 px-2.5 font-medium focus:ring-0 w-32">
297+
{#if success}
298+
<CheckOutline class="h-3 w-3" /> Copied
299+
{:else}
300+
<ClipboardCleanSolid class="h-3 w-3" /> Copy text
301+
{/if}
302+
</Clipboard>
303+
{/if}
304+
{/snippet}
305+
</Textarea>
306+
```

0 commit comments

Comments
 (0)