sqlui 0.1.59 → 0.1.60
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.
- 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
|