@matdata/yasgui 5.0.1 → 5.1.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 +148 -111
- package/build/ts/src/Tab.js +3 -1
- package/build/ts/src/Tab.js.map +1 -1
- package/build/ts/src/TabElements.js +6 -0
- package/build/ts/src/TabElements.js.map +1 -1
- package/build/ts/src/TabSettingsModal.d.ts +2 -0
- package/build/ts/src/TabSettingsModal.js +30 -8
- package/build/ts/src/TabSettingsModal.js.map +1 -1
- package/build/ts/src/ThemeManager.d.ts +17 -0
- package/build/ts/src/ThemeManager.js +69 -0
- package/build/ts/src/ThemeManager.js.map +1 -0
- package/build/ts/src/defaults.js +2 -0
- package/build/ts/src/defaults.js.map +1 -1
- package/build/ts/src/index.d.ts +10 -0
- package/build/ts/src/index.js +17 -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 +100 -90
- package/build/yasgui.min.js.map +4 -4
- package/package.json +1 -1
- package/src/Tab.ts +5 -0
- package/src/TabElements.scss +33 -6
- package/src/TabElements.ts +9 -0
- package/src/TabSettingsModal.scss +52 -20
- package/src/TabSettingsModal.ts +40 -12
- package/src/ThemeManager.ts +120 -0
- package/src/defaults.ts +2 -0
- package/src/index.ts +32 -0
- package/src/tab.scss +10 -6
- package/src/themes.scss +520 -0
package/package.json
CHANGED
package/src/Tab.ts
CHANGED
|
@@ -356,8 +356,13 @@ export class Tab extends EventEmitter {
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
private initYasqe() {
|
|
359
|
+
// Set theme based on current yasgui theme
|
|
360
|
+
const currentTheme = this.yasgui.getTheme();
|
|
361
|
+
const cmTheme = currentTheme === "dark" ? "material-palenight" : "default";
|
|
362
|
+
|
|
359
363
|
const yasqeConf: Partial<YasqeConfig> = {
|
|
360
364
|
...this.yasgui.config.yasqe,
|
|
365
|
+
theme: cmTheme,
|
|
361
366
|
value: this.persistentJson.yasqe.value,
|
|
362
367
|
editorHeight: this.persistentJson.yasqe.editorHeight ? this.persistentJson.yasqe.editorHeight : undefined,
|
|
363
368
|
persistenceId: null, //yasgui handles persistent storing
|
package/src/TabElements.scss
CHANGED
|
@@ -18,6 +18,33 @@ $minTabHeight: 35px;
|
|
|
18
18
|
border-bottom: 2px solid transparent;
|
|
19
19
|
box-sizing: border-box;
|
|
20
20
|
}
|
|
21
|
+
.themeToggle {
|
|
22
|
+
cursor: pointer;
|
|
23
|
+
height: 100%;
|
|
24
|
+
margin-left: auto;
|
|
25
|
+
margin-right: 10px;
|
|
26
|
+
padding: 6px 8px;
|
|
27
|
+
background: transparent;
|
|
28
|
+
border: none;
|
|
29
|
+
color: var(--yasgui-button-text, #505050);
|
|
30
|
+
fill: var(--yasgui-button-text, #505050);
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
|
|
36
|
+
&:hover {
|
|
37
|
+
color: var(--yasgui-button-hover, #000);
|
|
38
|
+
fill: var(--yasgui-button-hover, #000);
|
|
39
|
+
background: var(--yasgui-bg-secondary, #f7f7f7);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
svg {
|
|
43
|
+
width: 20px;
|
|
44
|
+
height: 20px;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
21
48
|
.addTab {
|
|
22
49
|
cursor: pointer;
|
|
23
50
|
height: 100%;
|
|
@@ -80,7 +107,7 @@ $minTabHeight: 35px;
|
|
|
80
107
|
}
|
|
81
108
|
&.active a {
|
|
82
109
|
border-bottom-color: $activeColor;
|
|
83
|
-
color: #555;
|
|
110
|
+
color: var(--yasgui-text-primary, #555);
|
|
84
111
|
}
|
|
85
112
|
input {
|
|
86
113
|
display: none;
|
|
@@ -100,7 +127,7 @@ $minTabHeight: 35px;
|
|
|
100
127
|
}
|
|
101
128
|
a {
|
|
102
129
|
font-weight: 600;
|
|
103
|
-
color: color.adjust(#555, $lightness: 20%);
|
|
130
|
+
color: var(--yasgui-text-secondary, color.adjust(#555, $lightness: 20%));
|
|
104
131
|
font-size: 15px;
|
|
105
132
|
line-height: 1.5rem;
|
|
106
133
|
font-weight: 500;
|
|
@@ -110,17 +137,17 @@ $minTabHeight: 35px;
|
|
|
110
137
|
overflow: hidden;
|
|
111
138
|
&:hover {
|
|
112
139
|
border-bottom-color: $hoverColor;
|
|
113
|
-
color: #555;
|
|
140
|
+
color: var(--yasgui-text-primary, #555);
|
|
114
141
|
}
|
|
115
142
|
&:focus {
|
|
116
143
|
border-bottom-color: #faa857;
|
|
117
|
-
color: #555;
|
|
144
|
+
color: var(--yasgui-text-primary, #555);
|
|
118
145
|
}
|
|
119
146
|
.closeTab {
|
|
120
|
-
color: #000;
|
|
147
|
+
color: var(--yasgui-text-primary, #000);
|
|
121
148
|
margin-left: 7px;
|
|
122
149
|
font-size: 15px;
|
|
123
|
-
text-shadow: 0 1px 0 #fff;
|
|
150
|
+
text-shadow: 0 1px 0 var(--yasgui-bg-primary, #fff);
|
|
124
151
|
opacity: 0.2;
|
|
125
152
|
font-weight: 700;
|
|
126
153
|
padding: 2px;
|
package/src/TabElements.ts
CHANGED
|
@@ -3,6 +3,15 @@ import TabContextMenu from "./TabContextMenu";
|
|
|
3
3
|
import { hasClass, addClass, removeClass } from "@matdata/yasgui-utils";
|
|
4
4
|
import sortablejs from "sortablejs";
|
|
5
5
|
import "./TabElements.scss";
|
|
6
|
+
|
|
7
|
+
// Theme toggle icons
|
|
8
|
+
const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
9
|
+
<path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
|
|
10
|
+
</svg>`;
|
|
11
|
+
|
|
12
|
+
const SUN_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
13
|
+
<path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/>
|
|
14
|
+
</svg>`;
|
|
6
15
|
export interface TabList {}
|
|
7
16
|
export class TabListEl {
|
|
8
17
|
private tabList: TabList;
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
.themeToggle {
|
|
2
|
+
cursor: pointer;
|
|
3
|
+
background: transparent;
|
|
4
|
+
color: inherit;
|
|
5
|
+
border: none;
|
|
6
|
+
padding: 6px 8px;
|
|
7
|
+
margin-left: 10px;
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
|
|
12
|
+
svg {
|
|
13
|
+
width: 20px;
|
|
14
|
+
height: 20px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&:hover {
|
|
18
|
+
opacity: 0.7;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&:active {
|
|
22
|
+
opacity: 0.5;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
1
26
|
.tabPrefixButton {
|
|
2
27
|
cursor: pointer;
|
|
3
28
|
background: transparent;
|
|
@@ -35,7 +60,7 @@
|
|
|
35
60
|
}
|
|
36
61
|
|
|
37
62
|
.tabSettingsModal {
|
|
38
|
-
background: white;
|
|
63
|
+
background: var(--yasgui-bg-primary, white);
|
|
39
64
|
border-radius: 8px;
|
|
40
65
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
41
66
|
max-width: 800px;
|
|
@@ -51,12 +76,13 @@
|
|
|
51
76
|
justify-content: space-between;
|
|
52
77
|
align-items: center;
|
|
53
78
|
padding: 20px;
|
|
54
|
-
border-bottom: 1px solid #e0e0e0;
|
|
79
|
+
border-bottom: 1px solid var(--yasgui-border-color, #e0e0e0);
|
|
55
80
|
|
|
56
81
|
h2 {
|
|
57
82
|
margin: 0;
|
|
58
83
|
font-size: 20px;
|
|
59
84
|
font-weight: 600;
|
|
85
|
+
color: var(--yasgui-text-primary, #000);
|
|
60
86
|
}
|
|
61
87
|
|
|
62
88
|
.closeButton {
|
|
@@ -65,13 +91,13 @@
|
|
|
65
91
|
font-size: 32px;
|
|
66
92
|
line-height: 1;
|
|
67
93
|
cursor: pointer;
|
|
68
|
-
color: #666;
|
|
94
|
+
color: var(--yasgui-text-secondary, #666);
|
|
69
95
|
padding: 0;
|
|
70
96
|
width: 32px;
|
|
71
97
|
height: 32px;
|
|
72
98
|
|
|
73
99
|
&:hover {
|
|
74
|
-
color: #333;
|
|
100
|
+
color: var(--yasgui-text-primary, #333);
|
|
75
101
|
}
|
|
76
102
|
}
|
|
77
103
|
}
|
|
@@ -86,7 +112,7 @@
|
|
|
86
112
|
display: flex;
|
|
87
113
|
gap: 10px;
|
|
88
114
|
margin-bottom: 20px;
|
|
89
|
-
border-bottom: 2px solid #e0e0e0;
|
|
115
|
+
border-bottom: 2px solid var(--yasgui-border-color, #e0e0e0);
|
|
90
116
|
}
|
|
91
117
|
|
|
92
118
|
.modalTabButton {
|
|
@@ -96,18 +122,18 @@
|
|
|
96
122
|
font-size: 14px;
|
|
97
123
|
font-weight: 600;
|
|
98
124
|
cursor: pointer;
|
|
99
|
-
color: #666;
|
|
125
|
+
color: var(--yasgui-text-secondary, #666);
|
|
100
126
|
border-bottom: 3px solid transparent;
|
|
101
127
|
margin-bottom: -2px;
|
|
102
128
|
transition: all 0.2s;
|
|
103
129
|
|
|
104
130
|
&:hover {
|
|
105
|
-
color: #337ab7;
|
|
131
|
+
color: var(--yasgui-accent-color, #337ab7);
|
|
106
132
|
}
|
|
107
133
|
|
|
108
134
|
&.active {
|
|
109
|
-
color: #337ab7;
|
|
110
|
-
border-bottom-color: #337ab7;
|
|
135
|
+
color: var(--yasgui-accent-color, #337ab7);
|
|
136
|
+
border-bottom-color: var(--yasgui-accent-color, #337ab7);
|
|
111
137
|
}
|
|
112
138
|
}
|
|
113
139
|
|
|
@@ -128,11 +154,12 @@
|
|
|
128
154
|
margin-bottom: 8px;
|
|
129
155
|
font-size: 14px;
|
|
130
156
|
display: block;
|
|
157
|
+
color: var(--yasgui-text-primary, #000);
|
|
131
158
|
}
|
|
132
159
|
|
|
133
160
|
.settingsHelp {
|
|
134
161
|
font-size: 12px;
|
|
135
|
-
color: #666;
|
|
162
|
+
color: var(--yasgui-text-secondary, #666);
|
|
136
163
|
margin-bottom: 10px;
|
|
137
164
|
font-style: italic;
|
|
138
165
|
}
|
|
@@ -140,13 +167,15 @@
|
|
|
140
167
|
.settingsSelect {
|
|
141
168
|
width: 100%;
|
|
142
169
|
padding: 8px;
|
|
143
|
-
border: 1px solid #ccc;
|
|
170
|
+
border: 1px solid var(--yasgui-input-border, #ccc);
|
|
144
171
|
border-radius: 4px;
|
|
145
172
|
font-size: 14px;
|
|
173
|
+
background-color: var(--yasgui-bg-secondary, white);
|
|
174
|
+
color: var(--yasgui-text-primary, #000);
|
|
146
175
|
|
|
147
176
|
&:focus {
|
|
148
177
|
outline: none;
|
|
149
|
-
border-color: #337ab7;
|
|
178
|
+
border-color: var(--yasgui-input-focus, #337ab7);
|
|
150
179
|
box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.1);
|
|
151
180
|
}
|
|
152
181
|
}
|
|
@@ -154,17 +183,19 @@
|
|
|
154
183
|
.prefixTextarea {
|
|
155
184
|
width: 100%;
|
|
156
185
|
padding: 10px;
|
|
157
|
-
border: 1px solid #ccc;
|
|
186
|
+
border: 1px solid var(--yasgui-input-border, #ccc);
|
|
158
187
|
border-radius: 4px;
|
|
159
188
|
font-family: monospace;
|
|
160
189
|
font-size: 13px;
|
|
161
190
|
resize: vertical;
|
|
162
191
|
box-sizing: border-box;
|
|
163
192
|
min-height: 200px;
|
|
193
|
+
background-color: var(--yasgui-bg-secondary, white);
|
|
194
|
+
color: var(--yasgui-text-primary, #000);
|
|
164
195
|
|
|
165
196
|
&:focus {
|
|
166
197
|
outline: none;
|
|
167
|
-
border-color: #337ab7;
|
|
198
|
+
border-color: var(--yasgui-input-focus, #337ab7);
|
|
168
199
|
box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.1);
|
|
169
200
|
}
|
|
170
201
|
}
|
|
@@ -186,12 +217,13 @@
|
|
|
186
217
|
font-size: 14px;
|
|
187
218
|
user-select: none;
|
|
188
219
|
margin: 0;
|
|
220
|
+
color: var(--yasgui-text-primary, #000);
|
|
189
221
|
}
|
|
190
222
|
}
|
|
191
223
|
|
|
192
224
|
.modalFooter {
|
|
193
225
|
padding: 15px 20px;
|
|
194
|
-
border-top: 1px solid #e0e0e0;
|
|
226
|
+
border-top: 1px solid var(--yasgui-border-color, #e0e0e0);
|
|
195
227
|
display: flex;
|
|
196
228
|
justify-content: flex-end;
|
|
197
229
|
gap: 10px;
|
|
@@ -208,19 +240,19 @@
|
|
|
208
240
|
}
|
|
209
241
|
|
|
210
242
|
.primaryButton {
|
|
211
|
-
background: #337ab7;
|
|
243
|
+
background: var(--yasgui-accent-color, #337ab7);
|
|
212
244
|
color: white;
|
|
213
245
|
|
|
214
246
|
&:hover {
|
|
215
|
-
background: #286090;
|
|
247
|
+
background: var(--yasgui-link-hover, #286090);
|
|
216
248
|
}
|
|
217
249
|
}
|
|
218
250
|
|
|
219
251
|
.secondaryButton {
|
|
220
|
-
background: #e0e0e0;
|
|
221
|
-
color: #333;
|
|
252
|
+
background: var(--yasgui-bg-tertiary, #e0e0e0);
|
|
253
|
+
color: var(--yasgui-text-primary, #333);
|
|
222
254
|
|
|
223
255
|
&:hover {
|
|
224
|
-
background: #d0d0d0;
|
|
256
|
+
background: var(--yasgui-border-color, #d0d0d0);
|
|
225
257
|
}
|
|
226
258
|
}
|
package/src/TabSettingsModal.ts
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import { addClass, removeClass
|
|
1
|
+
import { addClass, removeClass } from "@matdata/yasgui-utils";
|
|
2
2
|
import "./TabSettingsModal.scss";
|
|
3
3
|
import Tab from "./Tab";
|
|
4
4
|
|
|
5
|
+
// Theme toggle icons
|
|
6
|
+
const MOON_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
7
|
+
<path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/>
|
|
8
|
+
</svg>`;
|
|
9
|
+
|
|
10
|
+
const SUN_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
11
|
+
<path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/>
|
|
12
|
+
</svg>`;
|
|
13
|
+
|
|
14
|
+
const SETTINGS_ICON = `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
15
|
+
<path d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.09-.16-.26-.25-.44-.25-.06 0-.12.01-.17.03l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.06-.02-.12-.03-.18-.03-.17 0-.34.09-.43.25l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.09.16.26.25.44.25.06 0 .12-.01.17-.03l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.06.02.12.03.18.03.17 0 .34-.09.43-.25l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zm-7.43 2.52c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/>
|
|
16
|
+
</svg>`;
|
|
17
|
+
|
|
5
18
|
const AcceptOptionsMap: { key: string; value: string }[] = [
|
|
6
19
|
{ key: "JSON", value: "application/sparql-results+json" },
|
|
7
20
|
{ key: "XML", value: "application/sparql-results+xml" },
|
|
@@ -24,6 +37,7 @@ export default class TabSettingsModal {
|
|
|
24
37
|
private modalOverlay!: HTMLElement;
|
|
25
38
|
private modalContent!: HTMLElement;
|
|
26
39
|
private settingsButton!: HTMLButtonElement;
|
|
40
|
+
private themeToggleButton!: HTMLButtonElement;
|
|
27
41
|
private prefixButton!: HTMLButtonElement;
|
|
28
42
|
private prefixTextarea!: HTMLTextAreaElement;
|
|
29
43
|
private autoCaptureCheckbox!: HTMLInputElement;
|
|
@@ -36,19 +50,26 @@ export default class TabSettingsModal {
|
|
|
36
50
|
private init(controlBarEl: HTMLElement) {
|
|
37
51
|
// Settings button
|
|
38
52
|
this.settingsButton = document.createElement("button");
|
|
53
|
+
addClass(this.settingsButton, "tabContextButton");
|
|
39
54
|
this.settingsButton.setAttribute("aria-label", "Settings");
|
|
40
55
|
this.settingsButton.title = "Settings";
|
|
41
|
-
this.settingsButton.
|
|
42
|
-
drawSvgStringAsElement(
|
|
43
|
-
`<svg width="100.06" height="100.05" data-name="Layer 1" version="1.1" viewBox="0 0 100.06 100.05" xmlns="http://www.w3.org/2000/svg">
|
|
44
|
-
<title>Settings</title>
|
|
45
|
-
<path d="m95.868 58.018-3-3.24a42.5 42.5 0 0 0 0-9.43l3-3.22c1.79-1.91 5-4.44 4-6.85l-4.11-10c-1-2.41-5.08-1.91-7.69-2l-4.43-0.16a43.24 43.24 0 0 0-6.64-6.66l-0.14-4.43c-0.08-2.6 0.43-6.69-2-7.69l-10-4.15c-2.4-1-4.95 2.25-6.85 4l-3.23 3a42.49 42.49 0 0 0-9.44 0l-3.21-3c-1.9-1.78-4.44-5-6.85-4l-10 4.11c-2.41 1-1.9 5.09-2 7.69l-0.16 4.42a43.24 43.24 0 0 0-6.67 6.65l-4.42 0.14c-2.6 0.08-6.69-0.43-7.69 2l-4.15 10c-1 2.4 2.25 4.94 4 6.84l3 3.23a42.49 42.49 0 0 0 0 9.44l-3 3.22c-1.78 1.9-5 4.43-4 6.84l4.11 10c1 2.41 5.09 1.91 7.7 2l4.41 0.15a43.24 43.24 0 0 0 6.66 6.68l0.13 4.41c0.08 2.6-0.43 6.7 2 7.7l10 4.15c2.4 1 4.94-2.25 6.84-4l3.24-3a42.5 42.5 0 0 0 9.42 0l3.22 3c1.91 1.79 4.43 5 6.84 4l10-4.11c2.41-1 1.91-5.08 2-7.7l0.15-4.42a43.24 43.24 0 0 0 6.68-6.65l4.42-0.14c2.6-0.08 6.7 0.43 7.7-2l4.15-10c1.04-2.36-2.22-4.9-3.99-6.82zm-45.74 15.7c-12.66 0-22.91-10.61-22.91-23.7s10.25-23.7 22.91-23.7 22.91 10.61 22.91 23.7-10.25 23.7-22.91 23.7z"/>
|
|
46
|
-
</svg>`,
|
|
47
|
-
),
|
|
48
|
-
);
|
|
49
|
-
addClass(this.settingsButton, "tabContextButton");
|
|
50
|
-
controlBarEl.appendChild(this.settingsButton);
|
|
56
|
+
this.settingsButton.innerHTML = SETTINGS_ICON;
|
|
51
57
|
this.settingsButton.onclick = () => this.open();
|
|
58
|
+
controlBarEl.appendChild(this.settingsButton);
|
|
59
|
+
|
|
60
|
+
// Theme toggle button (if enabled)
|
|
61
|
+
if (this.tab.yasgui.config.showThemeToggle) {
|
|
62
|
+
this.themeToggleButton = document.createElement("button");
|
|
63
|
+
addClass(this.themeToggleButton, "themeToggle");
|
|
64
|
+
this.themeToggleButton.setAttribute("aria-label", "Toggle between light and dark theme");
|
|
65
|
+
this.themeToggleButton.title = "Toggle theme";
|
|
66
|
+
this.themeToggleButton.innerHTML = this.getThemeToggleIcon();
|
|
67
|
+
this.themeToggleButton.addEventListener("click", () => {
|
|
68
|
+
this.tab.yasgui.toggleTheme();
|
|
69
|
+
this.themeToggleButton.innerHTML = this.getThemeToggleIcon();
|
|
70
|
+
});
|
|
71
|
+
controlBarEl.appendChild(this.themeToggleButton);
|
|
72
|
+
}
|
|
52
73
|
|
|
53
74
|
// Prefix button
|
|
54
75
|
this.prefixButton = document.createElement("button");
|
|
@@ -77,7 +98,7 @@ export default class TabSettingsModal {
|
|
|
77
98
|
const header = document.createElement("div");
|
|
78
99
|
addClass(header, "modalHeader");
|
|
79
100
|
const title = document.createElement("h2");
|
|
80
|
-
title.textContent = "
|
|
101
|
+
title.textContent = "Settings";
|
|
81
102
|
header.appendChild(title);
|
|
82
103
|
|
|
83
104
|
const closeBtn = document.createElement("button");
|
|
@@ -416,6 +437,13 @@ export default class TabSettingsModal {
|
|
|
416
437
|
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
417
438
|
}
|
|
418
439
|
|
|
440
|
+
private getThemeToggleIcon(): string {
|
|
441
|
+
const currentTheme = this.tab.yasgui.getTheme();
|
|
442
|
+
// In dark mode, show moon icon (clicking will switch to light)
|
|
443
|
+
// In light mode, show sun icon (clicking will switch to dark)
|
|
444
|
+
return currentTheme === "dark" ? MOON_ICON : SUN_ICON;
|
|
445
|
+
}
|
|
446
|
+
|
|
419
447
|
public destroy() {
|
|
420
448
|
if (this.modalOverlay && this.modalOverlay.parentNode) {
|
|
421
449
|
this.modalOverlay.parentNode.removeChild(this.modalOverlay);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Storage as YStorage } from "@matdata/yasgui-utils";
|
|
2
|
+
|
|
3
|
+
export type Theme = "light" | "dark";
|
|
4
|
+
|
|
5
|
+
export class ThemeManager {
|
|
6
|
+
private static STORAGE_KEY = "yasgui_theme";
|
|
7
|
+
private storage: YStorage;
|
|
8
|
+
private currentTheme: Theme;
|
|
9
|
+
private rootElement: HTMLElement;
|
|
10
|
+
|
|
11
|
+
constructor(rootElement: HTMLElement) {
|
|
12
|
+
this.storage = new YStorage("yasgui");
|
|
13
|
+
this.rootElement = rootElement;
|
|
14
|
+
this.currentTheme = this.getStoredTheme() || this.getSystemTheme();
|
|
15
|
+
this.applyTheme(this.currentTheme);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get the currently active theme
|
|
20
|
+
*/
|
|
21
|
+
public getTheme(): Theme {
|
|
22
|
+
return this.currentTheme;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Set and apply a new theme
|
|
27
|
+
*/
|
|
28
|
+
public setTheme(theme: Theme): void {
|
|
29
|
+
this.currentTheme = theme;
|
|
30
|
+
this.applyTheme(theme);
|
|
31
|
+
// Store theme preference with 1 year expiration
|
|
32
|
+
this.storage.set(ThemeManager.STORAGE_KEY, theme, 60 * 60 * 24 * 365, () => {
|
|
33
|
+
console.warn("Failed to store theme preference due to quota exceeded");
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Toggle between light and dark themes
|
|
39
|
+
*/
|
|
40
|
+
public toggleTheme(): Theme {
|
|
41
|
+
const newTheme = this.currentTheme === "light" ? "dark" : "light";
|
|
42
|
+
this.setTheme(newTheme);
|
|
43
|
+
return newTheme;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Apply theme to the DOM
|
|
48
|
+
*/
|
|
49
|
+
private applyTheme(theme: Theme): void {
|
|
50
|
+
// Set data-theme attribute on root element
|
|
51
|
+
this.rootElement.setAttribute("data-theme", theme);
|
|
52
|
+
|
|
53
|
+
// Also set on document root for global styles
|
|
54
|
+
document.documentElement.setAttribute("data-theme", theme);
|
|
55
|
+
|
|
56
|
+
// Update Yasqe CodeMirror theme
|
|
57
|
+
this.updateCodeMirrorTheme(theme);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Update CodeMirror theme for all Yasqe editors
|
|
62
|
+
*/
|
|
63
|
+
private updateCodeMirrorTheme(theme: Theme): void {
|
|
64
|
+
const cmTheme = theme === "dark" ? "material-palenight" : "default";
|
|
65
|
+
|
|
66
|
+
// Find all CodeMirror instances within the root element
|
|
67
|
+
const cmElements = this.rootElement.querySelectorAll(".CodeMirror");
|
|
68
|
+
cmElements.forEach((element) => {
|
|
69
|
+
const cm = (element as any).CodeMirror;
|
|
70
|
+
if (cm && cm.setOption) {
|
|
71
|
+
cm.setOption("theme", cmTheme);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get theme from localStorage
|
|
78
|
+
*/
|
|
79
|
+
private getStoredTheme(): Theme | null {
|
|
80
|
+
const stored = this.storage.get<Theme>(ThemeManager.STORAGE_KEY);
|
|
81
|
+
if (stored === "light" || stored === "dark") {
|
|
82
|
+
return stored;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Detect system theme preference
|
|
89
|
+
*/
|
|
90
|
+
private getSystemTheme(): Theme {
|
|
91
|
+
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
|
|
92
|
+
return "dark";
|
|
93
|
+
}
|
|
94
|
+
return "light";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Listen for system theme changes
|
|
99
|
+
*/
|
|
100
|
+
public listenToSystemTheme(): void {
|
|
101
|
+
if (window.matchMedia) {
|
|
102
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
103
|
+
mediaQuery.addEventListener("change", (e) => {
|
|
104
|
+
// Only update if user hasn't manually set a theme
|
|
105
|
+
if (!this.storage.get(ThemeManager.STORAGE_KEY)) {
|
|
106
|
+
const newTheme = e.matches ? "dark" : "light";
|
|
107
|
+
this.currentTheme = newTheme;
|
|
108
|
+
this.applyTheme(newTheme);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Refresh theme application (useful after new content is loaded)
|
|
116
|
+
*/
|
|
117
|
+
public refresh(): void {
|
|
118
|
+
this.applyTheme(this.currentTheme);
|
|
119
|
+
}
|
|
120
|
+
}
|
package/src/defaults.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -12,8 +12,11 @@ import { default as Yasr, Config as YasrConfig } from "@matdata/yasr";
|
|
|
12
12
|
import { addClass, removeClass } from "@matdata/yasgui-utils";
|
|
13
13
|
import GeoPlugin from "yasgui-geo-tg";
|
|
14
14
|
import GraphPlugin from "@matdata/yasgui-graph-plugin";
|
|
15
|
+
import { ThemeManager, Theme } from "./ThemeManager";
|
|
15
16
|
import "./index.scss";
|
|
17
|
+
import "./themes.scss";
|
|
16
18
|
import "../../yasr/src/scss/global.scss";
|
|
19
|
+
import "codemirror/theme/material-palenight.css";
|
|
17
20
|
|
|
18
21
|
// Register plugins to Yasr
|
|
19
22
|
Yasr.registerPlugin("Geo", GeoPlugin);
|
|
@@ -50,6 +53,8 @@ export interface Config<EndpointObject extends CatalogueItem = CatalogueItem> {
|
|
|
50
53
|
requestConfig: YasguiRequestConfig;
|
|
51
54
|
contextMenuContainer: HTMLElement | undefined;
|
|
52
55
|
nonSslDomain?: string;
|
|
56
|
+
theme?: Theme;
|
|
57
|
+
showThemeToggle?: boolean;
|
|
53
58
|
}
|
|
54
59
|
export type PartialConfig = {
|
|
55
60
|
[P in keyof Config]?: Config[P] extends object ? Partial<Config[P]> : Config[P];
|
|
@@ -96,6 +101,7 @@ export class Yasgui extends EventEmitter {
|
|
|
96
101
|
public tabPanelsEl: HTMLDivElement;
|
|
97
102
|
public config: Config;
|
|
98
103
|
public persistentConfig: PersistentConfig;
|
|
104
|
+
public themeManager: ThemeManager;
|
|
99
105
|
public static Tab = Tab;
|
|
100
106
|
constructor(parent: HTMLElement, config: PartialConfig) {
|
|
101
107
|
super();
|
|
@@ -104,6 +110,13 @@ export class Yasgui extends EventEmitter {
|
|
|
104
110
|
parent.appendChild(this.rootEl);
|
|
105
111
|
|
|
106
112
|
this.config = merge({}, Yasgui.defaults, config);
|
|
113
|
+
|
|
114
|
+
// Initialize theme manager
|
|
115
|
+
this.themeManager = new ThemeManager(this.rootEl);
|
|
116
|
+
if (this.config.theme) {
|
|
117
|
+
this.themeManager.setTheme(this.config.theme);
|
|
118
|
+
}
|
|
119
|
+
this.themeManager.listenToSystemTheme();
|
|
107
120
|
this.persistentConfig = new PersistentConfig(this);
|
|
108
121
|
|
|
109
122
|
this.tabElements = new TabElements(this);
|
|
@@ -348,6 +361,24 @@ export class Yasgui extends EventEmitter {
|
|
|
348
361
|
this.addTab(true, config.tab, { atIndex: config.index });
|
|
349
362
|
}
|
|
350
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Get the current theme
|
|
366
|
+
*/
|
|
367
|
+
public getTheme(): Theme {
|
|
368
|
+
return this.themeManager.getTheme();
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Set the theme
|
|
372
|
+
*/
|
|
373
|
+
public setTheme(theme: Theme): void {
|
|
374
|
+
this.themeManager.setTheme(theme);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Toggle between light and dark themes
|
|
378
|
+
*/
|
|
379
|
+
public toggleTheme(): Theme {
|
|
380
|
+
return this.themeManager.toggleTheme();
|
|
381
|
+
}
|
|
351
382
|
public destroy() {
|
|
352
383
|
this.removeAllListeners();
|
|
353
384
|
this.tabElements.destroy();
|
|
@@ -370,4 +401,5 @@ export function getRandomId() {
|
|
|
370
401
|
return Math.random().toString(36).substring(7);
|
|
371
402
|
}
|
|
372
403
|
|
|
404
|
+
export type { Theme } from "./ThemeManager";
|
|
373
405
|
export default Yasgui;
|
package/src/tab.scss
CHANGED
|
@@ -32,9 +32,13 @@
|
|
|
32
32
|
background: none;
|
|
33
33
|
align-self: center;
|
|
34
34
|
padding-left: 10px;
|
|
35
|
+
padding-right: 10px;
|
|
35
36
|
cursor: pointer;
|
|
36
|
-
color: #505050;
|
|
37
|
-
fill: #505050;
|
|
37
|
+
color: var(--yasgui-button-text, #505050);
|
|
38
|
+
fill: var(--yasgui-button-text, #505050);
|
|
39
|
+
display: flex;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
38
42
|
|
|
39
43
|
.svgImg {
|
|
40
44
|
width: 15px;
|
|
@@ -43,13 +47,13 @@
|
|
|
43
47
|
}
|
|
44
48
|
// IE11 Needs this specified otherwise it will not resize the svg
|
|
45
49
|
svg {
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
width: 20px;
|
|
51
|
+
height: 20px;
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
&:hover {
|
|
51
|
-
color: black;
|
|
52
|
-
fill: black;
|
|
55
|
+
color: var(--yasgui-button-hover, black);
|
|
56
|
+
fill: var(--yasgui-button-hover, black);
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
|