Skip to content

Commit ebba58e

Browse files
committed
style(yasgui): compact Build query bar with icon actions
One row at the top of the Build query view: All checkbox with a ticked count on the left, Replace block and icon buttons for insert at cursor and new tab on the right.
1 parent abb5013 commit ebba58e

6 files changed

Lines changed: 112 additions & 38 deletions

File tree

‎.changeset/sparql-anything-builder-tweaks.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"@matdata/yasgui": minor
33
---
44

5-
SPARQL Anything source inspector: **Load structure** on the chip (and in the Ctrl+K search) now opens the Source view of the side panel. The **Build query** view has its buttons at the top, and an **All values** checkbox to tick every value of the source at once.
5+
SPARQL Anything source inspector: **Load structure** on the chip (and in the Ctrl+K search) now opens the Source view of the side panel. The **Build query** view has one compact bar at the top with an **All** checkbox to tick every value of the source at once, **Replace block**, and icon buttons to insert at the cursor or open in a new tab.

‎docs/user-guide.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1084,7 +1084,7 @@ The structure is remembered per endpoint and set of options for a day; the reloa
10841084

10851085
- **Structure**: the tree of the file as SPARQL Anything sees it: the document (`fx:root`), its rows, array items or elements, and their properties, each with a count and an example value. Click a property or element to insert it at the cursor.
10861086
- **Preview**: the first 100 triples of the converted file, in Turtle, fetched with a `CONSTRUCT ... LIMIT 100` query. **Open as query** puts that query in a new tab.
1087-
- **Build query**: tick the values you need, or **All values** at the top to tick them all (click it again to clear the selection). MatGUI writes the patterns from the root to each value: `fx:anySlot` for rows and array items, `a xyz:<element>` for XML elements, and `OPTIONAL` for values you mark as optional (the first value you tick is required, later ones optional; click the label to switch). The buttons stay at the top of the view: **Replace block** puts the patterns in the SERVICE block (keeping its `fx:properties` options; `Ctrl+Z` undoes it), **Insert at cursor** inserts them, and **New tab** opens a complete SELECT query.
1087+
- **Build query**: tick the values you need, or **All** at the top to tick them all (click it again to clear the selection; next to it you see how many are ticked). MatGUI writes the patterns from the root to each value: `fx:anySlot` for rows and array items, `a xyz:<element>` for XML elements, and `OPTIONAL` for values you mark as optional (the first value you tick is required, later ones optional; click the label to switch). The bar with **All** stays at the top of the view and has the actions: **Replace block** puts the patterns in the SERVICE block (keeping its `fx:properties` options; `Ctrl+Z` undoes it), the text cursor button inserts them at the cursor, and the arrow button opens a complete SELECT query in a new tab.
10881088

10891089
CSV, JSON and XML sources are shown best; other formats SPARQL Anything supports work too, with a more generic tree. A site can switch all of this off with `sparqlAnything: false`. To try it, the repository has a Docker Compose setup with a SPARQL Anything server and sample files in `dev/sparql-anything`.
10901090

‎packages/yasgui/src/icons.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ const paths = {
5454
'<path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M4.268 21a2 2 0 0 0 1.727 1H18a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v3"/><path d="m9 18-1.5-1.5"/><circle cx="5" cy="14" r="3"/>',
5555
refresh:
5656
'<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M8 16H3v5"/>',
57+
externalLink:
58+
'<path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>',
59+
textCursor:
60+
'<path d="M17 22h-1a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h1"/><path d="M7 22h1a4 4 0 0 0 4-4v-1"/><path d="M7 2h1a4 4 0 0 1 4 4v1"/>',
5761
cornerDownLeft: '<path d="M20 4v7a4 4 0 0 1-4 4H4"/><path d="m9 10-5 5 5 5"/>',
5862
code: '<path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/>',
5963
command: '<path d="M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3"/>',

‎packages/yasgui/src/sparqlAnything/SourceView.ts‎

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,8 @@ export default class SourceView {
498498
const pickedValues: PickedValue[] = picks.map((p) => ({ node: nodesByKey.get(p.key)!, optional: p.optional }));
499499
const built = pickedValues.length ? buildPatterns(pickedValues, source, queryPrefixes) : undefined;
500500

501-
// The actions stay on top, so they're in reach without scrolling past a long tree
502-
const toolbar = el("div", "yasgui-sa__actions top");
501+
// One bar on top with the selection and the actions, so they're in reach without scrolling past a long tree
502+
const toolbar = el("div", "yasgui-sa__buildbar");
503503
const allLabel = el("label", "yasgui-sa__all");
504504
const all = el("input", "yasgui-sa__check");
505505
all.type = "checkbox";
@@ -515,15 +515,20 @@ export default class SourceView {
515515
}
516516
setPicks(next);
517517
});
518-
allLabel.title = "Select all values, or clear the selection";
519-
allLabel.append(all, el("span", undefined, picks.length ? `${picks.length} of ${values.length}` : "All values"));
518+
allLabel.title = picks.length ? "Clear the selection" : "Select all values";
519+
allLabel.append(
520+
all,
521+
el("span", undefined, "All"),
522+
el("span", "yasgui-sa__allCount", `${picks.length}/${values.length}`),
523+
);
520524
toolbar.append(allLabel);
521525

522-
const replace = button(
523-
"Replace block",
524-
"yasgui-sa__button primary",
525-
"Replace the content of the SERVICE block (Ctrl+Z undoes it)",
526-
);
526+
const replace = iconButton("braces", "Replace block", {
527+
className: "yasgui-sa__buildButton primary",
528+
showLabel: true,
529+
title: "Replace the content of the SERVICE block (Ctrl+Z undoes it)",
530+
size: 14,
531+
});
527532
replace.addEventListener("click", () => {
528533
if (!yasqe || !built) return;
529534
const current = yasqe.getSparqlAnythingSources().find((s) => s.key === source.key);
@@ -550,19 +555,29 @@ export default class SourceView {
550555
yasqe.addPrefixes(used);
551556
yasqe.focus();
552557
});
553-
const insert = button("Insert at cursor", "yasgui-sa__button", "Insert the patterns at the cursor");
558+
const insert = iconButton("textCursor", "Insert at cursor", {
559+
className: "yasgui-sa__buildButton",
560+
title: "Insert the patterns at the cursor",
561+
size: 15,
562+
});
554563
insert.addEventListener("click", () => {
555564
if (!yasqe || !built) return;
556565
yasqe.replaceSelection(built.lines.join("\n"));
557566
yasqe.addPrefixes(built.prefixes);
558567
yasqe.focus();
559568
});
560-
const newTab = button("New tab", "yasgui-sa__button", "Open a SELECT query for these values in a new tab");
569+
const newTab = iconButton("externalLink", "New tab", {
570+
className: "yasgui-sa__buildButton",
571+
title: "Open a SELECT query for these values in a new tab",
572+
size: 15,
573+
});
561574
newTab.addEventListener("click", () => {
562575
if (built) this.openInNewTab(tab, buildQuery(pickedValues, source, queryPrefixes));
563576
});
564577
for (const action of [replace, insert, newTab]) action.disabled = !built;
565-
toolbar.append(replace, insert, newTab);
578+
const actions = el("div", "yasgui-sa__buildActions");
579+
actions.append(replace, insert, newTab);
580+
toolbar.append(actions);
566581
wrapper.append(toolbar);
567582
wrapper.append(el("p", "yasgui-sa__hint", "Tick the values you need. Rows and array items become fx:anySlot."));
568583

‎packages/yasgui/src/sparqlAnything/SparqlAnything.scss‎

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -488,39 +488,94 @@
488488
color: var(--yasgui-ink-muted, #56606c);
489489
}
490490

491-
&__actions {
491+
// The query builder's bar: selection on the left, actions on the right; stays at the top of the scrolling body
492+
&__buildbar {
493+
position: sticky;
494+
z-index: 1;
495+
top: -8px;
492496
display: flex;
493497
flex-wrap: wrap;
498+
align-items: center;
499+
justify-content: space-between;
494500
gap: 6px;
495-
margin-top: 10px;
496-
497-
// The query builder's actions stay at the top of the scrolling body
498-
&.top {
499-
position: sticky;
500-
z-index: 1;
501-
top: -8px;
502-
align-items: center;
503-
margin: -8px -6px 8px;
504-
padding: 8px 6px;
505-
border-bottom: 1px solid var(--yasgui-line, #dfe5ec);
506-
background: var(--yasgui-surface-raised, #fff);
507-
}
501+
margin: -8px -6px 6px;
502+
padding: 6px 4px 6px 10px;
503+
border-bottom: 1px solid var(--yasgui-line, #dfe5ec);
504+
background: var(--yasgui-surface-raised, #fff);
508505
}
509506

510-
// Below the action buttons, on its own line
511507
&__all {
512508
display: flex;
513-
flex-basis: 100%;
514-
order: 1;
515509
align-items: center;
516510
gap: 6px;
517-
padding: 2px 0 0 4px;
518511
color: var(--yasgui-ink-body, #3a4552);
519512
font-size: 12px;
520513
cursor: pointer;
521514
user-select: none;
522515
}
523516

517+
&__allCount {
518+
padding: 0 6px;
519+
border-radius: 999px;
520+
background: var(--yasgui-surface-sunken, #f2f5f8);
521+
color: var(--yasgui-ink-subtle, #6a7380);
522+
font-size: 11px;
523+
font-variant-numeric: tabular-nums;
524+
line-height: 18px;
525+
}
526+
527+
&__buildActions {
528+
display: flex;
529+
align-items: center;
530+
gap: 2px;
531+
margin-left: auto;
532+
}
533+
534+
&__buildButton {
535+
display: inline-flex;
536+
align-items: center;
537+
justify-content: center;
538+
gap: 5px;
539+
min-width: 28px;
540+
height: 28px;
541+
padding: 0 6px;
542+
border: 0;
543+
border-radius: var(--yasgui-radius-sm, 6px);
544+
background: transparent;
545+
color: var(--yasgui-ink-muted, #56606c);
546+
font: inherit;
547+
font-size: 12px;
548+
font-weight: 500;
549+
white-space: nowrap;
550+
cursor: pointer;
551+
552+
.yasgui-btnLabel {
553+
font-size: 12px;
554+
}
555+
556+
&:hover {
557+
background: var(--yasgui-surface-sunken, #f2f5f8);
558+
color: var(--yasgui-ink, #1c2633);
559+
}
560+
561+
&.primary {
562+
margin-right: 2px;
563+
padding: 0 9px 0 7px;
564+
background: var(--yasgui-brand-fill, #0563bb);
565+
color: var(--yasgui-on-brand, #fff);
566+
567+
&:hover {
568+
background: var(--yasgui-brand-fill-strong, #044a8c);
569+
}
570+
}
571+
572+
&:disabled {
573+
opacity: 0.4;
574+
cursor: default;
575+
pointer-events: none;
576+
}
577+
}
578+
524579
&__footer {
525580
display: flex;
526581
flex-wrap: wrap;

‎test/sparql-anything-browser.ts‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,12 @@ describe("SPARQL Anything", function () {
171171
// The actions are above the tree and wait for a selection
172172
expect(
173173
await page.$eval(".yasgui-sa__section", (section) => {
174-
const actions = section.querySelector(".yasgui-sa__actions")!;
174+
const actions = section.querySelector(".yasgui-sa__buildbar")!;
175175
const tree = section.querySelector(".yasgui-sa__tree")!;
176176
return !!(actions.compareDocumentPosition(tree) & Node.DOCUMENT_POSITION_FOLLOWING);
177177
}),
178178
).to.equal(true);
179-
expect(await page.$eval(".yasgui-sa__actions .primary", (b) => (b as HTMLButtonElement).disabled)).to.equal(true);
179+
expect(await page.$eval(".yasgui-sa__buildbar .primary", (b) => (b as HTMLButtonElement).disabled)).to.equal(true);
180180

181181
// The view redraws often, so click the current checkbox rather than a handle to it
182182
await page.evaluate(() => document.querySelector<HTMLInputElement>(".yasgui-sa__all input")?.click());
@@ -185,8 +185,8 @@ describe("SPARQL Anything", function () {
185185
);
186186
expect(checked.length).to.be.greaterThan(1);
187187
expect(checked.every(Boolean)).to.equal(true);
188-
expect(await page.$eval(".yasgui-sa__all", (el) => el.textContent)).to.equal(
189-
`${checked.length} of ${checked.length}`,
188+
expect(await page.$eval(".yasgui-sa__allCount", (el) => el.textContent)).to.equal(
189+
`${checked.length}/${checked.length}`,
190190
);
191191
const code = await page.$eval(".yasgui-sa__code", (el) => el.textContent || "");
192192
expect(code).to.contain("xyz:email");
@@ -249,8 +249,8 @@ describe("SPARQL Anything", function () {
249249
);
250250
row?.querySelector<HTMLInputElement>(".yasgui-sa__check")?.click();
251251
});
252-
await page.waitForSelector(".yasgui-sa__actions .primary", { visible: true });
253-
await page.click(".yasgui-sa__actions .primary");
252+
await page.waitForSelector(".yasgui-sa__buildbar .primary", { visible: true });
253+
await page.click(".yasgui-sa__buildbar .primary");
254254
const value = await page.evaluate(() => (window as any).yasgui.getTab().getYasqe().getValue());
255255
expect(value).to.contain("?root fx:anySlot ?row .");
256256
expect(value).to.contain("?row xyz:email ?email .");

0 commit comments

Comments
 (0)