Skip to content

Commit 886969f

Browse files
jeddy3ybiquitous
andauthored
Add toolbar with report-bug and copy-link actions (#510)
Co-authored-by: Masafumi Koba <[email protected]>
1 parent 3ac1ad7 commit 886969f

File tree

6 files changed

+143
-8
lines changed

6 files changed

+143
-8
lines changed

src/components/copy-link.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
export type CopyLinkOptions = {
2+
element: HTMLElement;
3+
};
4+
5+
export function setupCopyLink({ element }: CopyLinkOptions) {
6+
const button = document.createElement('button');
7+
const buttonText = 'Copy link';
8+
9+
button.textContent = buttonText;
10+
button.addEventListener('click', copy);
11+
element.replaceChildren(button);
12+
13+
async function copy() {
14+
try {
15+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
16+
await navigator.clipboard.writeText(window.location.href);
17+
18+
button.textContent = 'Copied!';
19+
setTimeout(() => {
20+
button.textContent = buttonText;
21+
}, 2000);
22+
} catch {
23+
// ignore
24+
}
25+
}
26+
}

src/components/deps-editor.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,12 @@ export async function setupDepsEditor({ element, listeners, init }: DepsEditorOp
3030
useDiffEditor: false,
3131
});
3232

33+
let installedPackages: PackageJsonData[] = [];
34+
3335
return {
3436
...monacoEditor,
3537
setPackages(packages: PackageJsonData[]) {
38+
installedPackages = packages;
3639
versionsPanel.innerHTML = '';
3740

3841
for (const pkg of packages) {
@@ -47,5 +50,8 @@ export async function setupDepsEditor({ element, listeners, init }: DepsEditorOp
4750
versionsPanel.appendChild(li);
4851
}
4952
},
53+
getPackages() {
54+
return installedPackages;
55+
},
5056
};
5157
}

src/components/report.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
export type ReportOptions = {
2+
element: HTMLElement;
3+
getData: () => ReportData;
4+
};
5+
6+
export type ReportData = {
7+
config: string;
8+
configFormat: string;
9+
code: string;
10+
fileName: string;
11+
packages: { name: string; version: string }[];
12+
};
13+
14+
export function setupReport({ element, getData }: ReportOptions) {
15+
const link = element.querySelector('a');
16+
17+
if (link) {
18+
link.addEventListener('click', () => {
19+
const data = getData();
20+
const url = new URL(link.href);
21+
22+
const codeLang = data.fileName.split('.').pop() ?? 'css';
23+
const configLang = data.configFormat.split('.').pop() ?? 'mjs';
24+
25+
url.searchParams.set('reproduce-bug', `\`\`\`${codeLang}\n${data.code}\n\`\`\``);
26+
url.searchParams.set(
27+
'stylelint-configuration',
28+
`\`\`\`${configLang}\n${data.config}\n\`\`\``,
29+
);
30+
url.searchParams.set('stylelint-run', `[Demo](${window.location.href})`);
31+
url.searchParams.set(
32+
'stylelint-version',
33+
`\`\`\`json\n${JSON.stringify(
34+
data.packages.reduce((acc, pkg) => ({ ...acc, [pkg.name]: pkg.version }), {}),
35+
null,
36+
2,
37+
)}\n\`\`\``,
38+
);
39+
link.href = url.toString();
40+
});
41+
}
42+
}

src/demo.css

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
--sd-black-a-8: oklch(from var(--sd-black) l c h / 8%);
55
--sd-gray-20: oklch(20% 0 0deg);
66
--sd-gray-25: oklch(25% 0 0deg);
7+
--sd-gray-35: oklch(35% 0 0deg);
78
--sd-gray-60: oklch(60% 0 0deg);
89
--sd-gray-90: oklch(90% 0 0deg);
910
--sd-gray-98: oklch(98% 0 0deg);
@@ -90,6 +91,27 @@
9091
border-radius: 2px;
9192
color: light-dark(initial, var(--sd-white));
9293
}
94+
95+
button {
96+
background: light-dark(var(--sd-white), var(--sd-gray-35));
97+
border: 1px solid light-dark(var(--sd-gray-90), var(--sd-gray-60));
98+
border-radius: 4px;
99+
box-shadow: 0 2px 2px 0 light-dark(var(--sd-black-a-8), var(--sd-white-a-8));
100+
color: light-dark(var(--sd-gray-60), var(--sd-gray-90));
101+
cursor: pointer;
102+
font-family: inherit;
103+
padding-block: 0.125rem;
104+
padding-inline: 0.5rem;
105+
106+
&:active {
107+
box-shadow: none;
108+
transform: translateY(1px);
109+
}
110+
111+
&:hover {
112+
color: light-dark(var(--sd-black), var(--sd-white));
113+
}
114+
}
93115
}
94116

95117
@layer custom-elements {
@@ -101,7 +123,8 @@
101123
'input-tabs' min-content
102124
'inputs' 1fr
103125
'output-tabs' min-content
104-
'outputs' max(10rem, 33vb);
126+
'outputs' max(10rem, 33vb)
127+
'toolbar' min-content;
105128

106129
&:not(:has(input[data-radio-name='code']:checked)) sd-code,
107130
&:not(:has(input[data-radio-name='config']:checked)) sd-config,
@@ -205,6 +228,7 @@
205228

206229
sd-outputs {
207230
display: block;
231+
grid-area: outputs;
208232
}
209233

210234
sd-xterm-wrapper {
@@ -282,4 +306,15 @@
282306
}
283307
}
284308
}
309+
310+
sd-toolbar {
311+
background-color: light-dark(var(--sd-gray-98), var(--sd-gray-25));
312+
display: flex;
313+
gap: 1rem;
314+
grid-area: toolbar;
315+
padding-block: 0.5rem;
316+
padding-inline: 1rem;
317+
place-content: end;
318+
place-items: center;
319+
}
285320
}

src/demo.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,14 @@
4141
<sd-warnings></sd-warnings>
4242
<sd-console><sd-xterm-wrapper></sd-xterm-wrapper></sd-console>
4343
</sd-outputs>
44+
<sd-toolbar>
45+
<sd-report>
46+
<a
47+
href="https://github.com/stylelint/stylelint/issues/new?template=REPORT_A_BUG.yml"
48+
target="_blank"
49+
>Report a bug</a
50+
>
51+
</sd-report>
52+
<sd-copy-link></sd-copy-link>
53+
</sd-toolbar>
4454
</stylelint-demo>

src/demo.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import { loadMonaco } from './monaco-editor';
1010
import { setupCodeEditor } from './components/code-editor';
1111
import { setupConfigEditor } from './components/config-editor';
1212
import { setupConsoleOutput } from './components/console';
13+
import { setupCopyLink } from './components/copy-link';
1314
import { setupDepsEditor } from './components/deps-editor';
1415
import { setupLintServer } from './linter-service';
16+
import { setupReport } from './components/report';
1517
import { setupTabs } from './components/output-tabs';
1618
import { setupWarningsPanel } from './components/warnings';
1719
import type stylelint from 'stylelint';
@@ -104,7 +106,7 @@ export async function mount({ element, init, listeners }: MountOptions) {
104106
config: value,
105107
});
106108
}),
107-
onChangeFormat: debounce((format) => {
109+
onChangeFormat: debounce(async (format) => {
108110
onChangeValues({
109111
configFormat: format,
110112
});
@@ -120,11 +122,7 @@ export async function mount({ element, init, listeners }: MountOptions) {
120122
return;
121123
}
122124

123-
listeners?.onChange?.({
124-
code: codeEditor.getLeftValue(),
125-
fileName: codeEditor.getFileName(),
126-
config: configEditor.getValue(),
127-
configFormat: configEditor.getFormat(),
125+
onChangeValues({
128126
deps: value,
129127
});
130128

@@ -146,6 +144,21 @@ export async function mount({ element, init, listeners }: MountOptions) {
146144
loadMonaco(),
147145
]);
148146

147+
setupReport({
148+
element: element.querySelector<HTMLElement>('sd-report')!,
149+
getData: () => ({
150+
config: configEditor.getValue(),
151+
configFormat: configEditor.getFormat(),
152+
code: codeEditor.getLeftValue(),
153+
fileName: codeEditor.getFileName(),
154+
packages: depsEditor.getPackages(),
155+
}),
156+
});
157+
158+
setupCopyLink({
159+
element: element.querySelector<HTMLElement>('sd-copy-link')!,
160+
});
161+
149162
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
150163
const updateTheme = () => {
151164
monaco.editor.setTheme(mediaQuery.matches ? 'vs-dark' : 'vs');
@@ -226,19 +239,22 @@ export async function mount({ element, init, listeners }: MountOptions) {
226239
fileName = codeEditor.getFileName(),
227240
config = configEditor.getValue(),
228241
configFormat = configEditor.getFormat(),
242+
deps = depsEditor.getValue(),
229243
}: {
230244
code?: string;
231245
fileName?: string;
232246
config?: string;
233247
configFormat?: ConfigFormat;
248+
deps?: string;
234249
}) {
235250
listeners?.onChange?.({
236251
code,
237252
fileName,
238253
config,
239254
configFormat,
240-
deps: depsEditor.getValue(),
255+
deps,
241256
});
257+
242258
lint({
243259
code,
244260
fileName,

0 commit comments

Comments
 (0)