@matdata/yasgui 5.1.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/build/ts/src/PersistentConfig.d.ts +4 -1
- package/build/ts/src/PersistentConfig.js +8 -0
- package/build/ts/src/PersistentConfig.js.map +1 -1
- package/build/ts/src/Tab.d.ts +8 -0
- package/build/ts/src/Tab.js +82 -0
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +2 -0
- package/build/ts/src/TabSettingsModal.js +98 -1
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/defaults.js +2 -0
- package/build/ts/src/defaults.js.map +1 -1
- package/build/ts/src/index.d.ts +6 -0
- package/build/ts/src/index.js.map +1 -1
- package/build/yasgui.min.css +1 -1
- package/build/yasgui.min.css.map +3 -3
- package/build/yasgui.min.js +95 -89
- package/build/yasgui.min.js.map +3 -3
- package/package.json +1 -1
- package/src/PersistentConfig.ts +10 -1
- package/src/Tab.ts +120 -0
- package/src/TabSettingsModal.scss +102 -0
- package/src/TabSettingsModal.ts +131 -1
- package/src/defaults.ts +2 -0
- package/src/endpointSelect.scss +34 -0
- package/src/index.scss +2 -2
- package/src/index.ts +11 -0
- package/src/tab.scss +50 -2
- package/src/themes.scss +23 -0
package/package.json
CHANGED
package/src/PersistentConfig.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Storage as YStorage } from "@matdata/yasgui-utils";
|
|
2
|
-
import Yasgui from "./";
|
|
2
|
+
import Yasgui, { EndpointButton } from "./";
|
|
3
3
|
import * as Tab from "./Tab";
|
|
4
4
|
export var storageNamespace = "triply";
|
|
5
5
|
export interface PersistedJson {
|
|
@@ -10,6 +10,7 @@ export interface PersistedJson {
|
|
|
10
10
|
lastClosedTab: { index: number; tab: Tab.PersistedJson } | undefined;
|
|
11
11
|
prefixes?: string;
|
|
12
12
|
autoCaptureEnabled?: boolean;
|
|
13
|
+
customEndpointButtons?: EndpointButton[];
|
|
13
14
|
}
|
|
14
15
|
function getDefaults(): PersistedJson {
|
|
15
16
|
return {
|
|
@@ -20,6 +21,7 @@ function getDefaults(): PersistedJson {
|
|
|
20
21
|
lastClosedTab: undefined,
|
|
21
22
|
prefixes: "",
|
|
22
23
|
autoCaptureEnabled: true,
|
|
24
|
+
customEndpointButtons: [],
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -152,6 +154,13 @@ export default class PersistentConfig {
|
|
|
152
154
|
this.persistedJson.autoCaptureEnabled = enabled;
|
|
153
155
|
this.toStorage();
|
|
154
156
|
}
|
|
157
|
+
public getCustomEndpointButtons(): EndpointButton[] {
|
|
158
|
+
return this.persistedJson.customEndpointButtons || [];
|
|
159
|
+
}
|
|
160
|
+
public setCustomEndpointButtons(buttons: EndpointButton[]) {
|
|
161
|
+
this.persistedJson.customEndpointButtons = buttons;
|
|
162
|
+
this.toStorage();
|
|
163
|
+
}
|
|
155
164
|
public static clear() {
|
|
156
165
|
const storage = new YStorage(storageNamespace);
|
|
157
166
|
storage.removeNamespace();
|
package/src/Tab.ts
CHANGED
|
@@ -9,6 +9,17 @@ import * as shareLink from "./linkUtils";
|
|
|
9
9
|
import EndpointSelect from "./endpointSelect";
|
|
10
10
|
import "./tab.scss";
|
|
11
11
|
import { getRandomId, default as Yasgui, YasguiRequestConfig } from "./";
|
|
12
|
+
|
|
13
|
+
// Layout orientation toggle icons
|
|
14
|
+
const HORIZONTAL_LAYOUT_ICON = `<svg viewBox="0 0 24 24" class="svgImg">
|
|
15
|
+
<rect x="2" y="4" width="9" height="16" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
16
|
+
<rect x="13" y="4" width="9" height="16" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
17
|
+
</svg>`;
|
|
18
|
+
|
|
19
|
+
const VERTICAL_LAYOUT_ICON = `<svg viewBox="0 0 24 24" class="svgImg">
|
|
20
|
+
<rect x="2" y="2" width="20" height="8" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
21
|
+
<rect x="2" y="12" width="20" height="10" stroke="currentColor" stroke-width="2" fill="none"/>
|
|
22
|
+
</svg>`;
|
|
12
23
|
export interface PersistedJsonYasr extends YasrPersistentConfig {
|
|
13
24
|
responseSummary: Parser.ResponseSummary;
|
|
14
25
|
}
|
|
@@ -57,12 +68,16 @@ export class Tab extends EventEmitter {
|
|
|
57
68
|
private yasqeWrapperEl: HTMLDivElement | undefined;
|
|
58
69
|
private yasrWrapperEl: HTMLDivElement | undefined;
|
|
59
70
|
private endpointSelect: EndpointSelect | undefined;
|
|
71
|
+
private endpointButtonsContainer: HTMLDivElement | undefined;
|
|
60
72
|
private settingsModal?: TabSettingsModal;
|
|
73
|
+
private currentOrientation: "vertical" | "horizontal";
|
|
74
|
+
private orientationToggleButton?: HTMLButtonElement;
|
|
61
75
|
constructor(yasgui: Yasgui, conf: PersistedJson) {
|
|
62
76
|
super();
|
|
63
77
|
if (!conf || conf.id === undefined) throw new Error("Expected a valid configuration to initialize tab with");
|
|
64
78
|
this.yasgui = yasgui;
|
|
65
79
|
this.persistentJson = conf;
|
|
80
|
+
this.currentOrientation = this.yasgui.config.orientation || "vertical";
|
|
66
81
|
}
|
|
67
82
|
public name() {
|
|
68
83
|
return this.persistentJson.name;
|
|
@@ -81,6 +96,9 @@ export class Tab extends EventEmitter {
|
|
|
81
96
|
this.rootEl.setAttribute("role", "tabpanel");
|
|
82
97
|
this.rootEl.setAttribute("aria-labelledby", "tab-" + this.persistentJson.id);
|
|
83
98
|
|
|
99
|
+
// Apply orientation class
|
|
100
|
+
addClass(this.rootEl, `orientation-${this.currentOrientation}`);
|
|
101
|
+
|
|
84
102
|
// We group controlbar and Yasqe, so that users can easily .appendChild() to the .editorwrapper div
|
|
85
103
|
// to add a div that goes alongside the controlbar and editor, while YASR still goes full width
|
|
86
104
|
// Useful for adding an infos div that goes alongside the editor without needing to rebuild the whole Yasgui class
|
|
@@ -99,6 +117,7 @@ export class Tab extends EventEmitter {
|
|
|
99
117
|
|
|
100
118
|
//yasr
|
|
101
119
|
this.yasrWrapperEl = document.createElement("div");
|
|
120
|
+
this.yasrWrapperEl.className = "yasrWrapperEl";
|
|
102
121
|
|
|
103
122
|
this.initTabSettingsMenu();
|
|
104
123
|
this.rootEl.appendChild(editorWrapper);
|
|
@@ -222,10 +241,63 @@ export class Tab extends EventEmitter {
|
|
|
222
241
|
}
|
|
223
242
|
private initControlbar() {
|
|
224
243
|
this.initEndpointSelectField();
|
|
244
|
+
this.initOrientationToggle();
|
|
245
|
+
this.initEndpointButtons();
|
|
225
246
|
if (this.yasgui.config.endpointInfo && this.controlBarEl) {
|
|
226
247
|
this.controlBarEl.appendChild(this.yasgui.config.endpointInfo());
|
|
227
248
|
}
|
|
228
249
|
}
|
|
250
|
+
|
|
251
|
+
private initOrientationToggle() {
|
|
252
|
+
if (!this.controlBarEl) return;
|
|
253
|
+
|
|
254
|
+
this.orientationToggleButton = document.createElement("button");
|
|
255
|
+
this.orientationToggleButton.className = "tabContextButton orientationToggle";
|
|
256
|
+
this.orientationToggleButton.setAttribute("aria-label", "Toggle layout orientation");
|
|
257
|
+
this.orientationToggleButton.title = "Toggle layout orientation";
|
|
258
|
+
|
|
259
|
+
this.updateOrientationToggleIcon();
|
|
260
|
+
|
|
261
|
+
this.orientationToggleButton.addEventListener("click", () => {
|
|
262
|
+
this.toggleOrientation();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
this.controlBarEl.appendChild(this.orientationToggleButton);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
private updateOrientationToggleIcon() {
|
|
269
|
+
if (!this.orientationToggleButton) return;
|
|
270
|
+
|
|
271
|
+
// Show the icon for the layout we'll switch TO (not the current layout)
|
|
272
|
+
this.orientationToggleButton.innerHTML =
|
|
273
|
+
this.currentOrientation === "vertical" ? HORIZONTAL_LAYOUT_ICON : VERTICAL_LAYOUT_ICON;
|
|
274
|
+
this.orientationToggleButton.title =
|
|
275
|
+
this.currentOrientation === "vertical" ? "Switch to horizontal layout" : "Switch to vertical layout";
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
public toggleOrientation() {
|
|
279
|
+
if (!this.rootEl) return;
|
|
280
|
+
|
|
281
|
+
// Remove old orientation class
|
|
282
|
+
removeClass(this.rootEl, `orientation-${this.currentOrientation}`);
|
|
283
|
+
|
|
284
|
+
// Toggle orientation
|
|
285
|
+
this.currentOrientation = this.currentOrientation === "vertical" ? "horizontal" : "vertical";
|
|
286
|
+
|
|
287
|
+
// Add new orientation class
|
|
288
|
+
addClass(this.rootEl, `orientation-${this.currentOrientation}`);
|
|
289
|
+
|
|
290
|
+
// Update button icon
|
|
291
|
+
this.updateOrientationToggleIcon();
|
|
292
|
+
|
|
293
|
+
// Refresh components to adjust to new layout
|
|
294
|
+
if (this.yasqe) {
|
|
295
|
+
this.yasqe.refresh();
|
|
296
|
+
}
|
|
297
|
+
if (this.yasr) {
|
|
298
|
+
this.yasr.refresh();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
229
301
|
public getYasqe() {
|
|
230
302
|
return this.yasqe;
|
|
231
303
|
}
|
|
@@ -253,6 +325,54 @@ export class Tab extends EventEmitter {
|
|
|
253
325
|
});
|
|
254
326
|
}
|
|
255
327
|
|
|
328
|
+
private initEndpointButtons() {
|
|
329
|
+
if (!this.controlBarEl) throw new Error("Need to initialize wrapper elements before drawing endpoint buttons");
|
|
330
|
+
|
|
331
|
+
// Create container if it doesn't exist
|
|
332
|
+
if (!this.endpointButtonsContainer) {
|
|
333
|
+
this.endpointButtonsContainer = document.createElement("div");
|
|
334
|
+
addClass(this.endpointButtonsContainer, "endpointButtonsContainer");
|
|
335
|
+
this.controlBarEl.appendChild(this.endpointButtonsContainer);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
this.refreshEndpointButtons();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
public refreshEndpointButtons() {
|
|
342
|
+
if (!this.endpointButtonsContainer) return;
|
|
343
|
+
|
|
344
|
+
// Clear existing buttons
|
|
345
|
+
this.endpointButtonsContainer.innerHTML = "";
|
|
346
|
+
|
|
347
|
+
// Merge config buttons with custom user buttons
|
|
348
|
+
const configButtons = this.yasgui.config.endpointButtons || [];
|
|
349
|
+
const customButtons = this.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
350
|
+
const allButtons = [...configButtons, ...customButtons];
|
|
351
|
+
|
|
352
|
+
if (allButtons.length === 0) {
|
|
353
|
+
// Hide container if no buttons
|
|
354
|
+
this.endpointButtonsContainer.style.display = "none";
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Show container
|
|
359
|
+
this.endpointButtonsContainer.style.display = "flex";
|
|
360
|
+
|
|
361
|
+
allButtons.forEach((buttonConfig) => {
|
|
362
|
+
const button = document.createElement("button");
|
|
363
|
+
addClass(button, "endpointButton");
|
|
364
|
+
button.textContent = buttonConfig.label;
|
|
365
|
+
button.title = `Set endpoint to ${buttonConfig.endpoint}`;
|
|
366
|
+
button.setAttribute("aria-label", `Set endpoint to ${buttonConfig.endpoint}`);
|
|
367
|
+
|
|
368
|
+
button.addEventListener("click", () => {
|
|
369
|
+
this.setEndpoint(buttonConfig.endpoint);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
this.endpointButtonsContainer!.appendChild(button);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
256
376
|
private checkEndpointForCors(endpoint: string) {
|
|
257
377
|
if (this.yasgui.config.corsProxy && !(endpoint in Yasgui.corsEnabled)) {
|
|
258
378
|
const askUrl = new URL(endpoint);
|
|
@@ -256,3 +256,105 @@
|
|
|
256
256
|
background: var(--yasgui-border-color, #d0d0d0);
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
+
|
|
260
|
+
.endpointButtonsList {
|
|
261
|
+
margin: 15px 0;
|
|
262
|
+
border: 1px solid var(--yasgui-border-color, #ddd);
|
|
263
|
+
border-radius: 4px;
|
|
264
|
+
padding: 10px;
|
|
265
|
+
background: var(--yasgui-bg-secondary, #f7f7f7);
|
|
266
|
+
max-height: 200px;
|
|
267
|
+
overflow-y: auto;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.endpointButtonItem {
|
|
271
|
+
display: flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
padding: 8px;
|
|
274
|
+
margin-bottom: 8px;
|
|
275
|
+
background: var(--yasgui-bg-primary, #fff);
|
|
276
|
+
border: 1px solid var(--yasgui-border-color, #ddd);
|
|
277
|
+
border-radius: 3px;
|
|
278
|
+
|
|
279
|
+
&:last-child {
|
|
280
|
+
margin-bottom: 0;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.buttonLabel {
|
|
284
|
+
font-weight: 600;
|
|
285
|
+
min-width: 100px;
|
|
286
|
+
color: var(--yasgui-text-primary, #333);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.buttonEndpoint {
|
|
290
|
+
flex: 1;
|
|
291
|
+
margin-left: 10px;
|
|
292
|
+
color: var(--yasgui-text-secondary, #666);
|
|
293
|
+
font-size: 13px;
|
|
294
|
+
overflow: hidden;
|
|
295
|
+
text-overflow: ellipsis;
|
|
296
|
+
white-space: nowrap;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.removeButton {
|
|
300
|
+
margin-left: auto;
|
|
301
|
+
background: transparent;
|
|
302
|
+
border: none;
|
|
303
|
+
color: var(--yasgui-error-color, #d32f2f);
|
|
304
|
+
font-size: 20px;
|
|
305
|
+
cursor: pointer;
|
|
306
|
+
padding: 0 8px;
|
|
307
|
+
line-height: 1;
|
|
308
|
+
|
|
309
|
+
&:hover {
|
|
310
|
+
opacity: 0.7;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.emptyMessage {
|
|
316
|
+
color: var(--yasgui-text-muted, #999);
|
|
317
|
+
font-style: italic;
|
|
318
|
+
text-align: center;
|
|
319
|
+
padding: 20px;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
.addEndpointButtonForm {
|
|
323
|
+
display: flex;
|
|
324
|
+
gap: 8px;
|
|
325
|
+
align-items: center;
|
|
326
|
+
|
|
327
|
+
input {
|
|
328
|
+
flex: 1;
|
|
329
|
+
padding: 8px 12px;
|
|
330
|
+
border: 1px solid var(--yasgui-border-color, #ddd);
|
|
331
|
+
border-radius: 3px;
|
|
332
|
+
background: var(--yasgui-bg-primary, #fff);
|
|
333
|
+
color: var(--yasgui-text-primary, #333);
|
|
334
|
+
font-size: 14px;
|
|
335
|
+
|
|
336
|
+
&:focus {
|
|
337
|
+
outline: none;
|
|
338
|
+
border-color: var(--yasgui-accent-color, #337ab7);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
&::placeholder {
|
|
342
|
+
color: var(--yasgui-text-muted, #999);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
.addEndpointButton {
|
|
347
|
+
padding: 8px 16px;
|
|
348
|
+
background: var(--yasgui-accent-color, #337ab7);
|
|
349
|
+
color: white;
|
|
350
|
+
border: none;
|
|
351
|
+
border-radius: 3px;
|
|
352
|
+
cursor: pointer;
|
|
353
|
+
font-size: 14px;
|
|
354
|
+
white-space: nowrap;
|
|
355
|
+
|
|
356
|
+
&:hover {
|
|
357
|
+
background: var(--yasgui-link-hover, #286090);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
package/src/TabSettingsModal.ts
CHANGED
|
@@ -126,8 +126,14 @@ export default class TabSettingsModal {
|
|
|
126
126
|
addClass(prefixTab, "modalTabButton");
|
|
127
127
|
prefixTab.onclick = () => this.switchTab("prefix");
|
|
128
128
|
|
|
129
|
+
const endpointsTab = document.createElement("button");
|
|
130
|
+
endpointsTab.textContent = "Endpoint Buttons";
|
|
131
|
+
addClass(endpointsTab, "modalTabButton");
|
|
132
|
+
endpointsTab.onclick = () => this.switchTab("endpoints");
|
|
133
|
+
|
|
129
134
|
tabsContainer.appendChild(requestTab);
|
|
130
135
|
tabsContainer.appendChild(prefixTab);
|
|
136
|
+
tabsContainer.appendChild(endpointsTab);
|
|
131
137
|
body.appendChild(tabsContainer);
|
|
132
138
|
|
|
133
139
|
// Tab content containers
|
|
@@ -141,8 +147,14 @@ export default class TabSettingsModal {
|
|
|
141
147
|
prefixContent.id = "prefix-content";
|
|
142
148
|
this.drawPrefixSettings(prefixContent);
|
|
143
149
|
|
|
150
|
+
const endpointsContent = document.createElement("div");
|
|
151
|
+
addClass(endpointsContent, "modalTabContent");
|
|
152
|
+
endpointsContent.id = "endpoints-content";
|
|
153
|
+
this.drawEndpointButtonsSettings(endpointsContent);
|
|
154
|
+
|
|
144
155
|
body.appendChild(requestContent);
|
|
145
156
|
body.appendChild(prefixContent);
|
|
157
|
+
body.appendChild(endpointsContent);
|
|
146
158
|
|
|
147
159
|
this.modalContent.appendChild(body);
|
|
148
160
|
|
|
@@ -174,7 +186,11 @@ export default class TabSettingsModal {
|
|
|
174
186
|
const contents = this.modalContent.querySelectorAll(".modalTabContent");
|
|
175
187
|
|
|
176
188
|
buttons.forEach((btn, index) => {
|
|
177
|
-
if (
|
|
189
|
+
if (
|
|
190
|
+
(tabName === "request" && index === 0) ||
|
|
191
|
+
(tabName === "prefix" && index === 1) ||
|
|
192
|
+
(tabName === "endpoints" && index === 2)
|
|
193
|
+
) {
|
|
178
194
|
addClass(btn as HTMLElement, "active");
|
|
179
195
|
} else {
|
|
180
196
|
removeClass(btn as HTMLElement, "active");
|
|
@@ -342,6 +358,9 @@ export default class TabSettingsModal {
|
|
|
342
358
|
this.tab.setRequestConfig(updates);
|
|
343
359
|
}
|
|
344
360
|
|
|
361
|
+
// Refresh endpoint buttons to show any changes
|
|
362
|
+
this.tab.refreshEndpointButtons();
|
|
363
|
+
|
|
345
364
|
this.close();
|
|
346
365
|
}
|
|
347
366
|
|
|
@@ -437,6 +456,117 @@ export default class TabSettingsModal {
|
|
|
437
456
|
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
438
457
|
}
|
|
439
458
|
|
|
459
|
+
private drawEndpointButtonsSettings(container: HTMLElement) {
|
|
460
|
+
const section = document.createElement("div");
|
|
461
|
+
addClass(section, "settingsSection");
|
|
462
|
+
|
|
463
|
+
const label = document.createElement("label");
|
|
464
|
+
label.textContent = "Custom Endpoint Buttons";
|
|
465
|
+
addClass(label, "settingsLabel");
|
|
466
|
+
|
|
467
|
+
const help = document.createElement("div");
|
|
468
|
+
help.textContent = "Add custom endpoint buttons that will appear next to the endpoint textbox.";
|
|
469
|
+
addClass(help, "settingsHelp");
|
|
470
|
+
|
|
471
|
+
section.appendChild(label);
|
|
472
|
+
section.appendChild(help);
|
|
473
|
+
|
|
474
|
+
// List of existing buttons
|
|
475
|
+
const buttonsList = document.createElement("div");
|
|
476
|
+
addClass(buttonsList, "endpointButtonsList");
|
|
477
|
+
this.renderEndpointButtonsList(buttonsList);
|
|
478
|
+
section.appendChild(buttonsList);
|
|
479
|
+
|
|
480
|
+
// Form to add new button
|
|
481
|
+
const addForm = document.createElement("div");
|
|
482
|
+
addClass(addForm, "addEndpointButtonForm");
|
|
483
|
+
|
|
484
|
+
const labelInput = document.createElement("input");
|
|
485
|
+
labelInput.type = "text";
|
|
486
|
+
labelInput.placeholder = "Button label (e.g., DBpedia)";
|
|
487
|
+
addClass(labelInput, "endpointButtonLabelInput");
|
|
488
|
+
|
|
489
|
+
const endpointInput = document.createElement("input");
|
|
490
|
+
endpointInput.type = "url";
|
|
491
|
+
endpointInput.placeholder = "Endpoint URL (e.g., https://dbpedia.org/sparql)";
|
|
492
|
+
addClass(endpointInput, "endpointButtonEndpointInput");
|
|
493
|
+
|
|
494
|
+
const addButton = document.createElement("button");
|
|
495
|
+
addButton.textContent = "+ Add Button";
|
|
496
|
+
addClass(addButton, "addEndpointButton");
|
|
497
|
+
addButton.type = "button";
|
|
498
|
+
addButton.onclick = () => {
|
|
499
|
+
const labelValue = labelInput.value.trim();
|
|
500
|
+
const endpointValue = endpointInput.value.trim();
|
|
501
|
+
|
|
502
|
+
if (!labelValue || !endpointValue) {
|
|
503
|
+
alert("Please enter both a label and an endpoint URL.");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Add to persistent config
|
|
508
|
+
const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
509
|
+
currentButtons.push({ label: labelValue, endpoint: endpointValue });
|
|
510
|
+
this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
|
|
511
|
+
|
|
512
|
+
// Clear inputs
|
|
513
|
+
labelInput.value = "";
|
|
514
|
+
endpointInput.value = "";
|
|
515
|
+
|
|
516
|
+
// Refresh list
|
|
517
|
+
this.renderEndpointButtonsList(buttonsList);
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
addForm.appendChild(labelInput);
|
|
521
|
+
addForm.appendChild(endpointInput);
|
|
522
|
+
addForm.appendChild(addButton);
|
|
523
|
+
section.appendChild(addForm);
|
|
524
|
+
|
|
525
|
+
container.appendChild(section);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
private renderEndpointButtonsList(container: HTMLElement) {
|
|
529
|
+
container.innerHTML = "";
|
|
530
|
+
const customButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
531
|
+
|
|
532
|
+
if (customButtons.length === 0) {
|
|
533
|
+
const emptyMsg = document.createElement("div");
|
|
534
|
+
emptyMsg.textContent = "No custom buttons yet. Add one below.";
|
|
535
|
+
addClass(emptyMsg, "emptyMessage");
|
|
536
|
+
container.appendChild(emptyMsg);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
customButtons.forEach((btn, index) => {
|
|
541
|
+
const item = document.createElement("div");
|
|
542
|
+
addClass(item, "endpointButtonItem");
|
|
543
|
+
|
|
544
|
+
const labelSpan = document.createElement("span");
|
|
545
|
+
labelSpan.textContent = `${btn.label}`;
|
|
546
|
+
addClass(labelSpan, "buttonLabel");
|
|
547
|
+
|
|
548
|
+
const endpointSpan = document.createElement("span");
|
|
549
|
+
endpointSpan.textContent = btn.endpoint;
|
|
550
|
+
addClass(endpointSpan, "buttonEndpoint");
|
|
551
|
+
|
|
552
|
+
const removeBtn = document.createElement("button");
|
|
553
|
+
removeBtn.textContent = "×";
|
|
554
|
+
addClass(removeBtn, "removeButton");
|
|
555
|
+
removeBtn.type = "button";
|
|
556
|
+
removeBtn.onclick = () => {
|
|
557
|
+
const currentButtons = this.tab.yasgui.persistentConfig.getCustomEndpointButtons();
|
|
558
|
+
currentButtons.splice(index, 1);
|
|
559
|
+
this.tab.yasgui.persistentConfig.setCustomEndpointButtons(currentButtons);
|
|
560
|
+
this.renderEndpointButtonsList(container);
|
|
561
|
+
};
|
|
562
|
+
|
|
563
|
+
item.appendChild(labelSpan);
|
|
564
|
+
item.appendChild(endpointSpan);
|
|
565
|
+
item.appendChild(removeBtn);
|
|
566
|
+
container.appendChild(item);
|
|
567
|
+
});
|
|
568
|
+
}
|
|
569
|
+
|
|
440
570
|
private getThemeToggleIcon(): string {
|
|
441
571
|
const currentTheme = this.tab.yasgui.getTheme();
|
|
442
572
|
// In dark mode, show moon icon (clicking will switch to light)
|
package/src/defaults.ts
CHANGED
package/src/endpointSelect.scss
CHANGED
|
@@ -119,4 +119,38 @@
|
|
|
119
119
|
opacity: 0.8;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
|
+
|
|
123
|
+
.endpointButtonsContainer {
|
|
124
|
+
display: flex;
|
|
125
|
+
align-items: center;
|
|
126
|
+
gap: 4px;
|
|
127
|
+
margin-left: 4px;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.endpointButton {
|
|
131
|
+
border: 1px solid var(--yasgui-endpoint-button-border, #337ab7);
|
|
132
|
+
background-color: var(--yasgui-endpoint-button-bg, #337ab7);
|
|
133
|
+
color: var(--yasgui-endpoint-button-text, #ffffff);
|
|
134
|
+
border-radius: 3px;
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
padding: 4px 12px;
|
|
137
|
+
font-size: 13px;
|
|
138
|
+
font-weight: 500;
|
|
139
|
+
white-space: nowrap;
|
|
140
|
+
transition: all 0.2s ease;
|
|
141
|
+
|
|
142
|
+
&:hover {
|
|
143
|
+
background-color: var(--yasgui-endpoint-button-hover-bg, #286090);
|
|
144
|
+
border-color: var(--yasgui-endpoint-button-hover-border, #286090);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
&:active {
|
|
148
|
+
transform: translateY(1px);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
&:focus {
|
|
152
|
+
outline: 2px solid var(--yasgui-endpoint-button-focus, #5cb3fd);
|
|
153
|
+
outline-offset: 2px;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
122
156
|
}
|
package/src/index.scss
CHANGED
package/src/index.ts
CHANGED
|
@@ -31,6 +31,10 @@ if (window) {
|
|
|
31
31
|
export type YasguiRequestConfig = Omit<RequestConfig<Yasgui>, "adjustQueryBeforeRequest"> & {
|
|
32
32
|
adjustQueryBeforeRequest: RequestConfig<Yasqe>["adjustQueryBeforeRequest"];
|
|
33
33
|
};
|
|
34
|
+
export interface EndpointButton {
|
|
35
|
+
endpoint: string;
|
|
36
|
+
label: string;
|
|
37
|
+
}
|
|
34
38
|
export interface Config<EndpointObject extends CatalogueItem = CatalogueItem> {
|
|
35
39
|
/**
|
|
36
40
|
* Autofocus yasqe on load or tab switch
|
|
@@ -41,6 +45,7 @@ export interface Config<EndpointObject extends CatalogueItem = CatalogueItem> {
|
|
|
41
45
|
tabName: string;
|
|
42
46
|
corsProxy: string | undefined;
|
|
43
47
|
endpointCatalogueOptions: EndpointSelectConfig<EndpointObject>;
|
|
48
|
+
endpointButtons?: EndpointButton[];
|
|
44
49
|
//The function allows us to modify the config before we pass it on to a tab
|
|
45
50
|
populateFromUrl: boolean | ((configFromUrl: PersistedTabJson) => PersistedTabJson);
|
|
46
51
|
autoAddOnInit: boolean;
|
|
@@ -55,6 +60,12 @@ export interface Config<EndpointObject extends CatalogueItem = CatalogueItem> {
|
|
|
55
60
|
nonSslDomain?: string;
|
|
56
61
|
theme?: Theme;
|
|
57
62
|
showThemeToggle?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Layout orientation: 'vertical' (default) or 'horizontal' (side-by-side)
|
|
65
|
+
* 'vertical': YASQE on top, YASR below
|
|
66
|
+
* 'horizontal': YASQE on left, YASR on right
|
|
67
|
+
*/
|
|
68
|
+
orientation?: "vertical" | "horizontal";
|
|
58
69
|
}
|
|
59
70
|
export type PartialConfig = {
|
|
60
71
|
[P in keyof Config]?: Config[P] extends object ? Partial<Config[P]> : Config[P];
|
package/src/tab.scss
CHANGED
|
@@ -18,9 +18,57 @@
|
|
|
18
18
|
left: 0;
|
|
19
19
|
right: 0;
|
|
20
20
|
z-index: 9999;
|
|
21
|
-
background:
|
|
22
|
-
border-bottom: 1px solid
|
|
21
|
+
background: var(--yasgui-bg-primary);
|
|
22
|
+
border-bottom: 1px solid var(--yasgui-border-color);
|
|
23
23
|
padding: 5px;
|
|
24
|
+
|
|
25
|
+
// Hide the layout orientation toggle button in fullscreen mode
|
|
26
|
+
.orientationToggle {
|
|
27
|
+
display: none;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Horizontal layout (side-by-side)
|
|
32
|
+
&.orientation-horizontal {
|
|
33
|
+
&.active {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: row;
|
|
36
|
+
gap: 10px;
|
|
37
|
+
// Allow flexible height - can use 100vh or be constrained by parent
|
|
38
|
+
min-height: var(--yasgui-min-height);
|
|
39
|
+
height: calc(100vh - var(--yasgui-header-height));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.editorwrapper {
|
|
43
|
+
flex: 1;
|
|
44
|
+
min-width: 0;
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Make YASQE fill the vertical space in horizontal mode
|
|
51
|
+
.yasqe {
|
|
52
|
+
display: flex;
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
flex: 1;
|
|
55
|
+
min-height: 0;
|
|
56
|
+
|
|
57
|
+
.CodeMirror {
|
|
58
|
+
height: 100% !important;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.yasrWrapperEl {
|
|
63
|
+
flex: 1;
|
|
64
|
+
min-width: 0;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.yasr {
|
|
69
|
+
margin-top: 0;
|
|
70
|
+
height: 100%;
|
|
71
|
+
}
|
|
24
72
|
}
|
|
25
73
|
}
|
|
26
74
|
.yasr {
|
package/src/themes.scss
CHANGED
|
@@ -36,6 +36,18 @@
|
|
|
36
36
|
--yasgui-fallback-border: #d1d1d1;
|
|
37
37
|
--yasgui-resize-handle: #d1d1d1;
|
|
38
38
|
--yasgui-nav-bg: #eee;
|
|
39
|
+
--yasgui-match-highlight-bg: #dbdeed;
|
|
40
|
+
|
|
41
|
+
// Layout dimensions (used for horizontal orientation)
|
|
42
|
+
--yasgui-header-height: 100px; // Height to subtract from viewport in horizontal layout
|
|
43
|
+
--yasgui-min-height: 400px; // Minimum height for horizontal layout panels
|
|
44
|
+
|
|
45
|
+
--yasgui-endpoint-button-bg: #337ab7;
|
|
46
|
+
--yasgui-endpoint-button-border: #337ab7;
|
|
47
|
+
--yasgui-endpoint-button-text: #ffffff;
|
|
48
|
+
--yasgui-endpoint-button-hover-bg: #286090;
|
|
49
|
+
--yasgui-endpoint-button-hover-border: #286090;
|
|
50
|
+
--yasgui-endpoint-button-focus: #5cb3fd;
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
[data-theme="dark"] {
|
|
@@ -73,6 +85,13 @@
|
|
|
73
85
|
--yasgui-fallback-border: #3e3e42;
|
|
74
86
|
--yasgui-resize-handle: #3e3e42;
|
|
75
87
|
--yasgui-nav-bg: #2d2d30;
|
|
88
|
+
--yasgui-match-highlight-bg: #3a4a5a;
|
|
89
|
+
--yasgui-endpoint-button-bg: #4fc3f7;
|
|
90
|
+
--yasgui-endpoint-button-border: #4fc3f7;
|
|
91
|
+
--yasgui-endpoint-button-text: #1e1e1e;
|
|
92
|
+
--yasgui-endpoint-button-hover-bg: #81d4fa;
|
|
93
|
+
--yasgui-endpoint-button-hover-border: #81d4fa;
|
|
94
|
+
--yasgui-endpoint-button-focus: #81d4fa;
|
|
76
95
|
}
|
|
77
96
|
|
|
78
97
|
// Apply theme colors to Yasgui components
|
|
@@ -255,6 +274,10 @@
|
|
|
255
274
|
fill: var(--yasgui-error-color);
|
|
256
275
|
}
|
|
257
276
|
|
|
277
|
+
.cm-matchhighlight {
|
|
278
|
+
background-color: var(--yasgui-match-highlight-bg);
|
|
279
|
+
}
|
|
280
|
+
|
|
258
281
|
// Yasqe buttons theming (share, query, fullscreen)
|
|
259
282
|
.yasqe_buttons {
|
|
260
283
|
svg {
|