diff --git a/docs/developer-guide.md b/docs/developer-guide.md
index 11e749c2..1dd67dc1 100644
--- a/docs/developer-guide.md
+++ b/docs/developer-guide.md
@@ -201,14 +201,11 @@ YASGUI is built as a monorepo with four main packages, each serving a specific p
- DOM manipulation utilities
- Local storage abstraction
- Common helper functions
-- SVG icon rendering
- DOMPurify integration for XSS protection
**Exports:**
- `Storage`: localStorage abstraction with namespacing
- `addClass`, `removeClass`, `hasClass`: DOM class utilities
-- `drawSvgStringAsElement`: SVG helper
-- `drawFontAwesomeIconAsSvg`: Icon rendering
#### @matdata/yasqe (SPARQL Query Editor)
diff --git a/package-lock.json b/package-lock.json
index 2818d9db..ef482010 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
"packages/*"
],
"dependencies": {
+ "@fortawesome/fontawesome-free": "^7.1.0",
"@matdata/yasgui-graph-plugin": "^1.4.1",
"@matdata/yasgui-table-plugin": "^1.2.0",
"@typescript-eslint/eslint-plugin": "^6.13.2",
@@ -1095,23 +1096,30 @@
}
},
"node_modules/@fortawesome/fontawesome-common-types": {
- "version": "0.2.36",
- "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
- "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
- "hasInstallScript": true,
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.1.0.tgz",
+ "integrity": "sha512-l/BQM7fYntsCI//du+6sEnHOP6a74UixFyOYUyz2DLMXKx+6DEhfR3F2NYGE45XH1JJuIamacb4IZs9S0ZOWLA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
+ "node_modules/@fortawesome/fontawesome-free": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.1.0.tgz",
+ "integrity": "sha512-+WxNld5ZCJHvPQCr/GnzCTVREyStrAJjisUPtUxG5ngDA8TMlPnKp6dddlTpai4+1GNmltAeuk1hJEkBohwZYA==",
+ "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@fortawesome/free-solid-svg-icons": {
- "version": "5.15.4",
- "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz",
- "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==",
- "hasInstallScript": true,
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.1.0.tgz",
+ "integrity": "sha512-Udu3K7SzAo9N013qt7qmm22/wo2hADdheXtBfxFTecp+ogsc0caQNRKEb7pkvvagUGOpG9wJC1ViH6WXs8oXIA==",
"license": "(CC-BY-4.0 AND MIT)",
"dependencies": {
- "@fortawesome/fontawesome-common-types": "^0.2.36"
+ "@fortawesome/fontawesome-common-types": "7.1.0"
},
"engines": {
"node": ">=6"
@@ -8974,7 +8982,7 @@
"version": "4.6.1",
"license": "MIT",
"dependencies": {
- "@fortawesome/free-solid-svg-icons": "^5.14.0",
+ "@fortawesome/free-solid-svg-icons": "^7.1.0",
"@json2csv/plainjs": "^7.0.4",
"@matdata/yasgui-utils": "^4.6.1",
"@matdata/yasqe": "^4.6.1",
diff --git a/package.json b/package.json
index 3571fea1..dd52f919 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
]
},
"dependencies": {
+ "@fortawesome/fontawesome-free": "^7.1.0",
"@matdata/yasgui-graph-plugin": "^1.4.1",
"@matdata/yasgui-table-plugin": "^1.2.0",
"@typescript-eslint/eslint-plugin": "^6.13.2",
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 013b5bd0..5afefcb1 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -3,36 +3,6 @@ export { default as Storage } from "./Storage";
const { sanitize } = DOMPurify;
-export function drawSvgStringAsElement(svgString: string) {
- if (svgString && svgString.trim().indexOf("`;
-}
-
export function hasClass(el: Element | undefined, className: string) {
if (!el) return;
if (el.classList) return el.classList.contains(className);
diff --git a/packages/yasgui/src/Tab.ts b/packages/yasgui/src/Tab.ts
index 90ef31ce..3b0116b7 100644
--- a/packages/yasgui/src/Tab.ts
+++ b/packages/yasgui/src/Tab.ts
@@ -18,24 +18,6 @@ import { getWorkspaceBackend } from "./queryManagement/backends/getWorkspaceBack
import { asWorkspaceBackendError } from "./queryManagement/backends/errors";
import { normalizeQueryFilename } from "./queryManagement/normalizeQueryFilename";
-// Layout orientation toggle icons
-const HORIZONTAL_LAYOUT_ICON = ``;
-
-const VERTICAL_LAYOUT_ICON = ``;
-
-// Overflow dropdown icon (three horizontal dots / ellipsis menu)
-const OVERFLOW_ICON = ``;
-
export interface PersistedJsonYasr extends YasrPersistentConfig {
responseSummary: Parser.ResponseSummary;
}
@@ -569,8 +551,11 @@ export class Tab extends EventEmitter {
if (!this.orientationToggleButton) return;
// Show the icon for the layout we'll switch TO (not the current layout)
+ // fa-columns for horizontal (side-by-side), fa-grip-lines for vertical (stacked)
this.orientationToggleButton.innerHTML =
- this.currentOrientation === "vertical" ? HORIZONTAL_LAYOUT_ICON : VERTICAL_LAYOUT_ICON;
+ this.currentOrientation === "vertical"
+ ? ''
+ : '';
this.orientationToggleButton.title =
this.currentOrientation === "vertical" ? "Switch to horizontal layout" : "Switch to vertical layout";
}
@@ -746,7 +731,7 @@ export class Tab extends EventEmitter {
if (!this.endpointOverflowButton) {
this.endpointOverflowButton = document.createElement("button");
addClass(this.endpointOverflowButton, "endpointOverflowBtn");
- this.endpointOverflowButton.innerHTML = OVERFLOW_ICON;
+ this.endpointOverflowButton.innerHTML = '';
this.endpointOverflowButton.title = "More endpoints";
this.endpointOverflowButton.setAttribute("aria-label", "More endpoint options");
this.endpointOverflowButton.setAttribute("aria-haspopup", "true");
@@ -1310,7 +1295,13 @@ export class Tab extends EventEmitter {
}
this.yasqe = new Yasqe(this.yasqeWrapperEl, yasqeConf);
- this.initSaveManagedQueryIcon();
+ // Hook up the save button to managed query save
+ this.yasqe.on("saveManagedQuery", () => {
+ void this.saveManagedQueryOrSaveAsManagedQuery();
+ });
+
+ // Show/hide save button based on workspace configuration
+ this.updateSaveButtonVisibility();
this.yasqe.on("blur", this.handleYasqeBlur);
this.yasqe.on("query", this.handleYasqeQuery);
@@ -1327,6 +1318,13 @@ export class Tab extends EventEmitter {
this.attachYasqeMouseHandler();
}
+ private updateSaveButtonVisibility() {
+ if (!this.yasqe) return;
+ const workspaces = this.yasgui.persistentConfig.getWorkspaces();
+ const hasWorkspaces = workspaces && workspaces.length > 0;
+ this.yasqe.setSaveButtonVisible(hasWorkspaces);
+ }
+
private initSaveManagedQueryIcon() {
if (!this.yasqe) return;
@@ -1345,10 +1343,7 @@ export class Tab extends EventEmitter {
saveBtn.className = "yasqe_saveManagedQueryButton";
saveBtn.title = "Save managed query";
saveBtn.setAttribute("aria-label", "Save managed query");
- saveBtn.innerHTML =
- '";
+ saveBtn.innerHTML = '';
saveBtn.addEventListener("click", (e) => {
e.preventDefault();
diff --git a/packages/yasgui/src/TabElements.scss b/packages/yasgui/src/TabElements.scss
index 10f9bfab..6bdc8080 100644
--- a/packages/yasgui/src/TabElements.scss
+++ b/packages/yasgui/src/TabElements.scss
@@ -25,13 +25,11 @@ $minTabHeight: 35px;
&:hover,
&:focus-visible {
color: var(--yasgui-button-hover, #000);
- fill: var(--yasgui-button-hover, #000);
background: var(--yasgui-bg-secondary, #f7f7f7);
}
- svg {
- width: 20px;
- height: 20px;
+ i {
+ font-size: 20px;
}
}
@@ -60,7 +58,6 @@ $minTabHeight: 35px;
background: transparent;
border: none;
color: var(--yasgui-button-text, #505050);
- fill: var(--yasgui-button-text, #505050);
display: flex;
align-items: center;
justify-content: center;
@@ -68,14 +65,8 @@ $minTabHeight: 35px;
&:hover {
color: var(--yasgui-button-hover, #000);
- fill: var(--yasgui-button-hover, #000);
background: var(--yasgui-bg-secondary, #f7f7f7);
}
-
- svg {
- width: 20px;
- height: 20px;
- }
}
.addTab {
diff --git a/packages/yasgui/src/TabElements.ts b/packages/yasgui/src/TabElements.ts
index a9a380fe..afe3dabd 100644
--- a/packages/yasgui/src/TabElements.ts
+++ b/packages/yasgui/src/TabElements.ts
@@ -4,14 +4,6 @@ import { hasClass, addClass, removeClass } from "@matdata/yasgui-utils";
import sortablejs from "sortablejs";
import "./TabElements.scss";
-// Theme toggle icons
-const MOON_ICON = ``;
-
-const SUN_ICON = ``;
export interface TabList {}
export class TabListEl {
private tabList: TabList;
@@ -323,7 +315,7 @@ export class TabList {
queryBrowserButton.className = "queryBrowserToggle";
queryBrowserButton.setAttribute("aria-label", "Open query browser");
queryBrowserButton.title = "Open query browser";
- queryBrowserButton.innerHTML = ``;
+ queryBrowserButton.innerHTML = '';
queryBrowserButton.addEventListener("click", () => {
this.yasgui.queryBrowser.toggle(queryBrowserButton);
});
diff --git a/packages/yasgui/src/TabSettingsModal.scss b/packages/yasgui/src/TabSettingsModal.scss
index 112dca2f..f3391f93 100644
--- a/packages/yasgui/src/TabSettingsModal.scss
+++ b/packages/yasgui/src/TabSettingsModal.scss
@@ -9,11 +9,6 @@
align-items: center;
justify-content: center;
- svg {
- width: 20px;
- height: 20px;
- }
-
&:hover {
opacity: 0.7;
}
diff --git a/packages/yasgui/src/TabSettingsModal.ts b/packages/yasgui/src/TabSettingsModal.ts
index b6388ce2..01901126 100644
--- a/packages/yasgui/src/TabSettingsModal.ts
+++ b/packages/yasgui/src/TabSettingsModal.ts
@@ -7,19 +7,6 @@ import * as OAuth2Utils from "./OAuth2Utils";
import PersistentConfig from "./PersistentConfig";
import { WorkspaceSettingsForm } from "./queryManagement/WorkspaceSettingsForm";
-// Theme toggle icons
-const MOON_ICON = ``;
-
-const SUN_ICON = ``;
-
-const SETTINGS_ICON = ``;
-
const AcceptOptionsMap: { key: string; value: string }[] = [
{ key: "JSON", value: "application/sparql-results+json" },
{ key: "XML", value: "application/sparql-results+xml" },
@@ -67,7 +54,7 @@ export default class TabSettingsModal {
addClass(this.settingsButton, "tabContextButton");
this.settingsButton.setAttribute("aria-label", "Settings");
this.settingsButton.title = "Settings";
- this.settingsButton.innerHTML = SETTINGS_ICON;
+ this.settingsButton.innerHTML = '';
this.settingsButton.onclick = () => this.open();
controlBarEl.appendChild(this.settingsButton);
@@ -89,9 +76,7 @@ export default class TabSettingsModal {
this.prefixButton = document.createElement("button");
this.prefixButton.setAttribute("aria-label", "Insert Prefixes");
this.prefixButton.title = "Insert saved prefixes into query";
- this.prefixButton.innerHTML = ``;
+ this.prefixButton.innerHTML = '';
addClass(this.prefixButton, "tabContextButton", "prefixButton");
controlBarEl.appendChild(this.prefixButton);
this.prefixButton.onclick = () => this.insertPrefixesIntoQuery();
@@ -1526,9 +1511,9 @@ export default class TabSettingsModal {
private getThemeToggleIcon(): string {
const currentTheme = this.tab.yasgui.getTheme();
- // In dark mode, show moon icon (clicking will switch to light)
- // In light mode, show sun icon (clicking will switch to dark)
- return currentTheme === "dark" ? MOON_ICON : SUN_ICON;
+ // In dark mode, show lightbulb icon (clicking will switch to light)
+ // In light mode, show moon icon (clicking will switch to dark)
+ return currentTheme === "dark" ? '' : '';
}
private drawImportExportSettings(container: HTMLElement) {
diff --git a/packages/yasgui/src/endpointSelect.scss b/packages/yasgui/src/endpointSelect.scss
index 247c3f4a..5a44188a 100644
--- a/packages/yasgui/src/endpointSelect.scss
+++ b/packages/yasgui/src/endpointSelect.scss
@@ -186,9 +186,8 @@
flex-shrink: 0;
transition: all 0.2s ease;
- svg {
- width: 16px;
- height: 16px;
+ i {
+ font-size: 16px;
}
&:hover {
diff --git a/packages/yasgui/src/index.scss b/packages/yasgui/src/index.scss
index 34d24f26..cd13bc33 100644
--- a/packages/yasgui/src/index.scss
+++ b/packages/yasgui/src/index.scss
@@ -1,3 +1,7 @@
+// Font Awesome icons
+@import "@fortawesome/fontawesome-free/css/fontawesome.css";
+@import "@fortawesome/fontawesome-free/css/solid.css";
+
// Main YASGUI container - fills 100% of parent element
// Parent element (typically body) should have height: 100%; width: 100%
.yasgui {
diff --git a/packages/yasgui/src/queryManagement/QueryBrowser.scss b/packages/yasgui/src/queryManagement/QueryBrowser.scss
index 5486a1b7..34f85fec 100644
--- a/packages/yasgui/src/queryManagement/QueryBrowser.scss
+++ b/packages/yasgui/src/queryManagement/QueryBrowser.scss
@@ -84,9 +84,8 @@
align-items: center;
justify-content: center;
- svg {
- width: 20px;
- height: 20px;
+ i {
+ font-size: 20px;
}
&:hover {
@@ -347,14 +346,11 @@
}
&__empty-icon {
- width: 64px;
- height: 64px;
color: var(--yasgui-text-secondary, #999);
opacity: 0.6;
- svg {
- width: 100%;
- height: 100%;
+ i {
+ font-size: 64px;
}
}
diff --git a/packages/yasgui/src/queryManagement/QueryBrowser.ts b/packages/yasgui/src/queryManagement/QueryBrowser.ts
index e923d8f7..c331110b 100644
--- a/packages/yasgui/src/queryManagement/QueryBrowser.ts
+++ b/packages/yasgui/src/queryManagement/QueryBrowser.ts
@@ -147,9 +147,7 @@ export default class QueryBrowser {
helpButton.type = "button";
addClass(helpButton, "yasgui-query-browser__help");
helpButton.setAttribute("aria-label", "Open documentation");
- helpButton.innerHTML = ``;
+ helpButton.innerHTML = '';
helpButton.addEventListener("click", () => {
window.open(
"https://yasgui-doc.matdata.eu/docs/user-guide#managed-queries-and-workspaces",
@@ -162,9 +160,7 @@ export default class QueryBrowser {
closeButton.type = "button";
addClass(closeButton, "yasgui-query-browser__close");
closeButton.setAttribute("aria-label", "Close query browser");
- closeButton.innerHTML = ``;
+ closeButton.innerHTML = '';
closeButton.addEventListener("click", () => this.close());
headerButtons.appendChild(helpButton);
@@ -350,9 +346,7 @@ export default class QueryBrowser {
const icon = document.createElement("div");
addClass(icon, "yasgui-query-browser__empty-icon");
- icon.innerHTML = ``;
+ icon.innerHTML = '';
const title = document.createElement("h3");
addClass(title, "yasgui-query-browser__empty-title");
diff --git a/packages/yasgui/src/tab.scss b/packages/yasgui/src/tab.scss
index ee8fd600..e530e8cb 100644
--- a/packages/yasgui/src/tab.scss
+++ b/packages/yasgui/src/tab.scss
@@ -140,35 +140,25 @@
padding: 6px;
cursor: pointer;
color: var(--yasgui-button-text, #505050);
- fill: var(--yasgui-button-text, #505050);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
- .svgImg {
- width: 15px;
- height: 15px;
- font-family: initial;
- }
-
- svg {
- width: 20px;
- height: 20px;
+ i {
+ font-size: 16px;
}
&:hover {
color: var(--yasgui-button-hover, black);
- fill: var(--yasgui-button-hover, black);
}
// Responsive spacing - reduce padding on smaller screens
@media (max-width: 768px) {
padding: 2px;
- svg {
- width: 18px;
- height: 18px;
+ i {
+ font-size: 18px;
}
}
}
diff --git a/packages/yasgui/src/themes.scss b/packages/yasgui/src/themes.scss
index b94b20f3..17cadb6f 100644
--- a/packages/yasgui/src/themes.scss
+++ b/packages/yasgui/src/themes.scss
@@ -27,7 +27,7 @@
--yasgui-input-border: rgba(0, 0, 0, 0.26);
--yasgui-input-focus: #337ab7;
--yasgui-label-color: rgba(0, 0, 0, 0.54);
- --yasgui-tooltip-bg: rgba(0, 0, 0, 0.8);
+ --yasgui-tooltip-bg: rgba(0, 0, 0, 1);
--yasgui-tooltip-text: #fff;
--yasgui-error-color: #d32f2f;
--yasgui-notification-bg: #eee;
@@ -77,7 +77,7 @@
--yasgui-input-border: rgba(255, 255, 255, 0.15);
--yasgui-input-focus: #4fc3f7;
--yasgui-label-color: rgba(255, 255, 255, 0.6);
- --yasgui-tooltip-bg: rgba(55, 55, 55, 0.95);
+ --yasgui-tooltip-bg: rgba(0, 0, 0, 1);
--yasgui-tooltip-text: #d4d4d4;
--yasgui-error-color: #f48771;
--yasgui-notification-bg: #2d2d30;
@@ -183,14 +183,8 @@
&:hover {
color: var(--yasgui-button-hover);
- fill: var(--yasgui-button-hover);
background: var(--yasgui-bg-secondary);
}
-
- svg {
- width: 20px;
- height: 20px;
- }
}
// Endpoint textbox theming
@@ -273,29 +267,25 @@
background-color: var(--yasgui-resize-handle);
}
- .parseErrorIcon svg g {
- fill: var(--yasgui-error-color);
- }
-
.cm-matchhighlight {
background-color: var(--yasgui-match-highlight-bg);
}
// Yasqe buttons theming (share, query, fullscreen)
.yasqe_buttons {
- svg {
- fill: var(--yasgui-button-text) !important;
+ i {
+ color: var(--yasgui-button-text) !important;
}
.yasqe_share {
- &:hover svg {
- fill: var(--yasgui-button-hover) !important;
+ &:hover i {
+ color: var(--yasgui-button-hover) !important;
}
}
.yasqe_fullscreenButton {
- &:hover svg {
- fill: var(--yasgui-accent-color) !important;
+ &:hover i {
+ color: var(--yasgui-accent-color) !important;
}
}
@@ -491,19 +481,16 @@ li.CodeMirror-hint-active {
color: var(--yasgui-accent-color) !important;
border-color: var(--yasgui-accent-color) !important;
- svg {
- fill: var(--yasgui-accent-color) !important;
+ i {
color: var(--yasgui-accent-color) !important;
}
&:hover {
color: var(--yasgui-link-hover) !important;
border-color: var(--yasgui-accent-color) !important;
- fill: var(--yasgui-link-hover) !important;
- svg {
+ i {
color: var(--yasgui-link-hover) !important;
- fill: var(--yasgui-link-hover) !important;
}
}
}
diff --git a/packages/yasqe/src/defaults.ts b/packages/yasqe/src/defaults.ts
index 14ff4988..c3aeaf57 100644
--- a/packages/yasqe/src/defaults.ts
+++ b/packages/yasqe/src/defaults.ts
@@ -87,6 +87,10 @@ SELECT * WHERE {
const yasqe: Yasqe = _yasqe;
yasqe.getInputField().blur();
},
+ F11: function (_yasqe: any) {
+ const yasqe: Yasqe = _yasqe;
+ yasqe.toggleFullscreen();
+ },
},
createShareableLink: function (yasqe: Yasqe) {
diff --git a/packages/yasqe/src/imgs.ts b/packages/yasqe/src/imgs.ts
deleted file mode 100644
index fc24e7c5..00000000
--- a/packages/yasqe/src/imgs.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-export var query =
- '';
-export var queryInvalid =
- '';
-export var download =
- '';
-export var share =
- '';
-export var warning =
- '';
-export var fullscreen =
- '';
-export var fullscreenExit =
- '';
-export var format =
- '';
-export var snippet =
- '';
-export var chevronDown =
- '';
diff --git a/packages/yasqe/src/index.ts b/packages/yasqe/src/index.ts
index faea7460..e30eff49 100644
--- a/packages/yasqe/src/index.ts
+++ b/packages/yasqe/src/index.ts
@@ -7,9 +7,9 @@ import * as sparql11Mode from "../grammar/tokenizer";
import { Storage as YStorage } from "@matdata/yasgui-utils";
import * as queryString from "query-string";
import tooltip from "./tooltip";
-import { drawSvgStringAsElement, addClass, removeClass } from "@matdata/yasgui-utils";
+import { addClass, removeClass } from "@matdata/yasgui-utils";
import * as Sparql from "./sparql";
-import * as imgs from "./imgs";
+
import * as Autocompleter from "./autocompleters";
import { merge, mergeWith, escape } from "lodash-es";
@@ -59,6 +59,7 @@ export class Yasqe extends CodeMirror {
private abortController: AbortController | undefined;
private queryStatus: "valid" | "error" | undefined;
private queryBtn: HTMLButtonElement | undefined;
+ private saveBtn: HTMLButtonElement | undefined;
private fullscreenBtn: HTMLButtonElement | undefined;
private isFullscreen: boolean = false;
private horizontalResizeWrapper?: HTMLDivElement;
@@ -246,15 +247,76 @@ export class Yasqe extends CodeMirror {
}
/**
- * draw share link button
+ * Draw query btn (FIRST)
+ */
+ if (this.config.showQueryButton) {
+ this.queryBtn = document.createElement("button");
+ addClass(this.queryBtn, "yasqe_queryButton");
+
+ /**
+ * Add all icon states: play (default), warning (error), loading (busy)
+ */
+ const queryEl = document.createElement("i");
+ addClass(queryEl, "fas");
+ addClass(queryEl, "fa-play");
+ addClass(queryEl, "queryIcon");
+ addClass(queryEl, "queryIcon--play");
+ queryEl.setAttribute("aria-hidden", "true");
+ this.queryBtn.appendChild(queryEl);
+
+ const warningEl = document.createElement("i");
+ addClass(warningEl, "fas");
+ addClass(warningEl, "fa-exclamation-triangle");
+ addClass(warningEl, "queryIcon");
+ addClass(warningEl, "queryIcon--warning");
+ warningEl.setAttribute("aria-hidden", "true");
+ this.queryBtn.appendChild(warningEl);
+
+ const loadingEl = document.createElement("i");
+ addClass(loadingEl, "fas");
+ addClass(loadingEl, "fa-spinner");
+ addClass(loadingEl, "fa-pulse");
+ addClass(loadingEl, "queryIcon");
+ addClass(loadingEl, "queryIcon--loading");
+ loadingEl.setAttribute("aria-hidden", "true");
+ this.queryBtn.appendChild(loadingEl);
+
+ /**
+ * Add text label
+ */
+ const queryTextLabel = document.createElement("span");
+ queryTextLabel.className = "yasqe_queryButton_text";
+ queryTextLabel.textContent = "Run";
+ this.queryBtn.appendChild(queryTextLabel);
+
+ this.queryBtn.onclick = () => {
+ if (this.config.queryingDisabled) return; // Don't do anything
+ if (this.req) {
+ this.abortQuery();
+ } else {
+ this.query().catch(() => {}); //catch this to avoid unhandled rejection
+ }
+ };
+ this.queryBtn.title = "Run query (Ctrl+Enter)";
+ this.queryBtn.setAttribute("aria-label", "Run query");
+
+ buttons.appendChild(this.queryBtn);
+ this.updateQueryButton();
+ }
+
+ /**
+ * draw share link button (SECOND)
*/
if (this.config.createShareableLink) {
- var svgShare = drawSvgStringAsElement(imgs.share);
+ const shareIcon = document.createElement("i");
+ addClass(shareIcon, "fas");
+ addClass(shareIcon, "fa-share-nodes");
+ shareIcon.setAttribute("aria-hidden", "true");
const shareLinkWrapper = document.createElement("button");
shareLinkWrapper.className = "yasqe_share";
shareLinkWrapper.title = "Share query";
shareLinkWrapper.setAttribute("aria-label", "Share query");
- shareLinkWrapper.appendChild(svgShare);
+ shareLinkWrapper.appendChild(shareIcon);
buttons.appendChild(shareLinkWrapper);
shareLinkWrapper.addEventListener("click", (event: MouseEvent) => showSharePopup(event));
shareLinkWrapper.addEventListener("keydown", (event: KeyboardEvent) => {
@@ -294,7 +356,10 @@ export class Yasqe extends CodeMirror {
if (type === "warning") {
const iconWrapper = document.createElement("span");
iconWrapper.className = "yasqe_toast-icon";
- const icon = drawSvgStringAsElement(imgs.warning);
+ const icon = document.createElement("i");
+ addClass(icon, "fas");
+ addClass(icon, "fa-exclamation-triangle");
+ icon.setAttribute("aria-hidden", "true");
iconWrapper.appendChild(icon);
toastElement.appendChild(iconWrapper);
}
@@ -468,9 +533,9 @@ export class Yasqe extends CodeMirror {
// Position popup after layout is complete
const positionPopup = () => {
if (!popup) return;
- const svgPos = svgShare.getBoundingClientRect();
- popup.style.top = svgShare.offsetTop + svgPos.height + "px";
- popup.style.left = svgShare.offsetLeft + svgShare.clientWidth - popup.clientWidth + "px";
+ const sharePos = shareLinkWrapper.getBoundingClientRect();
+ popup.style.top = shareLinkWrapper.offsetTop + sharePos.height + "px";
+ popup.style.left = shareLinkWrapper.offsetLeft + shareLinkWrapper.clientWidth - popup.clientWidth + "px";
};
if (typeof window !== "undefined" && typeof window.requestAnimationFrame === "function") {
@@ -481,47 +546,37 @@ export class Yasqe extends CodeMirror {
}
};
}
+
/**
- * Draw query btn
+ * Draw save button (THIRD)
*/
- if (this.config.showQueryButton) {
- this.queryBtn = document.createElement("button");
- addClass(this.queryBtn, "yasqe_queryButton");
-
- /**
- * Add busy/valid/error btns
- */
- const queryEl = drawSvgStringAsElement(imgs.query);
- addClass(queryEl, "queryIcon");
- this.queryBtn.appendChild(queryEl);
-
- const warningIcon = drawSvgStringAsElement(imgs.warning);
- addClass(warningIcon, "warningIcon");
- this.queryBtn.appendChild(warningIcon);
-
- this.queryBtn.onclick = () => {
- if (this.config.queryingDisabled) return; // Don't do anything
- if (this.req) {
- this.abortQuery();
- } else {
- this.query().catch(() => {}); //catch this to avoid unhandled rejection
- }
- };
- this.queryBtn.title = "Run query";
- this.queryBtn.setAttribute("aria-label", "Run query");
-
- buttons.appendChild(this.queryBtn);
- this.updateQueryButton();
- }
+ const saveBtn = document.createElement("button");
+ addClass(saveBtn, "yasqe_saveButton");
+ const saveIcon = document.createElement("i");
+ addClass(saveIcon, "fas");
+ addClass(saveIcon, "fa-save");
+ saveIcon.setAttribute("aria-hidden", "true");
+ saveBtn.appendChild(saveIcon);
+ saveBtn.onclick = () => {
+ // Call the managed query save function if available
+ this.emit("saveManagedQuery");
+ };
+ saveBtn.title = "Save managed query (Ctrl+S)";
+ saveBtn.setAttribute("aria-label", "Save managed query");
+ saveBtn.style.display = "none"; // Hidden by default, shown when workspace is configured
+ this.saveBtn = saveBtn;
+ buttons.appendChild(saveBtn);
/**
- * Draw format btn
+ * Draw format btn (FOURTH)
*/
if (this.config.showFormatButton) {
const formatBtn = document.createElement("button");
addClass(formatBtn, "yasqe_formatButton");
- const formatIcon = drawSvgStringAsElement(imgs.format);
- addClass(formatIcon, "formatIcon");
+ const formatIcon = document.createElement("i");
+ addClass(formatIcon, "fas");
+ addClass(formatIcon, "fa-align-left");
+ formatIcon.setAttribute("aria-hidden", "true");
formatBtn.appendChild(formatIcon);
formatBtn.onclick = () => {
this.format();
@@ -532,15 +587,21 @@ export class Yasqe extends CodeMirror {
}
/**
- * Draw fullscreen btn
+ * Draw fullscreen btn (FIFTH)
*/
this.fullscreenBtn = document.createElement("button");
addClass(this.fullscreenBtn, "yasqe_fullscreenButton");
- const fullscreenIcon = drawSvgStringAsElement(imgs.fullscreen);
+ const fullscreenIcon = document.createElement("i");
+ addClass(fullscreenIcon, "fas");
+ addClass(fullscreenIcon, "fa-expand");
addClass(fullscreenIcon, "fullscreenIcon");
+ fullscreenIcon.setAttribute("aria-hidden", "true");
this.fullscreenBtn.appendChild(fullscreenIcon);
- const fullscreenExitIcon = drawSvgStringAsElement(imgs.fullscreenExit);
+ const fullscreenExitIcon = document.createElement("i");
+ addClass(fullscreenExitIcon, "fas");
+ addClass(fullscreenExitIcon, "fa-compress");
addClass(fullscreenExitIcon, "fullscreenExitIcon");
+ fullscreenExitIcon.setAttribute("aria-hidden", "true");
this.fullscreenBtn.appendChild(fullscreenExitIcon);
this.fullscreenBtn.onclick = () => {
this.toggleFullscreen();
@@ -633,8 +694,11 @@ export class Yasqe extends CodeMirror {
const dropdownBtn = document.createElement("button");
addClass(dropdownBtn, "yasqe_snippetDropdownButton");
dropdownBtn.textContent = groupName + " ";
- const chevron = drawSvgStringAsElement(imgs.chevronDown);
+ const chevron = document.createElement("i");
+ addClass(chevron, "fas");
+ addClass(chevron, "fa-chevron-down");
addClass(chevron, "chevronIcon");
+ chevron.setAttribute("aria-hidden", "true");
dropdownBtn.appendChild(chevron);
dropdownBtn.setAttribute("aria-label", `${groupName} snippets`);
dropdownBtn.setAttribute("aria-expanded", "false");
@@ -703,8 +767,11 @@ export class Yasqe extends CodeMirror {
const showMoreBtn = document.createElement("button");
addClass(showMoreBtn, "yasqe_snippetDropdownButton", "yasqe_showMoreButton");
showMoreBtn.textContent = "More ";
- const chevron = drawSvgStringAsElement(imgs.chevronDown);
+ const chevron = document.createElement("i");
+ addClass(chevron, "fas");
+ addClass(chevron, "fa-chevron-down");
addClass(chevron, "chevronIcon");
+ chevron.setAttribute("aria-hidden", "true");
showMoreBtn.appendChild(chevron);
showMoreBtn.setAttribute("aria-label", "Show more snippets");
showMoreBtn.setAttribute("aria-expanded", "false");
@@ -926,7 +993,7 @@ export class Yasqe extends CodeMirror {
this.queryBtn.title = this.config.queryingDisabled;
} else {
removeClass(this.queryBtn, "query_disabled");
- this.queryBtn.title = "Run query";
+ this.queryBtn.title = "Run query (Ctrl+Enter)";
this.queryBtn.setAttribute("aria-label", "Run query");
}
if (!status) {
@@ -940,14 +1007,32 @@ export class Yasqe extends CodeMirror {
}
/**
- * Set/remove spinner if needed
+ * Set/remove spinner if needed and manage icon visibility
*/
- if (this.req && this.queryBtn.className.indexOf("busy") < 0) {
+ const isBusy = !!this.req;
+ const hasError = status === "error";
+
+ if (isBusy && this.queryBtn.className.indexOf("busy") < 0) {
this.queryBtn.className = this.queryBtn.className += " busy";
}
- if (!this.req && this.queryBtn.className.indexOf("busy") >= 0) {
+ if (!isBusy && this.queryBtn.className.indexOf("busy") >= 0) {
this.queryBtn.className = this.queryBtn.className.replace("busy", "");
}
+
+ // Update button state classes for icon switching
+ if (isBusy) {
+ addClass(this.queryBtn, "state-loading");
+ removeClass(this.queryBtn, "state-error");
+ removeClass(this.queryBtn, "state-normal");
+ } else if (hasError) {
+ removeClass(this.queryBtn, "state-loading");
+ addClass(this.queryBtn, "state-error");
+ removeClass(this.queryBtn, "state-normal");
+ } else {
+ removeClass(this.queryBtn, "state-loading");
+ removeClass(this.queryBtn, "state-error");
+ addClass(this.queryBtn, "state-normal");
+ }
}
public handleLocalStorageQuotaFull(_e: any) {
console.warn("Localstorage quota exceeded. Clearing all queries");
@@ -1273,25 +1358,42 @@ export class Yasqe extends CodeMirror {
//we don't want the gutter error, so return
return;
}
- const warningEl = drawSvgStringAsElement(imgs.warning);
+
+ // Add gutter error icon
+ const errorEl = document.createElement("i");
+ errorEl.className = "fas fa-exclamation-circle parseErrorIcon";
+
+ // Build tooltip message
if (state.errorMsg) {
- tooltip(this, warningEl, escape(token.state.errorMsg));
+ tooltip(this, errorEl, escape(state.errorMsg));
} else if (state.possibleCurrent && state.possibleCurrent.length > 0) {
- var expectedEncoded: string[] = [];
+ const expectedEncoded: string[] = [];
state.possibleCurrent.forEach(function (expected) {
expectedEncoded.push("" + escape(expected) + "");
});
- tooltip(this, warningEl, "This line is invalid. Expected: " + expectedEncoded.join(", "));
+ tooltip(this, errorEl, "This line is invalid. Expected: " + expectedEncoded.join(", "));
+ } else {
+ tooltip(this, errorEl, "Syntax error");
+ }
+
+ this.setGutterMarker(l, "gutterErrorBar", errorEl);
+
+ // Also change run button to show error state
+ if (this.queryBtn) {
+ addClass(this.queryBtn, "query_error");
+ this.queryBtn.title = "Query has syntax errors";
}
- // warningEl.style.marginTop = "2px";
- // warningEl.style.marginLeft = "2px";
- warningEl.className = "parseErrorIcon";
- this.setGutterMarker(l, "gutterErrorBar", warningEl);
this.queryValid = false;
break;
}
}
+ if (this.queryValid) {
+ if (this.queryBtn) {
+ removeClass(this.queryBtn, "query_error");
+ this.queryBtn.title = "Run query (Ctrl+Enter)";
+ }
+ }
}
public setCheckConstructVariables(isEnabled: boolean) {
@@ -1304,6 +1406,12 @@ export class Yasqe extends CodeMirror {
}
}
+ public setSaveButtonVisible(visible: boolean) {
+ if (this.saveBtn) {
+ this.saveBtn.style.display = visible ? "inline-flex" : "none";
+ }
+ }
+
public checkConstructVariables() {
// Clear any existing warnings first
this.clearGutter("gutterConstructWarning");
@@ -1349,12 +1457,14 @@ export class Yasqe extends CodeMirror {
const escapedVar = undefinedVar.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
const varRegex = new RegExp(`${escapedVar}(?![a-zA-Z0-9_])`);
if (varRegex.test(line)) {
- const warningEl = drawSvgStringAsElement(imgs.warning);
- warningEl.className = "constructVariableWarning";
+ const warningEl = document.createElement("i");
+ warningEl.className = "fas fa-exclamation-triangle constructVariableWarning";
tooltip(
this,
warningEl,
- escape(`Variable ${undefinedVar} is used in CONSTRUCT but not defined in WHERE clause`),
+ "Variable " +
+ escape(undefinedVar) +
+ " is used in CONSTRUCT but not defined in WHERE clause",
);
this.setGutterMarker(l, "gutterConstructWarning", warningEl);
break; // Only one marker per line
diff --git a/packages/yasqe/src/scss/buttons.scss b/packages/yasqe/src/scss/buttons.scss
index 180555a6..139706c6 100644
--- a/packages/yasqe/src/scss/buttons.scss
+++ b/packages/yasqe/src/scss/buttons.scss
@@ -64,28 +64,33 @@
position: absolute;
top: 10px;
right: 20px;
-
- svg {
- fill: #505050;
- }
-
z-index: 5;
.yasqe_share {
cursor: pointer;
- margin-top: 3px;
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
border: none;
background: none;
- svg {
- height: 25px;
- width: 25px;
+ padding: 6px;
+ margin-left: 8px;
+ height: 36px;
+ width: 36px;
+ color: #505050;
+
+ i {
+ font-size: 20px;
+ }
+
+ &:hover {
+ color: #337ab7;
}
}
button {
- vertical-align: top;
- margin-left: 5px;
+ vertical-align: middle;
+ margin-left: 8px;
}
.yasqe_sharePopup {
position: absolute;
@@ -163,77 +168,118 @@
}
}
.yasqe_queryButton {
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
position: relative;
- border: none;
- background: none;
- padding: 0;
+ border: 1px solid #337ab7;
+ background: #337ab7;
+ padding: 7px 12px;
cursor: pointer;
- width: $queryButtonWidth;
- height: $queryButtonHeight;
+ border-radius: 4px;
+ color: white;
+ font-weight: 500;
+ font-size: 14px;
+ transition: all 0.2s ease;
+ line-height: 1.4;
+ height: 36px;
+
+ .yasqe_queryButton_text {
+ line-height: 1.4;
+ white-space: nowrap;
+ flex-shrink: 0;
+ }
.queryIcon {
- display: block;
- svg {
- width: $queryButtonWidth;
- height: $queryButtonHeight;
- }
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+ font-size: 14px;
+ width: 14px;
+ height: 14px;
}
- .svgImg {
- position: absolute;
- height: inherit;
- top: 0;
+
+ // Hide all icons by default
+ .queryIcon--warning,
+ .queryIcon--loading {
+ display: none;
}
- &.busy {
- svg {
- #loadingIcon {
- stroke-dasharray: 100;
- animation: dash 1.5s linear infinite;
- stroke-width: 8px;
- stroke: white;
- }
+ // Show warning icon when in error state
+ &.state-error {
+ .queryIcon--play {
+ display: none;
+ }
+ .queryIcon--warning {
+ display: flex;
}
}
- @keyframes dash {
- to {
- stroke-dashoffset: 200;
+ // Show loading icon when busy
+ &.state-loading {
+ .queryIcon--play {
+ display: none;
+ }
+ .queryIcon--loading {
+ display: flex;
}
}
- @keyframes rotate {
- 100% {
- transform: rotate(360deg);
- }
+ &:hover {
+ background: #2868a0;
+ border-color: #2868a0;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
- .warningIcon {
- display: none;
+ &:active {
+ background: #1f5180;
+ border-color: #1f5180;
}
&.query_error {
- .warningIcon {
- display: block;
- top: 5px;
- right: 0px;
+ background: #dc3545;
+ border-color: #dc3545;
- svg {
- width: 15px;
- height: 15px;
+ &:hover {
+ background: #c82333;
+ border-color: #c82333;
+ }
- g {
- fill: red;
- }
- }
+ &:active {
+ background: #bd2130;
+ border-color: #bd2130;
+ }
+ }
+
+ &.busy {
+ background: #5a8fc2;
+ border-color: #5a8fc2;
+ cursor: wait;
+ }
+
+ @keyframes dash {
+ to {
+ stroke-dashoffset: 200;
+ }
+ }
+
+ @keyframes rotate {
+ 100% {
+ transform: rotate(360deg);
}
}
&.query_disabled {
cursor: not-allowed;
- .queryIcon {
- opacity: 0.5;
- filter: alpha(opacity=50);
+ opacity: 0.6;
+ background: #95b3d0;
+ border-color: #95b3d0;
+
+ &:hover {
+ background: #95b3d0;
+ border-color: #95b3d0;
+ box-shadow: none;
}
}
@@ -244,51 +290,65 @@
}
}
- .yasqe_saveManagedQueryButton {
- display: inline-block;
+ .yasqe_saveButton {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
border: none;
background: none;
cursor: pointer;
- padding: 0;
- margin-left: 5px;
+ padding: 6px;
+ margin-left: 8px;
+ height: 36px;
+ width: 36px;
+ color: #505050;
- svg {
- height: 25px;
- width: 25px;
+ i {
+ font-size: 20px;
+ }
+
+ &:hover {
+ color: #337ab7;
}
}
.yasqe_formatButton {
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
border: none;
background: none;
cursor: pointer;
- padding: 0;
- margin-left: 5px;
+ padding: 6px;
+ margin-left: 8px;
+ height: 36px;
+ width: 36px;
+ color: #505050;
- svg {
- height: 25px;
- width: 25px;
+ i {
+ font-size: 20px;
}
&:hover {
- svg {
- fill: #337ab7;
- }
+ color: #337ab7;
}
}
.yasqe_fullscreenButton {
- display: inline-block;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
border: none;
background: none;
cursor: pointer;
- padding: 0;
- margin-left: 5px;
+ padding: 6px;
+ margin-left: 8px;
+ height: 36px;
+ width: 36px;
+ color: #505050;
- svg {
- height: 25px;
- width: 25px;
+ i {
+ font-size: 20px;
}
.fullscreenExitIcon {
@@ -296,9 +356,7 @@
}
&:hover {
- svg {
- fill: #337ab7;
- }
+ color: #337ab7;
}
}
}
@@ -405,10 +463,8 @@
.chevronIcon {
width: 16px;
height: 16px;
- svg {
- width: 16px;
- height: 16px;
- fill: var(--yasgui-text-secondary, #505050);
+ i {
+ font-size: 12px;
}
}
@@ -518,10 +574,8 @@
align-items: center;
flex-shrink: 0;
- svg {
- width: 24px;
- height: 24px;
- fill: #000;
+ i {
+ font-size: 24px;
}
}
}
diff --git a/packages/yasqe/src/scss/yasqe.scss b/packages/yasqe/src/scss/yasqe.scss
index f92bc0a9..651b0fda 100644
--- a/packages/yasqe/src/scss/yasqe.scss
+++ b/packages/yasqe/src/scss/yasqe.scss
@@ -1,3 +1,6 @@
+@import "@fortawesome/fontawesome-free/css/fontawesome.css";
+@import "@fortawesome/fontawesome-free/css/solid.css";
+
.yasqe {
display: flex;
flex-direction: column;
@@ -9,10 +12,6 @@
// Height is set via inline style by the resize handler
}
- .svgImg {
- display: inline-block;
- }
-
span.shortlinkErr {
font-size: small;
color: red;
@@ -50,13 +49,10 @@
}
.parseErrorIcon {
- width: 13px;
- height: 13px;
- margin-top: 2px;
+ font-size: 13px;
+ color: #d32f2f;
+ cursor: pointer;
margin-left: 2px;
- svg g {
- fill: red;
- }
}
.constructVariableWarning {
@@ -64,19 +60,29 @@
height: 13px;
margin-top: 2px;
margin-left: 2px;
- svg g {
- fill: orange;
- }
+ font-size: 13px;
+ color: orange;
+ cursor: pointer;
}
.yasqe_tooltip {
- background: rgba(0, 0, 0, 0.8);
+ font-size: 14px;
border-radius: 5px;
color: #fff;
padding: 5px 15px;
width: 220px;
white-space: normal;
+ line-height: 1.5;
margin-top: 5px;
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
+ font-weight: normal;
+ font-style: normal;
+ text-align: left;
+
+ strong {
+ font-weight: 600;
+ text-decoration: underline;
+ }
}
.notificationLoader {
diff --git a/packages/yasqe/src/tooltip.ts b/packages/yasqe/src/tooltip.ts
index f8df7cb6..b4a629e6 100644
--- a/packages/yasqe/src/tooltip.ts
+++ b/packages/yasqe/src/tooltip.ts
@@ -6,7 +6,7 @@
*/
import Yasqe from "./";
-export default function tooltip(_yasqe: Yasqe, parent: HTMLDivElement, html: string) {
+export default function tooltip(_yasqe: Yasqe, parent: HTMLElement, html: string) {
var tooltip: HTMLDivElement;
parent.onmouseover = function () {
if (!tooltip) {
diff --git a/packages/yasr/package.json b/packages/yasr/package.json
index 9a6cac20..48952ca7 100644
--- a/packages/yasr/package.json
+++ b/packages/yasr/package.json
@@ -24,7 +24,7 @@
"directory": "packages/yasr"
},
"dependencies": {
- "@fortawesome/free-solid-svg-icons": "^5.14.0",
+ "@fortawesome/free-solid-svg-icons": "^7.1.0",
"@json2csv/plainjs": "^7.0.4",
"@matdata/yasgui-utils": "^4.6.1",
"@matdata/yasqe": "^4.6.1",
diff --git a/packages/yasr/src/imgs.ts b/packages/yasr/src/imgs.ts
deleted file mode 100644
index 1f9fe226..00000000
--- a/packages/yasr/src/imgs.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-export var download =
- '';
-export var pivot = ``;
-export var fullscreen =
- '';
-export var fullscreenExit =
- '';
diff --git a/packages/yasr/src/index.ts b/packages/yasr/src/index.ts
index cd8a9b4c..af5a87e7 100644
--- a/packages/yasr/src/index.ts
+++ b/packages/yasr/src/index.ts
@@ -2,20 +2,11 @@ import { EventEmitter } from "events";
import { merge, filter, mapValues, uniqueId } from "lodash-es";
import getDefaults from "./defaults";
import type { Plugin } from "./plugins";
-import {
- Storage as YStorage,
- drawFontAwesomeIconAsSvg,
- drawSvgStringAsElement,
- removeClass,
- addClass,
- hasClass,
-} from "@matdata/yasgui-utils";
+import { Storage as YStorage, removeClass, addClass, hasClass } from "@matdata/yasgui-utils";
import Parser from "./parsers";
export { default as Parser } from "./parsers";
import { addScript, addCss, sanitize } from "./helpers";
-import * as faDownload from "@fortawesome/free-solid-svg-icons/faDownload";
-import * as faQuestionCircle from "@fortawesome/free-solid-svg-icons/faQuestionCircle";
-import * as imgs from "./imgs";
+
import "./main.scss";
export interface PersistentConfig {
@@ -374,7 +365,8 @@ export class Yasr extends EventEmitter {
this.downloadBtn.setAttribute("aria-label", "Download Results");
this.downloadBtn.setAttribute("tabindex", "0"); // anchor elements with no href are not automatically included in the tabindex
this.downloadBtn.setAttribute("role", "button");
- const iconEl = drawSvgStringAsElement(drawFontAwesomeIconAsSvg(faDownload));
+ const iconEl = document.createElement("i");
+ addClass(iconEl, "fas", "fa-download");
iconEl.setAttribute("aria-hidden", "true");
this.downloadBtn.appendChild(iconEl);
this.downloadBtn.addEventListener("click", () => {
@@ -455,12 +447,16 @@ export class Yasr extends EventEmitter {
this.fullscreenBtn.setAttribute("tabindex", "0");
this.fullscreenBtn.setAttribute("role", "button");
- const fullscreenIcon = drawSvgStringAsElement(imgs.fullscreen);
+ const fullscreenIcon = document.createElement("i");
+ addClass(fullscreenIcon, "fas");
+ addClass(fullscreenIcon, "fa-expand");
addClass(fullscreenIcon, "fullscreenIcon");
fullscreenIcon.setAttribute("aria-hidden", "true");
this.fullscreenBtn.appendChild(fullscreenIcon);
- const fullscreenExitIcon = drawSvgStringAsElement(imgs.fullscreenExit);
+ const fullscreenExitIcon = document.createElement("i");
+ addClass(fullscreenExitIcon, "fas");
+ addClass(fullscreenExitIcon, "fa-compress");
addClass(fullscreenExitIcon, "fullscreenExitIcon");
fullscreenExitIcon.setAttribute("aria-hidden", "true");
this.fullscreenBtn.appendChild(fullscreenExitIcon);
@@ -520,7 +516,9 @@ export class Yasr extends EventEmitter {
private drawDocumentationButton() {
this.documentationLink = document.createElement("a");
addClass(this.documentationLink, "yasr_btn", "yasr_external_ref_btn");
- this.documentationLink.appendChild(drawSvgStringAsElement(drawFontAwesomeIconAsSvg(faQuestionCircle)));
+ const icon = document.createElement("i");
+ addClass(icon, "fas", "fa-circle-question");
+ this.documentationLink.appendChild(icon);
this.documentationLink.href = "https://docs.triply.cc/yasgui/";
this.documentationLink.target = "_blank";
this.documentationLink.rel = "noopener noreferrer";
diff --git a/packages/yasr/src/main.scss b/packages/yasr/src/main.scss
index 55895777..6b528e6c 100644
--- a/packages/yasr/src/main.scss
+++ b/packages/yasr/src/main.scss
@@ -1,4 +1,6 @@
@use "scss/variables.scss";
+@import "@fortawesome/fontawesome-free/css/fontawesome.css";
+@import "@fortawesome/fontawesome-free/css/solid.css";
.yasr {
display: flex;
@@ -14,17 +16,12 @@
.yasr_btn {
border: none;
background: inherit;
- }
-
- .svgImg {
display: flex;
- flex-direction: row;
- svg {
- max-width: 100%;
- max-height: 100%;
- width: 15px;
- height: 15px;
- align-self: center;
+ align-items: center;
+ justify-content: center;
+
+ i {
+ font-size: 16px;
}
}
@@ -36,10 +33,6 @@
color: inherit;
text-decoration-color: inherit;
}
- .svgImg svg {
- width: 18px;
- height: 18px;
- }
}
a {
@@ -174,15 +167,15 @@
.yasr_fullscreenButton {
border: none;
background: none;
-
- svg {
- height: 20px;
- width: 20px;
- }
+ color: var(--yasgui-button-text, #505050);
.fullscreenExitIcon {
display: none;
}
+
+ &:hover {
+ color: var(--yasgui-button-hover, #337ab7);
+ }
}
&.fullscreen {
diff --git a/packages/yasr/src/plugins/boolean/index.scss b/packages/yasr/src/plugins/boolean/index.scss
index 2d91ecf7..1f6105e3 100644
--- a/packages/yasr/src/plugins/boolean/index.scss
+++ b/packages/yasr/src/plugins/boolean/index.scss
@@ -3,9 +3,9 @@
display: flex;
align-items: center;
justify-content: center;
- svg {
- margin-bottom: -10px;
- margin-right: 7px;
+ i {
+ font-size: 30px;
+ margin-right: 12px;
}
}
}
diff --git a/packages/yasr/src/plugins/boolean/index.ts b/packages/yasr/src/plugins/boolean/index.ts
index 6ba7c385..d36add06 100644
--- a/packages/yasr/src/plugins/boolean/index.ts
+++ b/packages/yasr/src/plugins/boolean/index.ts
@@ -5,11 +5,6 @@ import Yasr from "../../";
import { Plugin } from "../";
import "./index.scss";
export interface PluginConfig {}
-import { drawSvgStringAsElement } from "@matdata/yasgui-utils";
-const cross =
- '';
-const check =
- '';
export default class Boolean implements Plugin {
private yasr: Yasr;
@@ -23,7 +18,9 @@ export default class Boolean implements Plugin {
el.className = "booleanResult";
const boolVal = this.yasr.results?.getBoolean();
- el.appendChild(drawSvgStringAsElement(boolVal ? check : cross));
+ const icon = document.createElement("i");
+ icon.className = boolVal ? "fas fa-check" : "fas fa-xmark";
+ el.appendChild(icon);
const textEl = document.createElement("span");
textEl.textContent = boolVal ? "True" : "False";
el.appendChild(textEl);
diff --git a/packages/yasr/src/plugins/response/index.scss b/packages/yasr/src/plugins/response/index.scss
index b7e24602..2fe4164f 100644
--- a/packages/yasr/src/plugins/response/index.scss
+++ b/packages/yasr/src/plugins/response/index.scss
@@ -44,19 +44,12 @@
color: #337ab7;
padding: 10px;
margin: 10px;
- svg {
+ i {
margin-left: 0.5rem;
- fill: #337ab7;
- color: #337ab7;
}
&:hover {
color: #255681;
border-color: #337ab7;
- fill: #255681;
- svg {
- color: #255681;
- fill: #255681;
- }
}
}
}
diff --git a/packages/yasr/src/plugins/response/index.ts b/packages/yasr/src/plugins/response/index.ts
index 3bde726a..027cd876 100644
--- a/packages/yasr/src/plugins/response/index.ts
+++ b/packages/yasr/src/plugins/response/index.ts
@@ -16,10 +16,8 @@ import "codemirror/mode/xml/xml.js";
import "codemirror/mode/javascript/javascript.js";
import "codemirror/lib/codemirror.css";
import "codemirror/theme/material-palenight.css";
-import { drawSvgStringAsElement, addClass, removeClass, drawFontAwesomeIconAsSvg } from "@matdata/yasgui-utils";
-import * as faAlignIcon from "@fortawesome/free-solid-svg-icons/faAlignLeft";
+import { addClass, removeClass } from "@matdata/yasgui-utils";
import { DeepReadonly } from "ts-essentials";
-import * as imgs from "../../imgs";
export interface PluginConfig {
maxLines: number;
@@ -51,7 +49,9 @@ export default class Response implements Plugin {
return true;
}
public getIcon() {
- return drawSvgStringAsElement(drawFontAwesomeIconAsSvg(faAlignIcon));
+ const icon = document.createElement("i");
+ icon.className = "fas fa-align-left";
+ return icon;
}
download(filename?: string) {
if (!this.yasr.results) return;
@@ -146,7 +146,10 @@ export default class Response implements Plugin {
const text = document.createElement("span");
text.innerText = "Download result";
downloadButton.appendChild(text);
- downloadButton.appendChild(drawSvgStringAsElement(imgs.download));
+ const downloadIcon = document.createElement("i");
+ addClass(downloadIcon, "fas");
+ addClass(downloadIcon, "fa-download");
+ downloadButton.appendChild(downloadIcon);
downloadButton.addEventListener("click", () => this.yasr.download());
downloadButton.addEventListener("keydown", (event) => {
if (event.code === "Space" || event.code === "Enter") this.yasr.download();