sqlui 0.1.59 → 0.1.60
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.release-version +1 -1
- data/app/server.rb +57 -13
- data/client/resources/sqlui.css +13 -6
- data/client/resources/sqlui.js +296 -73
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45285cfe6706fdfa5bae7abf73f1a287e0238709f2f83a18eda8386c71f4de63
|
4
|
+
data.tar.gz: 9d3eacf4a4306697c4c34d91b51303dd88b376bec29526124d18ec5c4fbe43d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f94e45bf232683013994f63a46eb18257aeaf9d81dd906ccffdbdced023be1743ebca28ff87f34a3b45932757b80516fae979d74a9ab1002affa78a7adb412dc
|
7
|
+
data.tar.gz: 4151e2f3f7d795931abfee848f040e9b6066c4ef4ec9503d0f3cdb586e82d3ced59f50c3eb8744c2adc2847324d275d642e313c0ba825768e452e7a0f8af7b75
|
data/.release-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.60
|
data/app/server.rb
CHANGED
@@ -143,31 +143,60 @@ class Server < Sinatra::Base
|
|
143
143
|
status 200
|
144
144
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
|
146
|
+
stream do |out|
|
147
|
+
database.with_client do |client|
|
148
|
+
begin
|
149
|
+
query_result = execute_query(client, variables, queries)
|
150
|
+
rescue Mysql2::Error => e
|
151
|
+
stacktrace = e.full_message(highlight: false)
|
152
|
+
message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}"
|
153
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
154
|
+
break
|
155
|
+
rescue StandardError => e
|
156
|
+
stacktrace = e.full_message(highlight: false)
|
157
|
+
message = e.message.lines.first&.strip || 'unknown error'
|
158
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
159
|
+
break
|
160
|
+
end
|
161
|
+
|
149
162
|
if query_result
|
150
163
|
json = <<~RES.chomp
|
151
164
|
{
|
152
165
|
"columns": #{query_result.fields.to_json},
|
153
166
|
"column_types": #{MysqlTypes.map_to_google_charts_types(query_result.field_types).to_json},
|
154
|
-
"total_rows": #{query_result.size.to_json},
|
155
167
|
"selection": #{params[:selection].to_json},
|
156
168
|
"query": #{params[:sql].to_json},
|
157
169
|
"rows": [
|
158
170
|
RES
|
159
171
|
out << json
|
160
|
-
|
172
|
+
bytes_written = json.bytesize
|
173
|
+
max_rows_written = false
|
174
|
+
rows_written = 0
|
175
|
+
total_rows = 0
|
161
176
|
query_result.each_with_index do |row, i|
|
177
|
+
total_rows += 1
|
178
|
+
next if max_rows_written
|
179
|
+
|
162
180
|
json = "#{i.zero? ? '' : ','}\n #{row.map { |v| big_decimal_to_float(v) }.to_json}"
|
163
|
-
|
164
|
-
|
181
|
+
bytesize = json.bytesize
|
182
|
+
if bytes_written + bytesize > Sqlui::MAX_BYTES
|
183
|
+
max_rows_written = true
|
184
|
+
next
|
185
|
+
end
|
165
186
|
|
166
187
|
out << json
|
188
|
+
bytes_written += bytesize
|
189
|
+
rows_written += 1
|
190
|
+
|
191
|
+
if rows_written == Sqlui::MAX_ROWS
|
192
|
+
max_rows_written = true
|
193
|
+
next
|
194
|
+
end
|
167
195
|
end
|
168
196
|
out << <<~RES
|
169
197
|
|
170
|
-
]
|
198
|
+
],
|
199
|
+
"total_rows": #{total_rows}
|
171
200
|
}
|
172
201
|
RES
|
173
202
|
else
|
@@ -198,9 +227,21 @@ class Server < Sinatra::Base
|
|
198
227
|
attachment 'result.csv'
|
199
228
|
status 200
|
200
229
|
|
201
|
-
|
202
|
-
|
203
|
-
|
230
|
+
stream do |out|
|
231
|
+
database.with_client do |client|
|
232
|
+
begin
|
233
|
+
query_result = execute_query(client, variables, queries)
|
234
|
+
rescue Mysql2::Error => e
|
235
|
+
stacktrace = e.full_message(highlight: false)
|
236
|
+
message = "ERROR #{e.error_number} (#{e.sql_state}): #{e.message.lines.first&.strip || 'unknown error'}"
|
237
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
238
|
+
break
|
239
|
+
rescue StandardError => e
|
240
|
+
stacktrace = e.full_message(highlight: false)
|
241
|
+
message = e.message.lines.first&.strip || 'unknown error'
|
242
|
+
out << { error: message, stacktrace: stacktrace }.compact.to_json
|
243
|
+
break
|
244
|
+
end
|
204
245
|
out << CSV::Row.new(query_result.fields, query_result.fields, header_row: true).to_s.strip
|
205
246
|
query_result.each do |row|
|
206
247
|
out << "\n#{CSV::Row.new(query_result.fields, row.map { |v| big_decimal_to_float(v) }).to_s.strip}"
|
@@ -230,7 +271,7 @@ class Server < Sinatra::Base
|
|
230
271
|
stacktrace = exception&.full_message(highlight: false)
|
231
272
|
if request.env['HTTP_ACCEPT'] == 'application/json'
|
232
273
|
headers 'Content-Type' => 'application/json; charset=utf-8'
|
233
|
-
message = exception&.message&.lines&.first&.strip || 'unexpected error'
|
274
|
+
message = "error: #{exception&.message&.lines&.first&.strip || 'unexpected error'}"
|
234
275
|
json = { error: message, stacktrace: stacktrace }.compact.to_json
|
235
276
|
body json
|
236
277
|
else
|
@@ -274,7 +315,10 @@ class Server < Sinatra::Base
|
|
274
315
|
variables.each do |name, value|
|
275
316
|
client.query("SET @#{name} = #{value};")
|
276
317
|
end
|
277
|
-
queries.map
|
318
|
+
queries[0..-2].map do |current|
|
319
|
+
client.query(current, stream: true)&.free
|
320
|
+
end
|
321
|
+
client.query(queries[-1], stream: true)
|
278
322
|
end
|
279
323
|
|
280
324
|
def big_decimal_to_float(maybe_big_decimal)
|
data/client/resources/sqlui.css
CHANGED
@@ -233,16 +233,15 @@ p {
|
|
233
233
|
}
|
234
234
|
|
235
235
|
#status-message {
|
236
|
-
|
237
|
-
justify-content:
|
238
|
-
align-content: center;
|
239
|
-
flex-direction: row;
|
236
|
+
min-width: 0;
|
237
|
+
justify-content: left;
|
240
238
|
font-family: Helvetica, sans-serif;
|
241
239
|
white-space: nowrap;
|
242
240
|
overflow: hidden;
|
243
241
|
font-size: 16px;
|
244
242
|
color: #333;
|
245
|
-
margin
|
243
|
+
margin: 0 5px;
|
244
|
+
text-overflow: ellipsis;
|
246
245
|
}
|
247
246
|
|
248
247
|
#result-box, #fetch-sql-box, #saved-box, #graph-box, #structure-box {
|
@@ -255,6 +254,10 @@ table tbody tr td {
|
|
255
254
|
height: 21px;
|
256
255
|
}
|
257
256
|
|
257
|
+
#result-table td, #result-table th {
|
258
|
+
cursor: default;
|
259
|
+
}
|
260
|
+
|
258
261
|
#result-table tbody tr td abbr a {
|
259
262
|
color: #555;
|
260
263
|
cursor: pointer;
|
@@ -264,6 +267,7 @@ table tbody tr td {
|
|
264
267
|
border: 1px dotted #555;
|
265
268
|
font-size: 12px;
|
266
269
|
display: inline-block;
|
270
|
+
user-select: none;
|
267
271
|
}
|
268
272
|
|
269
273
|
#result-table tbody tr td abbr {
|
@@ -358,7 +362,8 @@ thead {
|
|
358
362
|
}
|
359
363
|
|
360
364
|
#status-box {
|
361
|
-
|
365
|
+
width: 100%;
|
366
|
+
padding: 5px 0;
|
362
367
|
display: flex;
|
363
368
|
flex-direction: row;
|
364
369
|
border-top: 1px solid #ddd;
|
@@ -485,11 +490,13 @@ select {
|
|
485
490
|
#pagination-box {
|
486
491
|
display: flex;
|
487
492
|
flex-direction: row;
|
493
|
+
margin: 0 5px;
|
488
494
|
}
|
489
495
|
|
490
496
|
#page-count-box {
|
491
497
|
align-self: center;
|
492
498
|
font-size: 16px;
|
499
|
+
white-space: nowrap;
|
493
500
|
}
|
494
501
|
|
495
502
|
.pagination-button {
|
data/client/resources/sqlui.js
CHANGED
@@ -1514,10 +1514,11 @@
|
|
1514
1514
|
/**
|
1515
1515
|
Create a selection range.
|
1516
1516
|
*/
|
1517
|
-
static range(anchor, head, goalColumn) {
|
1518
|
-
let
|
1519
|
-
|
1520
|
-
|
1517
|
+
static range(anchor, head, goalColumn, bidiLevel) {
|
1518
|
+
let flags = ((goalColumn !== null && goalColumn !== void 0 ? goalColumn : 33554431 /* RangeFlag.NoGoalColumn */) << 5 /* RangeFlag.GoalColumnOffset */) |
|
1519
|
+
(bidiLevel == null ? 3 : Math.min(2, bidiLevel));
|
1520
|
+
return head < anchor ? SelectionRange.create(head, anchor, 16 /* RangeFlag.Inverted */ | 8 /* RangeFlag.AssocAfter */ | flags)
|
1521
|
+
: SelectionRange.create(anchor, head, (head > anchor ? 4 /* RangeFlag.AssocBefore */ : 0) | flags);
|
1521
1522
|
}
|
1522
1523
|
/**
|
1523
1524
|
@internal
|
@@ -4346,6 +4347,26 @@
|
|
4346
4347
|
}
|
4347
4348
|
}
|
4348
4349
|
}
|
4350
|
+
function scrollableParent(dom) {
|
4351
|
+
let doc = dom.ownerDocument;
|
4352
|
+
for (let cur = dom.parentNode; cur;) {
|
4353
|
+
if (cur == doc.body) {
|
4354
|
+
break;
|
4355
|
+
}
|
4356
|
+
else if (cur.nodeType == 1) {
|
4357
|
+
if (cur.scrollHeight > cur.clientHeight || cur.scrollWidth > cur.clientWidth)
|
4358
|
+
return cur;
|
4359
|
+
cur = cur.assignedSlot || cur.parentNode;
|
4360
|
+
}
|
4361
|
+
else if (cur.nodeType == 11) {
|
4362
|
+
cur = cur.host;
|
4363
|
+
}
|
4364
|
+
else {
|
4365
|
+
break;
|
4366
|
+
}
|
4367
|
+
}
|
4368
|
+
return null;
|
4369
|
+
}
|
4349
4370
|
class DOMSelectionState {
|
4350
4371
|
constructor() {
|
4351
4372
|
this.anchorNode = null;
|
@@ -7544,22 +7565,30 @@
|
|
7544
7565
|
this.compositionFirstChange = null;
|
7545
7566
|
this.compositionEndedAt = 0;
|
7546
7567
|
this.mouseSelection = null;
|
7568
|
+
let handleEvent = (handler, event) => {
|
7569
|
+
if (this.ignoreDuringComposition(event))
|
7570
|
+
return;
|
7571
|
+
if (event.type == "keydown" && this.keydown(view, event))
|
7572
|
+
return;
|
7573
|
+
if (this.mustFlushObserver(event))
|
7574
|
+
view.observer.forceFlush();
|
7575
|
+
if (this.runCustomHandlers(event.type, view, event))
|
7576
|
+
event.preventDefault();
|
7577
|
+
else
|
7578
|
+
handler(view, event);
|
7579
|
+
};
|
7547
7580
|
for (let type in handlers) {
|
7548
7581
|
let handler = handlers[type];
|
7549
|
-
view.contentDOM.addEventListener(type,
|
7550
|
-
if (
|
7551
|
-
|
7552
|
-
if (type == "keydown" && this.keydown(view, event))
|
7553
|
-
return;
|
7554
|
-
if (this.mustFlushObserver(event))
|
7555
|
-
view.observer.forceFlush();
|
7556
|
-
if (this.runCustomHandlers(type, view, event))
|
7557
|
-
event.preventDefault();
|
7558
|
-
else
|
7559
|
-
handler(view, event);
|
7582
|
+
view.contentDOM.addEventListener(type, event => {
|
7583
|
+
if (eventBelongsToEditor(view, event))
|
7584
|
+
handleEvent(handler, event);
|
7560
7585
|
}, handlerOptions[type]);
|
7561
7586
|
this.registeredEvents.push(type);
|
7562
7587
|
}
|
7588
|
+
view.scrollDOM.addEventListener("mousedown", (event) => {
|
7589
|
+
if (event.target == view.scrollDOM)
|
7590
|
+
handleEvent(handlers.mousedown, event);
|
7591
|
+
});
|
7563
7592
|
if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
|
7564
7593
|
// On Chrome 102, viewport updates somehow stop wheel-based
|
7565
7594
|
// scrolling. Turning off pointer events during the scroll seems
|
@@ -7716,12 +7745,18 @@
|
|
7716
7745
|
const EmacsyPendingKeys = "dthko";
|
7717
7746
|
// Key codes for modifier keys
|
7718
7747
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
7748
|
+
function dragScrollSpeed(dist) {
|
7749
|
+
return dist * 0.7 + 8;
|
7750
|
+
}
|
7719
7751
|
class MouseSelection {
|
7720
7752
|
constructor(view, startEvent, style, mustSelect) {
|
7721
7753
|
this.view = view;
|
7722
7754
|
this.style = style;
|
7723
7755
|
this.mustSelect = mustSelect;
|
7756
|
+
this.scrollSpeed = { x: 0, y: 0 };
|
7757
|
+
this.scrolling = -1;
|
7724
7758
|
this.lastEvent = startEvent;
|
7759
|
+
this.scrollParent = scrollableParent(view.contentDOM);
|
7725
7760
|
let doc = view.contentDOM.ownerDocument;
|
7726
7761
|
doc.addEventListener("mousemove", this.move = this.move.bind(this));
|
7727
7762
|
doc.addEventListener("mouseup", this.up = this.up.bind(this));
|
@@ -7737,11 +7772,24 @@
|
|
7737
7772
|
}
|
7738
7773
|
}
|
7739
7774
|
move(event) {
|
7775
|
+
var _a;
|
7740
7776
|
if (event.buttons == 0)
|
7741
7777
|
return this.destroy();
|
7742
7778
|
if (this.dragging !== false)
|
7743
7779
|
return;
|
7744
7780
|
this.select(this.lastEvent = event);
|
7781
|
+
let sx = 0, sy = 0;
|
7782
|
+
let rect = ((_a = this.scrollParent) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect())
|
7783
|
+
|| { left: 0, top: 0, right: this.view.win.innerWidth, bottom: this.view.win.innerHeight };
|
7784
|
+
if (event.clientX <= rect.left)
|
7785
|
+
sx = -dragScrollSpeed(rect.left - event.clientX);
|
7786
|
+
else if (event.clientX >= rect.right)
|
7787
|
+
sx = dragScrollSpeed(event.clientX - rect.right);
|
7788
|
+
if (event.clientY <= rect.top)
|
7789
|
+
sy = -dragScrollSpeed(rect.top - event.clientY);
|
7790
|
+
else if (event.clientY >= rect.bottom)
|
7791
|
+
sy = dragScrollSpeed(event.clientY - rect.bottom);
|
7792
|
+
this.setScrollSpeed(sx, sy);
|
7745
7793
|
}
|
7746
7794
|
up(event) {
|
7747
7795
|
if (this.dragging == null)
|
@@ -7751,19 +7799,41 @@
|
|
7751
7799
|
this.destroy();
|
7752
7800
|
}
|
7753
7801
|
destroy() {
|
7802
|
+
this.setScrollSpeed(0, 0);
|
7754
7803
|
let doc = this.view.contentDOM.ownerDocument;
|
7755
7804
|
doc.removeEventListener("mousemove", this.move);
|
7756
7805
|
doc.removeEventListener("mouseup", this.up);
|
7757
7806
|
this.view.inputState.mouseSelection = null;
|
7758
7807
|
}
|
7808
|
+
setScrollSpeed(sx, sy) {
|
7809
|
+
this.scrollSpeed = { x: sx, y: sy };
|
7810
|
+
if (sx || sy) {
|
7811
|
+
if (this.scrolling < 0)
|
7812
|
+
this.scrolling = setInterval(() => this.scroll(), 50);
|
7813
|
+
}
|
7814
|
+
else if (this.scrolling > -1) {
|
7815
|
+
clearInterval(this.scrolling);
|
7816
|
+
this.scrolling = -1;
|
7817
|
+
}
|
7818
|
+
}
|
7819
|
+
scroll() {
|
7820
|
+
if (this.scrollParent) {
|
7821
|
+
this.scrollParent.scrollLeft += this.scrollSpeed.x;
|
7822
|
+
this.scrollParent.scrollTop += this.scrollSpeed.y;
|
7823
|
+
}
|
7824
|
+
else {
|
7825
|
+
this.view.win.scrollBy(this.scrollSpeed.x, this.scrollSpeed.y);
|
7826
|
+
}
|
7827
|
+
if (this.dragging === false)
|
7828
|
+
this.select(this.lastEvent);
|
7829
|
+
}
|
7759
7830
|
select(event) {
|
7760
7831
|
let selection = this.style.get(event, this.extend, this.multiple);
|
7761
7832
|
if (this.mustSelect || !selection.eq(this.view.state.selection) ||
|
7762
7833
|
selection.main.assoc != this.view.state.selection.main.assoc)
|
7763
7834
|
this.view.dispatch({
|
7764
7835
|
selection,
|
7765
|
-
userEvent: "select.pointer"
|
7766
|
-
scrollIntoView: true
|
7836
|
+
userEvent: "select.pointer"
|
7767
7837
|
});
|
7768
7838
|
this.mustSelect = false;
|
7769
7839
|
}
|
@@ -7954,23 +8024,15 @@
|
|
7954
8024
|
function basicMouseSelection(view, event) {
|
7955
8025
|
let start = queryPos(view, event), type = getClickType(event);
|
7956
8026
|
let startSel = view.state.selection;
|
7957
|
-
let last = start, lastEvent = event;
|
7958
8027
|
return {
|
7959
8028
|
update(update) {
|
7960
8029
|
if (update.docChanged) {
|
7961
8030
|
start.pos = update.changes.mapPos(start.pos);
|
7962
8031
|
startSel = startSel.map(update.changes);
|
7963
|
-
lastEvent = null;
|
7964
8032
|
}
|
7965
8033
|
},
|
7966
8034
|
get(event, extend, multiple) {
|
7967
|
-
let cur;
|
7968
|
-
if (lastEvent && event.clientX == lastEvent.clientX && event.clientY == lastEvent.clientY)
|
7969
|
-
cur = last;
|
7970
|
-
else {
|
7971
|
-
cur = last = queryPos(view, event);
|
7972
|
-
lastEvent = event;
|
7973
|
-
}
|
8035
|
+
let cur = queryPos(view, event);
|
7974
8036
|
let range = rangeForClick(view, cur.pos, cur.bias, type);
|
7975
8037
|
if (start.pos != cur.pos && !extend) {
|
7976
8038
|
let startRange = rangeForClick(view, start.pos, start.bias, type);
|
@@ -16967,7 +17029,7 @@
|
|
16967
17029
|
*/
|
16968
17030
|
const defaultHighlightStyle = /*@__PURE__*/HighlightStyle.define([
|
16969
17031
|
{ tag: tags.meta,
|
16970
|
-
color: "#
|
17032
|
+
color: "#404740" },
|
16971
17033
|
{ tag: tags.link,
|
16972
17034
|
textDecoration: "underline" },
|
16973
17035
|
{ tag: tags.heading,
|
@@ -17898,7 +17960,7 @@
|
|
17898
17960
|
function extendSel(view, how) {
|
17899
17961
|
let selection = updateSel(view.state.selection, range => {
|
17900
17962
|
let head = how(range);
|
17901
|
-
return EditorSelection.range(range.anchor, head.head, head.goalColumn);
|
17963
|
+
return EditorSelection.range(range.anchor, head.head, head.goalColumn, head.bidiLevel || undefined);
|
17902
17964
|
});
|
17903
17965
|
if (selection.eq(view.state.selection))
|
17904
17966
|
return false;
|
@@ -20091,6 +20153,7 @@
|
|
20091
20153
|
closeOnBlur: true,
|
20092
20154
|
maxRenderedOptions: 100,
|
20093
20155
|
defaultKeymap: true,
|
20156
|
+
tooltipClass: () => "",
|
20094
20157
|
optionClass: () => "",
|
20095
20158
|
aboveCursor: false,
|
20096
20159
|
icons: true,
|
@@ -20101,6 +20164,7 @@
|
|
20101
20164
|
defaultKeymap: (a, b) => a && b,
|
20102
20165
|
closeOnBlur: (a, b) => a && b,
|
20103
20166
|
icons: (a, b) => a && b,
|
20167
|
+
tooltipClass: (a, b) => c => joinClass(a(c), b(c)),
|
20104
20168
|
optionClass: (a, b) => c => joinClass(a(c), b(c)),
|
20105
20169
|
addToOptions: (a, b) => a.concat(b)
|
20106
20170
|
});
|
@@ -20179,14 +20243,17 @@
|
|
20179
20243
|
key: this
|
20180
20244
|
};
|
20181
20245
|
this.space = null;
|
20246
|
+
this.currentClass = "";
|
20182
20247
|
let cState = view.state.field(stateField);
|
20183
20248
|
let { options, selected } = cState.open;
|
20184
20249
|
let config = view.state.facet(completionConfig);
|
20185
20250
|
this.optionContent = optionContent(config);
|
20186
20251
|
this.optionClass = config.optionClass;
|
20252
|
+
this.tooltipClass = config.tooltipClass;
|
20187
20253
|
this.range = rangeAroundSelected(options.length, selected, config.maxRenderedOptions);
|
20188
20254
|
this.dom = document.createElement("div");
|
20189
20255
|
this.dom.className = "cm-tooltip-autocomplete";
|
20256
|
+
this.updateTooltipClass(view.state);
|
20190
20257
|
this.dom.addEventListener("mousedown", (e) => {
|
20191
20258
|
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
|
20192
20259
|
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options.length) {
|
@@ -20207,12 +20274,25 @@
|
|
20207
20274
|
var _a, _b, _c;
|
20208
20275
|
let cState = update.state.field(this.stateField);
|
20209
20276
|
let prevState = update.startState.field(this.stateField);
|
20277
|
+
this.updateTooltipClass(update.state);
|
20210
20278
|
if (cState != prevState) {
|
20211
20279
|
this.updateSel();
|
20212
20280
|
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
|
20213
20281
|
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
|
20214
20282
|
}
|
20215
20283
|
}
|
20284
|
+
updateTooltipClass(state) {
|
20285
|
+
let cls = this.tooltipClass(state);
|
20286
|
+
if (cls != this.currentClass) {
|
20287
|
+
for (let c of this.currentClass.split(" "))
|
20288
|
+
if (c)
|
20289
|
+
this.dom.classList.remove(c);
|
20290
|
+
for (let c of cls.split(" "))
|
20291
|
+
if (c)
|
20292
|
+
this.dom.classList.add(c);
|
20293
|
+
this.currentClass = cls;
|
20294
|
+
}
|
20295
|
+
}
|
20216
20296
|
positioned(space) {
|
20217
20297
|
this.space = space;
|
20218
20298
|
if (this.info)
|
@@ -20473,13 +20553,13 @@
|
|
20473
20553
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
20474
20554
|
active = this.active;
|
20475
20555
|
let open = this.open;
|
20556
|
+
if (open && tr.docChanged)
|
20557
|
+
open = open.map(tr.changes);
|
20476
20558
|
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
20477
20559
|
!sameResults(active, this.active))
|
20478
|
-
open = CompletionDialog.build(active, state, this.id,
|
20560
|
+
open = CompletionDialog.build(active, state, this.id, open, conf);
|
20479
20561
|
else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
|
20480
20562
|
open = null;
|
20481
|
-
else if (open && tr.docChanged)
|
20482
|
-
open = open.map(tr.changes);
|
20483
20563
|
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
|
20484
20564
|
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
|
20485
20565
|
for (let effect of tr.effects)
|
@@ -24310,6 +24390,7 @@
|
|
24310
24390
|
let drag = new Drag();
|
24311
24391
|
|
24312
24392
|
document.addEventListener('mousedown', (event) => {
|
24393
|
+
if (event.button !== 0) return
|
24313
24394
|
if (!event.target.classList.contains('col-resizer')) return
|
24314
24395
|
|
24315
24396
|
drag = new Drag();
|
@@ -24366,10 +24447,11 @@
|
|
24366
24447
|
});
|
24367
24448
|
|
24368
24449
|
class ResizeTable extends HTMLTableElement {
|
24369
|
-
constructor (columns, rows, cellRenderer) {
|
24450
|
+
constructor (columns, rows, headerRenderer, cellRenderer) {
|
24370
24451
|
super();
|
24371
24452
|
|
24372
24453
|
this.columns = columns;
|
24454
|
+
this.headerRenderer = headerRenderer;
|
24373
24455
|
this.cellRenderer = cellRenderer;
|
24374
24456
|
|
24375
24457
|
this.style.tableLayout = 'auto';
|
@@ -24408,7 +24490,11 @@
|
|
24408
24490
|
resizerElement.dataset.colId = colElement.dataset.colId;
|
24409
24491
|
contentWrapperElement.appendChild(resizerElement);
|
24410
24492
|
|
24411
|
-
|
24493
|
+
if (headerRenderer) {
|
24494
|
+
nameElement.appendChild(headerRenderer(headerElement, index, column));
|
24495
|
+
} else {
|
24496
|
+
nameElement.innerText = column;
|
24497
|
+
}
|
24412
24498
|
});
|
24413
24499
|
|
24414
24500
|
headerTrElement.appendChild(document.createElement('th'));
|
@@ -24475,21 +24561,21 @@
|
|
24475
24561
|
this.tbodyElement = tbodyElement;
|
24476
24562
|
|
24477
24563
|
let highlight = false;
|
24478
|
-
rows.forEach(function (row) {
|
24564
|
+
rows.forEach(function (row, rowIndex) {
|
24479
24565
|
const rowElement = document.createElement('tr');
|
24480
24566
|
if (highlight) {
|
24481
24567
|
rowElement.classList.add('highlighted-row');
|
24482
24568
|
}
|
24483
24569
|
highlight = !highlight;
|
24484
24570
|
tbodyElement.appendChild(rowElement);
|
24485
|
-
row.forEach(function (value,
|
24571
|
+
row.forEach(function (value, columnIndex) {
|
24572
|
+
const cellElement = document.createElement('td');
|
24486
24573
|
if (cellRenderer) {
|
24487
|
-
cellRenderer(
|
24574
|
+
cellElement.appendChild(cellRenderer(cellElement, rowIndex, columnIndex, value));
|
24488
24575
|
} else {
|
24489
|
-
const cellElement = document.createElement('td');
|
24490
24576
|
cellElement.innerText = value;
|
24491
|
-
rowElement.appendChild(cellElement);
|
24492
24577
|
}
|
24578
|
+
rowElement.appendChild(cellElement);
|
24493
24579
|
});
|
24494
24580
|
rowElement.appendChild(document.createElement('td'));
|
24495
24581
|
});
|
@@ -24502,6 +24588,96 @@
|
|
24502
24588
|
|
24503
24589
|
customElements.define('resize-table', ResizeTable, { extends: 'table' });
|
24504
24590
|
|
24591
|
+
function styleInject(css, ref) {
|
24592
|
+
if ( ref === void 0 ) ref = {};
|
24593
|
+
var insertAt = ref.insertAt;
|
24594
|
+
|
24595
|
+
if (!css || typeof document === 'undefined') { return; }
|
24596
|
+
|
24597
|
+
var head = document.head || document.getElementsByTagName('head')[0];
|
24598
|
+
var style = document.createElement('style');
|
24599
|
+
style.type = 'text/css';
|
24600
|
+
|
24601
|
+
if (insertAt === 'top') {
|
24602
|
+
if (head.firstChild) {
|
24603
|
+
head.insertBefore(style, head.firstChild);
|
24604
|
+
} else {
|
24605
|
+
head.appendChild(style);
|
24606
|
+
}
|
24607
|
+
} else {
|
24608
|
+
head.appendChild(style);
|
24609
|
+
}
|
24610
|
+
|
24611
|
+
if (style.styleSheet) {
|
24612
|
+
style.styleSheet.cssText = css;
|
24613
|
+
} else {
|
24614
|
+
style.appendChild(document.createTextNode(css));
|
24615
|
+
}
|
24616
|
+
}
|
24617
|
+
|
24618
|
+
var css_248z = ".popup_popup-wrapper__huCTg {\n position: absolute;\n background: rgba(0, 0, 0, 0.3);\n width: 100%;\n height: 100%;\n top: 0;\n left: 0;\n z-index: 2;\n}\n\n.popup_popup-content__iGYws {\n position: absolute;\n top: 50px;\n left: 50px;\n height: calc(100% - 100px);\n width: calc(100% - 100px);\n background: #fff;\n border: 1px solid #888;\n}\n\n.popup_popup-pre__gF3-Q {\n position: absolute;\n top: 40px;\n left: 0;\n height: calc(100% - 112px);\n width: calc(100% - 20px);\n overflow: auto;\n font-size: 18px;\n border-top: 1px solid #ddd;\n border-bottom: 1px solid #ddd;\n margin: 0px;\n padding: 10px;\n background: #fff;\n font-family: monospace;\n}\n\n.popup_popup-pre__gF3-Q:focus {\n outline: none;\n}\n.popup_popup-close__3GBw3 {\n position: absolute;\n right: 20px;\n bottom: 10px;\n width: 100px;\n height: 30px;\n cursor: pointer;\n font-family: Helvetica, sans-serif;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n color: #333;\n border: 1px solid #888;\n background: #fff;\n}\n\n.popup_popup-close__3GBw3:active {\n background-color: #e6e6e6;\n}\n\n.popup_popup-title__W2N2J {\n position: absolute;\n left: 60px;\n top: 60px;\n font-family: Helvetica, sans-serif;\n font-size: 18px;\n color: #333;\n font-weight: bold;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n width: calc(100% - 120px);\n}\n";
|
24619
|
+
var styles = {"popup-wrapper":"popup_popup-wrapper__huCTg","popup-content":"popup_popup-content__iGYws","popup-pre":"popup_popup-pre__gF3-Q","popup-close":"popup_popup-close__3GBw3","popup-title":"popup_popup-title__W2N2J"};
|
24620
|
+
styleInject(css_248z);
|
24621
|
+
|
24622
|
+
function createPopup (title, text) {
|
24623
|
+
const wrapperElement = document.createElement('div');
|
24624
|
+
wrapperElement.id = 'popup';
|
24625
|
+
wrapperElement.classList.add(styles['popup-wrapper']);
|
24626
|
+
document.body.appendChild(wrapperElement);
|
24627
|
+
|
24628
|
+
const contentElement = document.createElement('div');
|
24629
|
+
contentElement.classList.add(styles['popup-content']);
|
24630
|
+
wrapperElement.appendChild(contentElement);
|
24631
|
+
|
24632
|
+
const closeElement = document.createElement('input');
|
24633
|
+
closeElement.id = 'popup-close';
|
24634
|
+
closeElement.classList.add(styles['popup-close']);
|
24635
|
+
closeElement.type = 'button';
|
24636
|
+
closeElement.value = 'Close';
|
24637
|
+
contentElement.appendChild(closeElement);
|
24638
|
+
|
24639
|
+
closeElement.addEventListener('click', (event) => {
|
24640
|
+
document.body.removeChild(wrapperElement);
|
24641
|
+
});
|
24642
|
+
|
24643
|
+
const titleElement = document.createElement('div');
|
24644
|
+
titleElement.classList.add(styles['popup-title']);
|
24645
|
+
titleElement.innerText = title;
|
24646
|
+
wrapperElement.appendChild(titleElement);
|
24647
|
+
|
24648
|
+
const preElement = document.createElement('pre');
|
24649
|
+
preElement.id = 'popup-pre';
|
24650
|
+
preElement.classList.add(styles['popup-pre']);
|
24651
|
+
contentElement.appendChild(preElement);
|
24652
|
+
preElement.innerText = text;
|
24653
|
+
|
24654
|
+
wrapperElement.addEventListener('click', () => {
|
24655
|
+
document.body.removeChild(wrapperElement);
|
24656
|
+
});
|
24657
|
+
|
24658
|
+
contentElement.addEventListener('click', (event) => {
|
24659
|
+
event.stopPropagation();
|
24660
|
+
});
|
24661
|
+
|
24662
|
+
closeElement.focus();
|
24663
|
+
}
|
24664
|
+
|
24665
|
+
document.addEventListener('keydown', (event) => {
|
24666
|
+
if (event.code === 'Escape') {
|
24667
|
+
const wrapperElement = document.getElementById('popup');
|
24668
|
+
if (wrapperElement) {
|
24669
|
+
event.preventDefault();
|
24670
|
+
document.body.removeChild(wrapperElement);
|
24671
|
+
}
|
24672
|
+
} else if (event.code === 'Tab') {
|
24673
|
+
const wrapperElement = document.getElementById('popup');
|
24674
|
+
if (wrapperElement) {
|
24675
|
+
event.preventDefault();
|
24676
|
+
document.getElementById('popup-close').focus();
|
24677
|
+
}
|
24678
|
+
}
|
24679
|
+
});
|
24680
|
+
|
24505
24681
|
/* global google */
|
24506
24682
|
|
24507
24683
|
const PAGE_SIZE = 100;
|
@@ -24630,6 +24806,7 @@
|
|
24630
24806
|
dropdownContent.classList.remove('submit-dropdown-content-show');
|
24631
24807
|
}
|
24632
24808
|
});
|
24809
|
+
|
24633
24810
|
window.editorView = createEditor(parent, window.metadata, onSubmit, onShiftSubmit);
|
24634
24811
|
}
|
24635
24812
|
|
@@ -24857,13 +25034,11 @@
|
|
24857
25034
|
}
|
24858
25035
|
rows.push(row);
|
24859
25036
|
}
|
24860
|
-
const cellRenderer = function (
|
24861
|
-
const cellElement = document.createElement('td');
|
25037
|
+
const cellRenderer = function (cellElement, _rowIndex, _columnIndex, value) {
|
24862
25038
|
cellElement.style.textAlign = (typeof value) === 'string' ? 'left' : 'right';
|
24863
|
-
|
24864
|
-
rowElement.appendChild(cellElement);
|
25039
|
+
return document.createTextNode(value)
|
24865
25040
|
};
|
24866
|
-
columnsElement.appendChild(new ResizeTable(columns, rows, cellRenderer));
|
25041
|
+
columnsElement.appendChild(new ResizeTable(columns, rows, null, cellRenderer));
|
24867
25042
|
}
|
24868
25043
|
|
24869
25044
|
const indexEntries = Object.entries(table.indexes);
|
@@ -24883,13 +25058,11 @@
|
|
24883
25058
|
rows.push(row);
|
24884
25059
|
}
|
24885
25060
|
}
|
24886
|
-
const cellRenderer = function (
|
24887
|
-
const cellElement = document.createElement('td');
|
25061
|
+
const cellRenderer = function (cellElement, _rowIndex, _columnIndex, value) {
|
24888
25062
|
cellElement.style.textAlign = (typeof value) === 'string' ? 'left' : 'right';
|
24889
|
-
|
24890
|
-
rowElement.appendChild(cellElement);
|
25063
|
+
return document.createTextNode(value)
|
24891
25064
|
};
|
24892
|
-
indexesElement.appendChild(new ResizeTable(columns, rows, cellRenderer));
|
25065
|
+
indexesElement.appendChild(new ResizeTable(columns, rows, null, cellRenderer));
|
24893
25066
|
}
|
24894
25067
|
});
|
24895
25068
|
window.structureLoaded = true;
|
@@ -25130,6 +25303,9 @@
|
|
25130
25303
|
}
|
25131
25304
|
}
|
25132
25305
|
displaySqlFetch(sqlFetch);
|
25306
|
+
}).catch(function (error) {
|
25307
|
+
setSqlFetchError(sqlFetch, error);
|
25308
|
+
displaySqlFetch(sqlFetch);
|
25133
25309
|
});
|
25134
25310
|
} else {
|
25135
25311
|
response.text().then((result) => {
|
@@ -25137,20 +25313,28 @@
|
|
25137
25313
|
sqlFetch.errorMessage = 'failed to execute query';
|
25138
25314
|
sqlFetch.errorDetails = result;
|
25139
25315
|
displaySqlFetch(sqlFetch);
|
25316
|
+
}).catch(function (error) {
|
25317
|
+
setSqlFetchError(sqlFetch, error);
|
25318
|
+
displaySqlFetch(sqlFetch);
|
25140
25319
|
});
|
25141
25320
|
}
|
25142
25321
|
})
|
25143
25322
|
.catch(function (error) {
|
25144
|
-
|
25145
|
-
sqlFetch.endedAt = window.performance.now();
|
25146
|
-
sqlFetch.state = 'error';
|
25147
|
-
sqlFetch.errorMessage = 'failed to execute query';
|
25148
|
-
sqlFetch.errorDetails = error;
|
25149
|
-
}
|
25323
|
+
setSqlFetchError(sqlFetch, error);
|
25150
25324
|
displaySqlFetch(sqlFetch);
|
25151
25325
|
});
|
25152
25326
|
}
|
25153
25327
|
|
25328
|
+
function setSqlFetchError (sqlFetch, error) {
|
25329
|
+
// Ignore the error unless pending since the error may be the result of aborting.
|
25330
|
+
if (sqlFetch.state === 'pending') {
|
25331
|
+
sqlFetch.endedAt = window.performance.now();
|
25332
|
+
sqlFetch.state = 'error';
|
25333
|
+
sqlFetch.errorMessage = 'failed to execute query';
|
25334
|
+
sqlFetch.errorDetails = error;
|
25335
|
+
}
|
25336
|
+
}
|
25337
|
+
|
25154
25338
|
function parseSqlVariables (params) {
|
25155
25339
|
return Object.fromEntries(
|
25156
25340
|
Array.from(params).filter(([key]) => {
|
@@ -25272,10 +25456,17 @@
|
|
25272
25456
|
return abbrElement
|
25273
25457
|
};
|
25274
25458
|
|
25275
|
-
const resultCellRenderer = function (
|
25459
|
+
const resultCellRenderer = function (cellElement, rowIndex, columnIndex, value) {
|
25276
25460
|
const column = window.sqlFetch.result.columns[columnIndex];
|
25277
25461
|
const columnType = window.sqlFetch.result.column_types[columnIndex];
|
25278
25462
|
|
25463
|
+
cellElement.dataset.column = columnIndex.toString();
|
25464
|
+
cellElement.dataset.row = rowIndex.toString();
|
25465
|
+
|
25466
|
+
if (typeof value === 'string' && value.indexOf('\n') >= 0) {
|
25467
|
+
value = value.replaceAll('\n', '¶');
|
25468
|
+
}
|
25469
|
+
|
25279
25470
|
if (value && window.metadata.columns[column]?.links?.length > 0) {
|
25280
25471
|
const linksElement = document.createElement('div');
|
25281
25472
|
window.metadata.columns[column].links.forEach((link) => {
|
@@ -25292,17 +25483,22 @@
|
|
25292
25483
|
wrapperElement.appendChild(linksElement);
|
25293
25484
|
wrapperElement.appendChild(textElement);
|
25294
25485
|
|
25295
|
-
|
25296
|
-
columnElement.appendChild(wrapperElement);
|
25297
|
-
rowElement.appendChild(columnElement);
|
25486
|
+
return wrapperElement
|
25298
25487
|
} else {
|
25299
|
-
const cellElement = document.createElement('td');
|
25300
25488
|
cellElement.style.textAlign = columnType === 'string' ? 'left' : 'right';
|
25301
|
-
|
25302
|
-
rowElement.appendChild(cellElement);
|
25489
|
+
return document.createTextNode(value)
|
25303
25490
|
}
|
25304
25491
|
};
|
25305
25492
|
|
25493
|
+
function resultHeaderRenderer (headerElement, columnIndex, value) {
|
25494
|
+
headerElement.dataset.column = columnIndex.toString();
|
25495
|
+
|
25496
|
+
if (typeof value === 'string' && value.indexOf('\n') >= 0) {
|
25497
|
+
value = value.replaceAll('\n', '¶');
|
25498
|
+
}
|
25499
|
+
return document.createTextNode(value)
|
25500
|
+
}
|
25501
|
+
|
25306
25502
|
function displaySqlFetchInResultTab (fetch) {
|
25307
25503
|
if (fetch.state === 'pending' || fetch.spinner === 'always') {
|
25308
25504
|
clearResultBox();
|
@@ -25352,13 +25548,45 @@
|
|
25352
25548
|
} else {
|
25353
25549
|
clearResultBox();
|
25354
25550
|
const resultBoxElement = document.getElementById('result-box');
|
25355
|
-
tableElement = new ResizeTable(fetch.result.columns, rows, resultCellRenderer);
|
25551
|
+
tableElement = new ResizeTable(fetch.result.columns, rows, resultHeaderRenderer, resultCellRenderer);
|
25356
25552
|
tableElement.id = 'result-table';
|
25553
|
+
registerTableCellPopup(tableElement);
|
25357
25554
|
resultBoxElement.appendChild(tableElement);
|
25358
25555
|
}
|
25359
25556
|
tableElement.setAttribute('data-page', fetch.page);
|
25360
25557
|
}
|
25361
25558
|
|
25559
|
+
function registerTableCellPopup (tableElement) {
|
25560
|
+
const listener = (event) => {
|
25561
|
+
if (event.which === 1 && event.detail === 2) {
|
25562
|
+
let node = event.target;
|
25563
|
+
while (!['td', 'th', 'table'].includes(node.tagName.toLowerCase()) && node.parentNode) {
|
25564
|
+
node = node.parentNode;
|
25565
|
+
}
|
25566
|
+
if (node.tagName.toLowerCase() === 'td') {
|
25567
|
+
if (event.type === 'mousedown') {
|
25568
|
+
const row = parseInt(node.dataset.row);
|
25569
|
+
const column = parseInt(node.dataset.column);
|
25570
|
+
const title = window.sqlFetch.result.columns[column].replaceAll('\n', '¶');
|
25571
|
+
createPopup(title, window.sqlFetch.result.rows[row][column]);
|
25572
|
+
}
|
25573
|
+
event.preventDefault();
|
25574
|
+
} else if (node.tagName.toLowerCase() === 'th') {
|
25575
|
+
if (event.type === 'mousedown') {
|
25576
|
+
const column = parseInt(node.dataset.column);
|
25577
|
+
const value = window.sqlFetch.result.columns[column];
|
25578
|
+
const title = value.replaceAll('\n', '¶');
|
25579
|
+
createPopup(title, value);
|
25580
|
+
}
|
25581
|
+
event.preventDefault();
|
25582
|
+
}
|
25583
|
+
}
|
25584
|
+
};
|
25585
|
+
// We only open the popup on mouseup but we need to preventDefault on mousedown to avoid the clicked text from
|
25586
|
+
// being highlighted.
|
25587
|
+
addEventListener(tableElement, 'mouseup', listener);
|
25588
|
+
addEventListener(tableElement, 'mousedown', listener);
|
25589
|
+
}
|
25362
25590
|
function disableDownloadButtons () {
|
25363
25591
|
document.getElementById('submit-dropdown-button-download-csv').classList.add('disabled');
|
25364
25592
|
document.getElementById('submit-dropdown-button-copy-csv').classList.add('disabled');
|
@@ -25389,15 +25617,10 @@
|
|
25389
25617
|
}
|
25390
25618
|
|
25391
25619
|
function displaySqlFetchError (message, details) {
|
25392
|
-
let statusMessage = 'error: ' + message;
|
25393
|
-
if (statusMessage.length > 90) {
|
25394
|
-
statusMessage = statusMessage.substring(0, 90) + '…';
|
25395
|
-
}
|
25396
25620
|
if (details) {
|
25397
|
-
console.log(
|
25398
|
-
statusMessage += ' (check console)';
|
25621
|
+
console.log(details);
|
25399
25622
|
}
|
25400
|
-
setStatus(
|
25623
|
+
setStatus(message);
|
25401
25624
|
}
|
25402
25625
|
|
25403
25626
|
function clearSpinner () {
|
@@ -25505,14 +25728,14 @@
|
|
25505
25728
|
const elapsed = Math.round(100 * (sqlFetch.getDuration() / 1000.0)) / 100;
|
25506
25729
|
|
25507
25730
|
let message;
|
25508
|
-
if (result.
|
25509
|
-
message =
|
25731
|
+
if (result.rows.length === 1) {
|
25732
|
+
message = `1 row returned after ${elapsed}s`;
|
25510
25733
|
} else {
|
25511
|
-
message = `${result.
|
25734
|
+
message = `${result.rows.length.toLocaleString()} rows returned after ${elapsed}s`;
|
25512
25735
|
}
|
25513
25736
|
|
25514
25737
|
if (result.total_rows > result.rows.length) {
|
25515
|
-
message += ` (truncated
|
25738
|
+
message += ` (truncated from ${result.total_rows.toLocaleString()})`;
|
25516
25739
|
}
|
25517
25740
|
setStatus(message);
|
25518
25741
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sqlui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.60
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Dower
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: airbrake
|