Skip to content

Commit dde133b

Browse files
committed
feat: data actions
1 parent c15405a commit dde133b

File tree

6 files changed

+115
-6
lines changed

6 files changed

+115
-6
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
.button {
2+
background-color: var(--acd-color-blue-700);
3+
border: none;
4+
border-radius: var(--acd-size-1);
5+
color: var(--acd-color-white);
6+
cursor: pointer;
7+
font-weight: var(--acd-font-medium);
8+
padding: var(--acd-size-2) var(--acd-size-3-5);
9+
}
10+
11+
.button:hover:not(.danger, .success) {
12+
background-color: var(--acd-color-blue-600);
13+
}
14+
15+
.button:active:not(.danger, .success) {
16+
background-color: var(--acd-color-blue-500);
17+
}
18+
19+
.success {
20+
background-color: var(--acd-color-emerald-600);
21+
}
22+
23+
.danger {
24+
background-color: var(--acd-color-rose-600);
25+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { createSignal, onCleanup, type JSX, type ParentComponent } from 'solid-js'
2+
3+
import styles from './DataButton.module.css'
4+
5+
const intentTimeoutDurationInMs = 1000
6+
7+
export const DataButton: ParentComponent<DataButtonProps> = (props) => {
8+
if (!props.onClick && !props.copyData) {
9+
throw new Error(`DataButton must have either an 'onClick' or 'copyData' prop.`)
10+
}
11+
12+
const [intent, setIntent] = createSignal<DataButtonIntent | undefined>(undefined)
13+
let intentTimeout: number | undefined
14+
15+
onCleanup(() => {
16+
if (intentTimeout) {
17+
clearTimeout(intentTimeout)
18+
}
19+
})
20+
21+
function setIntentWithTimeout(intent: DataButtonIntent) {
22+
if (intentTimeout) {
23+
clearTimeout(intentTimeout)
24+
}
25+
26+
setIntent(intent)
27+
28+
intentTimeout = setTimeout(() => {
29+
setIntent(undefined)
30+
}, intentTimeoutDurationInMs)
31+
}
32+
33+
async function handleCopyData() {
34+
if (!props.copyData) {
35+
return
36+
}
37+
38+
try {
39+
await navigator.clipboard.writeText(props.copyData)
40+
41+
setIntentWithTimeout('success')
42+
} catch (error) {
43+
console.error('Failed to copy data to the clipboard.', error)
44+
45+
setIntentWithTimeout('danger')
46+
}
47+
}
48+
49+
return (
50+
<button
51+
class={styles.button}
52+
classList={{
53+
[styles.danger!]: intent() === 'danger',
54+
[styles.success!]: intent() === 'success',
55+
}}
56+
onClick={props.copyData ? handleCopyData : props.onClick}
57+
>
58+
{props.children}
59+
</button>
60+
)
61+
}
62+
63+
interface DataButtonProps {
64+
copyData?: string
65+
onClick?: JSX.EventHandler<HTMLButtonElement, MouseEvent>
66+
}
67+
68+
type DataButtonIntent = 'danger' | 'success'

packages/astro-content-devtools/src/components/data/DataSection.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@
1212
gap: var(--acd-size-2);
1313
padding: var(--acd-size-2);
1414
}
15+
16+
.content.horizontal {
17+
flex-direction: row;
18+
}

packages/astro-content-devtools/src/components/data/DataSection.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,14 @@ export const DataSection: ParentComponent<DataSectionProps> = (props) => {
66
return (
77
<section>
88
<h2 class={styles.title}>{props.title}</h2>
9-
<div class={styles.content}>{props.children}</div>
9+
<div class={styles.content} classList={{ [styles.horizontal!]: props.horizontal }}>
10+
{props.children}
11+
</div>
1012
</section>
1113
)
1214
}
1315

1416
interface DataSectionProps {
17+
horizontal?: boolean
1518
title: string
1619
}

packages/astro-content-devtools/src/components/panels/DataPanel.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createResource, type Component, Show } from 'solid-js'
22

33
import { useSelection } from '../../hooks/useSelection'
44
import { fetchCollectionEntry } from '../../libs/content'
5+
import { DataButton } from '../data/DataButton'
56
import { DataContent } from '../data/DataContent'
67
import { DataFrontmatter } from '../data/DataFrontmatter'
78
import { DataSection } from '../data/DataSection'
@@ -29,13 +30,13 @@ export const DataPanel: Component = () => {
2930
<Panel style={{ 'background-color': 'var(--acd-color-gray-700)' }}>
3031
<Show when={entry() !== undefined} fallback="// TODO(HiDeoo) pelase select a valid entry">
3132
<DataSection title="Details">
32-
<DataVariable key="Id" value={entry()?.id} />
33+
<DataVariable key="ID" value={entry()?.id} />
3334
<DataVariable key="Slug" value={entry()?.slug} />
3435
</DataSection>
35-
<DataSection title="Actions">
36-
<div>{'// TODO(HiDeoo)'}</div>
37-
<div>{'// TODO(HiDeoo)'}</div>
38-
<div>{'// TODO(HiDeoo)'}</div>
36+
<DataSection title="Actions" horizontal>
37+
<DataButton copyData={entry()?.id}>Copy ID</DataButton>
38+
<DataButton copyData={JSON.stringify(entry()?.data, null, 2)}>Copy Frontmatter</DataButton>
39+
<DataButton copyData={entry()?.body}>Copy Content</DataButton>
3940
</DataSection>
4041
<DataSection title="Frontmatter">
4142
<DataFrontmatter frontmatter={entry()?.data} />

packages/astro-content-devtools/src/styles/theme.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,23 @@
99
--acd-color-gray-100: #bfc1c9;
1010

1111
--acd-color-blue-700: #344ef9;
12+
--acd-color-blue-600: #2563eb;
13+
--acd-color-blue-500: #3b83f6;
14+
1215
--acd-color-cyan-400: #3fbad9;
1316

17+
--acd-color-emerald-600: #059669;
18+
19+
--acd-color-rose-600: #e11d48;
20+
1421
--acd-text-xs: 0.625rem;
1522
--acd-text-sm: 0.75rem;
1623
--acd-text-base: 0.875rem;
1724

1825
--acd-font-normal: 400;
1926
--acd-font-medium: 500;
2027

28+
--acd-size-1: 0.25rem;
2129
--acd-size-1-5: 0.375rem;
2230
--acd-size-2: 0.5rem;
2331
--acd-size-3-5: 0.875rem;

0 commit comments

Comments
 (0)