Skip to content

Commit 3cdd410

Browse files
authored
Add copyable element
1 parent dcd80bf commit 3cdd410

File tree

15 files changed

+196
-14
lines changed

15 files changed

+196
-14
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ A collection of unstyled, headless custom elements designed to make building ric
55
## Overview
66

77
**Hyperkit**
8-
Version: 0.0.1
8+
Version: 0.0.2
99
[Github](https://github.com/hyperlaunch/hyperkit)
1010

1111
**Headless Elements, Supercharged UIs**

packages/docs/markdoc.config.ts

+3
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@ export default defineMarkdocConfig({
110110
"./src/astro-components/Examples/FieldsetRepeater/TransitionFieldsetRepeater.astro",
111111
),
112112
},
113+
copyable: {
114+
render: component("./src/astro-components/Examples/Copyable.astro"),
115+
},
113116
},
114117
extends: [
115118
shiki({

packages/docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hyperkitxyz/docs",
3-
"version": "0.0.1",
3+
"version": "0.0.2",
44
"dependencies": {
55
"@fontsource/poppins": "^5.1.0",
66
"@hyperkitxyz/elements": "workspace:*",

packages/docs/src/assets/robot.png

-4.13 MB
Binary file not shown.
1.14 MB
Loading
1.3 MB
Loading

packages/docs/src/astro-components/ComponentNavItem.astro

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ type Props = {
44
};
55
66
const { href } = Astro.props;
7-
const active = Astro.url.pathname === href;
7+
const normalize = (str: string) => str.replace(/^\/+|\/+$/g, "");
8+
const active = normalize(Astro.url.pathname) === normalize(href);
89
---
910

1011
<li class="pb-2 last:pb-0">
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<hyperkit-copyable value="https://www.hyperkit.xyz/">
2+
<button
3+
class="group px-4 py-2 bg-white text-xs text-gray-800 rounded shadow hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all duration-200"
4+
>
5+
<span class="group-data-[copied]:hidden">Copy</span>
6+
<span class="hidden group-data-[copied]:block">Copied</span>
7+
</button>
8+
</hyperkit-copyable>
9+
<script>
10+
import "@hyperkitxyz/elements/copyable.ts";
11+
</script>

packages/docs/src/astro-components/Layout.astro

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const pageTitle = [title, name].filter(Boolean).join(" — ");
7373
<span
7474
class="px-2 py-1 text-xs font-medium shadow-sm rounded-full bg-zinc-300 dark:bg-zinc-700 text-zinc-800 dark:text-zinc-200"
7575
>
76-
0.0.1
76+
0.0.2
7777
</span>
7878
</h1>
7979

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
name: Copyable
3+
tagline: A button that allows easy copying of text to the clipboard.
4+
thumbnail: "./thumbnails/modal.png"
5+
---
6+
7+
The `<hyperkit-copyable />` element provides an easy way to copy specified text to the user's clipboard. It wraps a button element, which, when clicked, copies the provided text value to the clipboard. Additionally, it fires a `copied` event when the action is successful and sets a `data-copied` attribute on the button.
8+
9+
{% example hero=true %}{% copyable / %}{% /example %}
10+
11+
## Usage
12+
13+
Import the JS:
14+
```js
15+
import "@hyperkitxyz/elements/copyable";
16+
```
17+
18+
Tag:
19+
```html
20+
<hyperkit-copyable value="Text to copy">
21+
<button>Copy</button>
22+
</hyperkit-copyable>
23+
```
24+
25+
### Options
26+
27+
{% table %}
28+
* Attribute
29+
* Value
30+
*
31+
---
32+
* `value`
33+
* String _(required)_
34+
* Specifies the text that will be copied to the clipboard when the button is clicked.
35+
---
36+
{% /table %}
37+
38+
### Children
39+
40+
```html
41+
<hyperkit-copyable value="Text to copy">
42+
<button>Copy</button>
43+
</hyperkit-copyable>
44+
```
45+
46+
#### `<button />`
47+
* The button that triggers the copy action when clicked.
48+
* Can be styled according to your needs and can contain any text or content.
49+
50+
## JavaScript API
51+
52+
The `hyperkit-copyable` element provides a simple JavaScript API to interact with its functionality and listen for events.
53+
54+
### Listening for the `copied` Event
55+
56+
The element emits a `copied` event whenever the text is successfully copied to the clipboard. You can listen for this event to respond to the copy action:
57+
58+
```js
59+
const copyable = document.querySelector("hyperkit-copyable");
60+
61+
copyable.on("copied", (event) => {
62+
console.log("Copied value:", event.detail.value);
63+
});
64+
```
65+
66+
## Examples
67+
68+
### Styling the button
69+
70+
This example shows how you can use the data-copied attribute to toggle the visible text when the button is used.
71+
72+
{% example %}{% copyable / %}{% /example %}
73+
74+
```html
75+
<hyperkit-copyable value="https://www.hyperkit.xyz/">
76+
<button
77+
class="group px-4 py-2 bg-white text-xs text-gray-800 rounded shadow hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-all duration-200"
78+
>
79+
<span class="group-data-[copied]:hidden">Copy</span>
80+
<span class="hidden group-data-[copied]:block">Copied</span>
81+
</button>
82+
</hyperkit-copyable>
83+
```

packages/docs/src/pages/index.astro

+47-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
---
22
import { Picture } from "astro:assets";
3-
import robot from "../assets/robot.png";
3+
import darkRocket from "../assets/rockets/dark.png";
4+
import lightRocket from "../assets/rockets/light.png";
45
import Layout from "../astro-components/Layout.astro";
56
import { getComponentTree } from "../utils/component-tree";
67
@@ -10,10 +11,17 @@ const tree = await getComponentTree();
1011
<Layout>
1112
<div class="flex flex-col items-center justify-end relative">
1213
<Picture
13-
src={robot}
14+
src={lightRocket}
1415
format="webp"
1516
alt=""
16-
class="w-full max-w-5xl"
17+
class="w-full max-w-5xl dark:hidden"
18+
widths={[375, 750, 1200, 1500]}
19+
/>
20+
<Picture
21+
src={darkRocket}
22+
format="webp"
23+
alt=""
24+
class="w-full max-w-5xl hidden dark:block"
1725
widths={[375, 750, 1200, 1500]}
1826
/>
1927
<div class="lg:absolute z-40">
@@ -24,7 +32,7 @@ lg:[text-shadow:_0_1px_50px_rgb(255_255_255/_100%)] dark:[text-shadow:_0_1px_30p
2432
<h1
2533
class="text-3xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl font-black mb-4 lg:mb-6 xl:mb-8"
2634
>
27-
Headless Elements, Supercharged UIs
35+
Headless Elements, Supercharged Sites
2836
</h1>
2937
<p class="md:text-lg lg:text-xl xl:text-2xl">
3038
A suite of unstyled custom elements designed for building rich,
@@ -36,11 +44,40 @@ lg:[text-shadow:_0_1px_50px_rgb(255_255_255/_100%)] dark:[text-shadow:_0_1px_30p
3644
</div>
3745
</div>
3846
</div>
39-
<div
40-
class="bg-zinc-950 dark:bg-white border border-zinc-700 dark:border-zinc-400 rounded shadow-md px-4 py-2 mr-auto lg:mx-auto white-space-no-wrap text-zinc-100 dark:text-zinc-950 text-sm lg:text-base font-medium"
41-
>
42-
bun add @hyperkitxyz/elements
43-
</div>
47+
<hyperkit-copyable value="bun add @hyperkitxyz/elements">
48+
<button
49+
class="group flex items-center gap-x-2 bg-zinc-950 dark:bg-white border border-zinc-700 dark:border-zinc-400 rounded shadow-md px-4 py-2 mr-auto lg:mx-auto white-space-no-wrap text-zinc-100 dark:text-zinc-950 text-sm lg:text-base font-medium"
50+
>
51+
bun add @hyperkitxyz/elements
52+
<svg
53+
xmlns="http://www.w3.org/2000/svg"
54+
fill="none"
55+
viewBox="0 0 24 24"
56+
stroke-width="1.5"
57+
stroke="currentColor"
58+
class="size-5 group-data-[copied]:hidden"
59+
>
60+
<path
61+
stroke-linecap="round"
62+
stroke-linejoin="round"
63+
d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
64+
></path>
65+
</svg>
66+
<svg
67+
xmlns="http://www.w3.org/2000/svg"
68+
fill="none"
69+
viewBox="0 0 24 24"
70+
stroke-width="1.5"
71+
stroke="currentColor"
72+
class="size-5 hidden group-data-[copied]:block"
73+
>
74+
<path
75+
stroke-linecap="round"
76+
stroke-linejoin="round"
77+
d="m4.5 12.75 6 6 9-13.5"></path>
78+
</svg>
79+
</button>
80+
</hyperkit-copyable>
4481
<div class="flex items-center justify-center w-full max-w-3xl">
4582
<div class="relative flex items-center w-full max-w-screen-lg">
4683
<div
@@ -117,4 +154,5 @@ lg:[text-shadow:_0_1px_50px_rgb(255_255_255/_100%)] dark:[text-shadow:_0_1px_30p
117154
</Layout>
118155
<script>
119156
import "@hyperkitxyz/elements/detail.ts";
157+
import "@hyperkitxyz/elements/copyable.ts";
120158
</script>

packages/docs/src/utils/component-tree.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { getEntry } from "astro:content";
33
export async function getComponentTree() {
44
const groups = {
55
UI: ["modal", "popover", "detail"],
6+
UX: ["copyable"],
67
Form: [
78
"select",
89
"sortable",

packages/elements/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@hyperkitxyz/elements",
33
"main": "./src/index.ts",
4-
"version": "0.0.1",
4+
"version": "0.0.2",
55
"repository": {
66
"type": "git",
77
"url": "git+https://github.com/hyperlaunch/hyperkit.git"

packages/elements/src/copyable.ts

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { HyperkitElement } from "./hyperkit-element";
2+
3+
export class HyperkitCopyable extends HyperkitElement<{
4+
events: { type: "copied"; detail: { value: string } };
5+
propagatedEvents: undefined;
6+
propTypes: { value: "string" };
7+
}> {
8+
requiredChildren = ["button"];
9+
public propTypes = { value: "string" } as const;
10+
11+
connectedCallback() {
12+
super.connectedCallback();
13+
14+
requestAnimationFrame(() => {
15+
const button = this.querySelector<HTMLButtonElement>("button");
16+
const valueToCopy = this.prop("value");
17+
18+
const copyHandler = async () => {
19+
if (!button) return;
20+
21+
try {
22+
await navigator.clipboard.writeText(valueToCopy);
23+
button.dataset.copied = "";
24+
this.fire("copied", { detail: { value: valueToCopy } });
25+
} catch (error) {
26+
console.error(error, this);
27+
}
28+
};
29+
30+
button?.addEventListener("click", copyHandler);
31+
});
32+
}
33+
}
34+
35+
if (!customElements.get("hyperkit-copyable"))
36+
customElements.define("hyperkit-copyable", HyperkitCopyable);

vercel.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"rewrites": [
3+
{
4+
"source": "/ingest/:path(.*)",
5+
"destination": "https://eu.posthog.com/:path*"
6+
}
7+
],
8+
"trailingSlash": false
9+
}

0 commit comments

Comments
 (0)