@luxalgo/vela 0.6.0 → 0.6.2
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/dist/{DataProvider-DNx9ntBD.d.ts → DataProvider-CNmk84SH.d.cts} +27 -3
- package/dist/{DataProvider-Ce6PuCzQ.d.cts → DataProvider-DHX4x6-r.d.ts} +27 -3
- package/dist/{chunk-CA3N3PYT.js → chunk-7D6YIF34.js} +2151 -2464
- package/dist/{chunk-ZNL2X6U2.js → chunk-7Y7CJ7EN.js} +1 -1
- package/dist/{chunk-EMS6PO2T.js → chunk-PN5KWFZ4.js} +6 -1
- package/dist/chunk-QHZ7IXFL.js +2598 -0
- package/dist/chunk-TP56QRMK.js +503 -0
- package/dist/{chunk-7ROONW6H.js → chunk-U5KCQHAC.js} +141 -10
- package/dist/{contributions-Dlc7ZWZz.d.cts → contributions-DDpv7hEL.d.cts} +6 -2
- package/dist/{contributions-DToMhiw3.d.ts → contributions-ciV6Neyd.d.ts} +6 -2
- package/dist/{history-BZADxpKx.d.cts → history-CFqZm5re.d.ts} +21 -3
- package/dist/{history-BuHXJZJ2.d.ts → history-ChoT34nz.d.cts} +21 -3
- package/dist/index.cjs +3724 -2742
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +4 -4
- package/dist/{options-BlQ00Fju.d.ts → options-BqGeFHtp.d.cts} +15 -1
- package/dist/{options-BlQ00Fju.d.cts → options-BqGeFHtp.d.ts} +15 -1
- package/dist/{plugin-BqjEa5r3.d.ts → plugin-C97gUCVf.d.ts} +7 -3
- package/dist/{plugin-BNPLOQO1.d.cts → plugin-CSb_sTiN.d.cts} +7 -3
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.ts +4 -4
- package/dist/plugin.js +2 -2
- package/dist/providers/binance.d.cts +2 -2
- package/dist/providers/binance.d.ts +2 -2
- package/dist/providers/coinbase.d.cts +2 -2
- package/dist/providers/coinbase.d.ts +2 -2
- package/dist/providers/hyperliquid.d.cts +2 -2
- package/dist/providers/hyperliquid.d.ts +2 -2
- package/dist/ui.cjs +1001 -247
- package/dist/ui.d.cts +258 -11
- package/dist/ui.d.ts +258 -11
- package/dist/ui.js +3 -3
- package/dist/vela.global.js +12358 -3961
- package/dist/vela.global.min.js +479 -124
- package/dist/widget.cjs +3463 -2867
- package/dist/widget.d.cts +20 -6
- package/dist/widget.d.ts +20 -6
- package/dist/widget.js +56 -11
- package/dist/workspace.cjs +3137 -2538
- package/dist/workspace.d.cts +22 -5
- package/dist/workspace.d.ts +22 -5
- package/dist/workspace.js +56 -8
- package/package.json +1 -1
- package/dist/chunk-OGRQW3M6.js +0 -1328
- package/dist/chunk-QWIEKZRE.js +0 -1042
package/dist/ui.cjs
CHANGED
|
@@ -73,6 +73,11 @@ function isDarkColor(color) {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// src/ui/styles.ts
|
|
76
|
+
function overlayScrollbarCss(selector, width = 8) {
|
|
77
|
+
return `${selector}::-webkit-scrollbar{width:${width}px;height:${width}px;}${selector}::-webkit-scrollbar-thumb{background:var(--vela-scroll);border-radius:4px;border:2px solid transparent;background-clip:padding-box;}${selector}::-webkit-scrollbar-track{background:transparent;}${selector}::-webkit-scrollbar-button{display:none;width:0;height:0;}`;
|
|
78
|
+
}
|
|
79
|
+
var FIELD_FOCUS_CSS = "outline:none;transition:border-color .12s ease,box-shadow .12s ease;";
|
|
80
|
+
var FIELD_FOCUS_RING = "border-color:var(--vela-focus);box-shadow:0 0 0 3px var(--vela-focus-soft);";
|
|
76
81
|
function injectStyles(id, css, root = document) {
|
|
77
82
|
if (typeof document === "undefined") return;
|
|
78
83
|
const host = root instanceof Document ? root.head : root;
|
|
@@ -920,6 +925,223 @@ var Menu = class {
|
|
|
920
925
|
this.root.destroy();
|
|
921
926
|
}
|
|
922
927
|
};
|
|
928
|
+
|
|
929
|
+
// src/ui/components/popover/controller.ts
|
|
930
|
+
function insetRect(r, inset) {
|
|
931
|
+
return {
|
|
932
|
+
left: r.left + inset,
|
|
933
|
+
top: r.top + inset,
|
|
934
|
+
right: r.right - inset,
|
|
935
|
+
bottom: r.bottom - inset,
|
|
936
|
+
width: r.width - inset * 2,
|
|
937
|
+
height: r.height - inset * 2
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
function viewportRect(width, height, inset) {
|
|
941
|
+
return {
|
|
942
|
+
left: inset,
|
|
943
|
+
top: inset,
|
|
944
|
+
right: width - inset,
|
|
945
|
+
bottom: height - inset,
|
|
946
|
+
width: width - inset * 2,
|
|
947
|
+
height: height - inset * 2
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
function intersectRects(a, b) {
|
|
951
|
+
const left = Math.max(a.left, b.left);
|
|
952
|
+
const top = Math.max(a.top, b.top);
|
|
953
|
+
const right = Math.min(a.right, b.right);
|
|
954
|
+
const bottom = Math.min(a.bottom, b.bottom);
|
|
955
|
+
return { left, top, right, bottom, width: right - left, height: bottom - top };
|
|
956
|
+
}
|
|
957
|
+
function placePopover(a) {
|
|
958
|
+
let left = a.align === "end" ? a.trigger.right - a.pop.width : a.trigger.left;
|
|
959
|
+
const below = a.trigger.bottom + a.gap;
|
|
960
|
+
const above = a.trigger.top - a.pop.height - a.gap;
|
|
961
|
+
const fitsBelow = below + a.pop.height <= a.clamp.bottom;
|
|
962
|
+
const fitsAbove = above >= a.clamp.top;
|
|
963
|
+
let top = below;
|
|
964
|
+
if (!fitsBelow && (fitsAbove || a.trigger.top - a.clamp.top > a.clamp.bottom - a.trigger.bottom)) {
|
|
965
|
+
top = Math.max(a.clamp.top, above);
|
|
966
|
+
}
|
|
967
|
+
if (left + a.pop.width > a.clamp.right) left = a.clamp.right - a.pop.width;
|
|
968
|
+
if (left < a.clamp.left) left = a.clamp.left;
|
|
969
|
+
if (top + a.pop.height > a.clamp.bottom) top = a.clamp.bottom - a.pop.height;
|
|
970
|
+
if (top < a.clamp.top) top = a.clamp.top;
|
|
971
|
+
return {
|
|
972
|
+
left: Math.round(left - a.originX),
|
|
973
|
+
top: Math.round(top - a.originY)
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
function popoverController(opts = {}) {
|
|
977
|
+
return {
|
|
978
|
+
gap: opts.gap ?? 4,
|
|
979
|
+
align: opts.align ?? "start",
|
|
980
|
+
matchWidth: opts.matchWidth ?? false,
|
|
981
|
+
position: opts.position ?? "fixed",
|
|
982
|
+
boundaryInset: opts.boundaryInset ?? 0,
|
|
983
|
+
viewportInset: opts.viewportInset ?? 6,
|
|
984
|
+
onClose: opts.onClose
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
// src/ui/components/popover/styles.ts
|
|
989
|
+
var POPOVER_STYLE_ID = "vela-ui-popover";
|
|
990
|
+
var POPOVER_CSS = `
|
|
991
|
+
.vela-popover {
|
|
992
|
+
position: fixed;
|
|
993
|
+
z-index: var(--vela-z-popover);
|
|
994
|
+
box-sizing: border-box;
|
|
995
|
+
/* Never serve as a scroll anchor: the shell is portaled and re-placed between
|
|
996
|
+
gestures, and anchoring against it jumps the scroller underneath. */
|
|
997
|
+
overflow-anchor: none;
|
|
998
|
+
}
|
|
999
|
+
.vela-popover[data-position='absolute'] { position: absolute; }
|
|
1000
|
+
.vela-popover[hidden] { display: none !important; }
|
|
1001
|
+
`;
|
|
1002
|
+
|
|
1003
|
+
// src/ui/components/popover/view.ts
|
|
1004
|
+
var open = null;
|
|
1005
|
+
function closeOpenPopovers() {
|
|
1006
|
+
open?.hide();
|
|
1007
|
+
}
|
|
1008
|
+
function isPopoverOpen() {
|
|
1009
|
+
return open !== null;
|
|
1010
|
+
}
|
|
1011
|
+
function openPopoverTrigger() {
|
|
1012
|
+
return open?.trigger ?? null;
|
|
1013
|
+
}
|
|
1014
|
+
var POPOVER_DISMISS_FLAG = "__velaPopoverDismiss";
|
|
1015
|
+
function markPopoverDismiss(e) {
|
|
1016
|
+
e[POPOVER_DISMISS_FLAG] = true;
|
|
1017
|
+
}
|
|
1018
|
+
function eventDismissedPopover(e) {
|
|
1019
|
+
return e[POPOVER_DISMISS_FLAG] === true;
|
|
1020
|
+
}
|
|
1021
|
+
function toRect(r) {
|
|
1022
|
+
return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
|
|
1023
|
+
}
|
|
1024
|
+
var Popover = class {
|
|
1025
|
+
constructor(opts) {
|
|
1026
|
+
this.onOutside = null;
|
|
1027
|
+
this.onKey = null;
|
|
1028
|
+
this.onReflow = null;
|
|
1029
|
+
this.shown = false;
|
|
1030
|
+
const doc = opts.trigger.ownerDocument;
|
|
1031
|
+
injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
|
|
1032
|
+
this.trigger = opts.trigger;
|
|
1033
|
+
this.host = opts.host ?? doc.body;
|
|
1034
|
+
this.ctrl = popoverController(opts);
|
|
1035
|
+
this.boundary = opts.boundary ?? "viewport";
|
|
1036
|
+
this.theme = opts.theme;
|
|
1037
|
+
this.el = doc.createElement("div");
|
|
1038
|
+
this.el.className = "vela-popover vela-ui-layer" + (opts.className ? ` ${opts.className}` : "");
|
|
1039
|
+
this.el.dataset.position = this.ctrl.position;
|
|
1040
|
+
if (opts.zIndex !== void 0) this.el.style.zIndex = String(opts.zIndex);
|
|
1041
|
+
this.el.addEventListener("pointerdown", (e) => e.stopPropagation());
|
|
1042
|
+
if (opts.content instanceof Node) this.el.appendChild(opts.content);
|
|
1043
|
+
else if (typeof opts.content === "function") opts.content(this.el);
|
|
1044
|
+
}
|
|
1045
|
+
get open() {
|
|
1046
|
+
return this.shown;
|
|
1047
|
+
}
|
|
1048
|
+
get position() {
|
|
1049
|
+
return this.ctrl.position;
|
|
1050
|
+
}
|
|
1051
|
+
get align() {
|
|
1052
|
+
return this.ctrl.align;
|
|
1053
|
+
}
|
|
1054
|
+
show() {
|
|
1055
|
+
if (this.shown) {
|
|
1056
|
+
this.place();
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
if (open && open !== this) open.hide();
|
|
1060
|
+
ensureUIHost(this.el, this.theme);
|
|
1061
|
+
this.host.appendChild(this.el);
|
|
1062
|
+
this.shown = true;
|
|
1063
|
+
open = this;
|
|
1064
|
+
this.place();
|
|
1065
|
+
const onOutside = (ev) => {
|
|
1066
|
+
const t = ev.target;
|
|
1067
|
+
if (this.el.contains(t) || this.trigger.contains(t)) return;
|
|
1068
|
+
markPopoverDismiss(ev);
|
|
1069
|
+
this.hide();
|
|
1070
|
+
};
|
|
1071
|
+
const onKey = (ev) => {
|
|
1072
|
+
if (ev.key !== "Escape") return;
|
|
1073
|
+
ev.preventDefault();
|
|
1074
|
+
ev.stopPropagation();
|
|
1075
|
+
this.hide();
|
|
1076
|
+
};
|
|
1077
|
+
const onReflow = () => this.place();
|
|
1078
|
+
setTimeout(() => document.addEventListener("pointerdown", onOutside, true), 0);
|
|
1079
|
+
document.addEventListener("keydown", onKey, true);
|
|
1080
|
+
window.addEventListener("resize", onReflow, true);
|
|
1081
|
+
document.addEventListener("scroll", onReflow, true);
|
|
1082
|
+
this.onOutside = onOutside;
|
|
1083
|
+
this.onKey = onKey;
|
|
1084
|
+
this.onReflow = onReflow;
|
|
1085
|
+
}
|
|
1086
|
+
hide() {
|
|
1087
|
+
if (!this.shown) return;
|
|
1088
|
+
if (this.onOutside) document.removeEventListener("pointerdown", this.onOutside, true);
|
|
1089
|
+
if (this.onKey) document.removeEventListener("keydown", this.onKey, true);
|
|
1090
|
+
if (this.onReflow) {
|
|
1091
|
+
window.removeEventListener("resize", this.onReflow, true);
|
|
1092
|
+
document.removeEventListener("scroll", this.onReflow, true);
|
|
1093
|
+
}
|
|
1094
|
+
this.onOutside = null;
|
|
1095
|
+
this.onKey = null;
|
|
1096
|
+
this.onReflow = null;
|
|
1097
|
+
this.el.remove();
|
|
1098
|
+
this.shown = false;
|
|
1099
|
+
if (open === this) open = null;
|
|
1100
|
+
this.ctrl.onClose?.();
|
|
1101
|
+
}
|
|
1102
|
+
/** Show if closed, hide if this instance is the open popover. */
|
|
1103
|
+
toggle() {
|
|
1104
|
+
if (this.shown) this.hide();
|
|
1105
|
+
else this.show();
|
|
1106
|
+
}
|
|
1107
|
+
destroy() {
|
|
1108
|
+
this.hide();
|
|
1109
|
+
}
|
|
1110
|
+
reposition() {
|
|
1111
|
+
if (this.shown) this.place();
|
|
1112
|
+
}
|
|
1113
|
+
clampRect() {
|
|
1114
|
+
const view = viewportRect(window.innerWidth, window.innerHeight, this.ctrl.viewportInset);
|
|
1115
|
+
const bound = this.readBoundary();
|
|
1116
|
+
if (!bound) return view;
|
|
1117
|
+
const inset = insetRect(bound, this.ctrl.boundaryInset);
|
|
1118
|
+
return intersectRects(view, inset);
|
|
1119
|
+
}
|
|
1120
|
+
readBoundary() {
|
|
1121
|
+
const b = this.boundary;
|
|
1122
|
+
if (b === "viewport") return null;
|
|
1123
|
+
const raw = typeof b === "function" ? b() : b.getBoundingClientRect();
|
|
1124
|
+
return raw ? toRect(raw) : null;
|
|
1125
|
+
}
|
|
1126
|
+
place() {
|
|
1127
|
+
const ar = this.trigger.getBoundingClientRect();
|
|
1128
|
+
if (this.ctrl.matchWidth) {
|
|
1129
|
+
this.el.style.minWidth = `${Math.round(Math.max(this.el.offsetWidth, ar.width))}px`;
|
|
1130
|
+
}
|
|
1131
|
+
const origin = this.ctrl.position === "absolute" ? this.host.getBoundingClientRect() : { left: 0, top: 0 };
|
|
1132
|
+
const pos = placePopover({
|
|
1133
|
+
trigger: toRect(ar),
|
|
1134
|
+
pop: { width: this.el.offsetWidth, height: this.el.offsetHeight },
|
|
1135
|
+
gap: this.ctrl.gap,
|
|
1136
|
+
align: this.ctrl.align,
|
|
1137
|
+
clamp: this.clampRect(),
|
|
1138
|
+
originX: origin.left,
|
|
1139
|
+
originY: origin.top
|
|
1140
|
+
});
|
|
1141
|
+
this.el.style.left = `${pos.left}px`;
|
|
1142
|
+
this.el.style.top = `${pos.top}px`;
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
923
1145
|
function dialogController(opts = {}) {
|
|
924
1146
|
return {
|
|
925
1147
|
machine: dialog__namespace.machine,
|
|
@@ -936,7 +1158,7 @@ function dialogController(opts = {}) {
|
|
|
936
1158
|
}
|
|
937
1159
|
|
|
938
1160
|
// src/ui/components/dialog/styles.ts
|
|
939
|
-
var DIALOG_STYLE_ID = "vela-ui-dialog";
|
|
1161
|
+
var DIALOG_STYLE_ID = "vela-ui-dialog-5";
|
|
940
1162
|
var DIALOG_CSS = `
|
|
941
1163
|
.vela-dialog-backdrop {
|
|
942
1164
|
position: fixed;
|
|
@@ -955,6 +1177,12 @@ var DIALOG_CSS = `
|
|
|
955
1177
|
padding-top: 10vh;
|
|
956
1178
|
z-index: var(--vela-z-dialog);
|
|
957
1179
|
}
|
|
1180
|
+
.vela-dialog-backdrop[data-contained],
|
|
1181
|
+
.vela-dialog-positioner[data-contained] { position: absolute; }
|
|
1182
|
+
.vela-dialog-positioner[data-align='center'] {
|
|
1183
|
+
align-items: center;
|
|
1184
|
+
padding-top: 0;
|
|
1185
|
+
}
|
|
958
1186
|
.vela-dialog {
|
|
959
1187
|
background: var(--vela-surface);
|
|
960
1188
|
color: var(--vela-fg);
|
|
@@ -997,14 +1225,56 @@ var DIALOG_CSS = `
|
|
|
997
1225
|
font-size: 15px;
|
|
998
1226
|
}
|
|
999
1227
|
.vela-dialog-close:hover { background: var(--vela-hover); color: var(--vela-fg-bright); }
|
|
1000
|
-
.vela-dialog-body { padding: var(--vela-space-4); overflow: auto; }
|
|
1001
|
-
.vela-dialog-body
|
|
1002
|
-
.vela-dialog-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1228
|
+
.vela-dialog-body { padding: var(--vela-space-4); overflow: auto; min-height: 0; flex: 1 1 auto; }
|
|
1229
|
+
.vela-dialog-body[data-flush] { padding: 0; overflow: hidden; display: flex; flex-direction: column; }
|
|
1230
|
+
.vela-dialog-footer { flex: 0 0 auto; }
|
|
1231
|
+
.vela-dialog--settings {
|
|
1232
|
+
width: fit-content;
|
|
1233
|
+
min-width: min(560px, 94vw);
|
|
1234
|
+
max-width: min(720px, 94vw);
|
|
1235
|
+
max-height: 70vh;
|
|
1236
|
+
font-size: 13px;
|
|
1237
|
+
cursor: default;
|
|
1238
|
+
}
|
|
1239
|
+
.vela-dialog--form {
|
|
1240
|
+
width: fit-content;
|
|
1241
|
+
min-width: min(380px, 90%);
|
|
1242
|
+
max-width: min(640px, 94%);
|
|
1243
|
+
max-height: 82%;
|
|
1244
|
+
font-size: 14px;
|
|
1245
|
+
}
|
|
1246
|
+
/* Form dialogs (indicator inputs, drawing settings): the tab strip / body owns the
|
|
1247
|
+
line under the header, and the footer carries its own top delimiter. */
|
|
1248
|
+
.vela-dialog--form .vela-dialog-header { align-items: flex-start; padding: 16px 20px 20px; border-bottom: none; }
|
|
1249
|
+
.vela-dialog--form .vela-dialog-title { font-size: 20px; line-height: 28px; }
|
|
1250
|
+
.vela-dialog--form .vela-dialog-footer {
|
|
1251
|
+
padding: 16px 20px;
|
|
1252
|
+
display: flex;
|
|
1253
|
+
justify-content: flex-end;
|
|
1254
|
+
gap: 12px;
|
|
1255
|
+
border-top: 1px solid var(--vela-border);
|
|
1007
1256
|
}
|
|
1257
|
+
.vela-dialog-btn {
|
|
1258
|
+
cursor: pointer;
|
|
1259
|
+
height: 34px;
|
|
1260
|
+
padding: 0 11px;
|
|
1261
|
+
border-radius: 6px;
|
|
1262
|
+
border: 1px solid var(--vela-border-strong);
|
|
1263
|
+
background: transparent;
|
|
1264
|
+
color: var(--vela-fg);
|
|
1265
|
+
font-weight: 400;
|
|
1266
|
+
font-size: 14px;
|
|
1267
|
+
font-family: inherit;
|
|
1268
|
+
transition: background var(--vela-dur-fast) ease, color var(--vela-dur-fast) ease, opacity var(--vela-dur-fast) ease, border-color var(--vela-dur-fast) ease;
|
|
1269
|
+
}
|
|
1270
|
+
.vela-dialog-btn:hover { background: var(--vela-hover); color: var(--vela-fg-bright); border-color: var(--vela-fg-muted); }
|
|
1271
|
+
.vela-dialog-btn-primary { border-color: var(--vela-selected-bg); background: var(--vela-selected-bg); color: var(--vela-selected-fg); }
|
|
1272
|
+
.vela-dialog-btn-primary:hover { background: var(--vela-selected-bg); color: var(--vela-selected-fg); opacity: 0.85; border-color: var(--vela-selected-bg); }
|
|
1273
|
+
${overlayScrollbarCss(".vela-dialog-body")}
|
|
1274
|
+
/* Scrollers NESTED in a dialog (a flush body's grid, a tab pane) get the same thin
|
|
1275
|
+
overlay bars \u2014 the two-class selectors below stay overridable by more specific
|
|
1276
|
+
per-dialog sheets. */
|
|
1277
|
+
${overlayScrollbarCss(".vela-dialog *")}
|
|
1008
1278
|
/* \u2500\u2500 mobile chrome: dialogs fill the shell \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
1009
1279
|
Inside a shell in the mobile size class ([data-layout='mobile'] on the widget root,
|
|
1010
1280
|
which is position:relative) every kit dialog presents fullscreen: desktop cards are
|
|
@@ -1039,9 +1309,20 @@ var Dialog = class {
|
|
|
1039
1309
|
if (opts.dimBackdrop !== true) this.backdrop.classList.add("vela-dialog-backdrop--clear");
|
|
1040
1310
|
this.positioner = doc.createElement("div");
|
|
1041
1311
|
this.positioner.className = "vela-dialog-positioner vela-ui-layer";
|
|
1312
|
+
if (opts.contained) {
|
|
1313
|
+
this.backdrop.dataset.contained = "";
|
|
1314
|
+
this.positioner.dataset.contained = "";
|
|
1315
|
+
}
|
|
1316
|
+
this.positioner.dataset.align = opts.align ?? "top";
|
|
1042
1317
|
this.panel = doc.createElement("div");
|
|
1043
1318
|
this.panel.className = "vela-dialog";
|
|
1319
|
+
if (opts.className) {
|
|
1320
|
+
for (const cls of opts.className.split(/\s+/)) if (cls) this.panel.classList.add(cls);
|
|
1321
|
+
}
|
|
1044
1322
|
this.panel.tabIndex = -1;
|
|
1323
|
+
this.panel.addEventListener("keydown", (e) => {
|
|
1324
|
+
if (e.key !== "Escape") e.stopPropagation();
|
|
1325
|
+
});
|
|
1045
1326
|
const header = doc.createElement("div");
|
|
1046
1327
|
header.className = "vela-dialog-header";
|
|
1047
1328
|
if (opts.draggable) {
|
|
@@ -1062,20 +1343,45 @@ var Dialog = class {
|
|
|
1062
1343
|
});
|
|
1063
1344
|
header.addEventListener("pointerup", () => dragging = false);
|
|
1064
1345
|
}
|
|
1346
|
+
if (opts.headerStart) header.appendChild(opts.headerStart);
|
|
1065
1347
|
const title = doc.createElement("div");
|
|
1066
1348
|
title.className = "vela-dialog-title";
|
|
1067
1349
|
title.textContent = opts.title ?? "";
|
|
1350
|
+
this.titleEl = title;
|
|
1068
1351
|
const close = doc.createElement("button");
|
|
1069
1352
|
close.className = "vela-dialog-close";
|
|
1070
1353
|
close.appendChild(iconEl("close", doc));
|
|
1071
1354
|
header.append(title, close);
|
|
1072
1355
|
this.body = doc.createElement("div");
|
|
1073
1356
|
this.body.className = "vela-dialog-body";
|
|
1357
|
+
if (opts.flush) this.body.dataset.flush = "";
|
|
1074
1358
|
if (opts.content instanceof Node) this.body.appendChild(opts.content);
|
|
1075
1359
|
else if (typeof opts.content === "function") opts.content(this.body);
|
|
1076
1360
|
this.panel.append(header, this.body);
|
|
1361
|
+
if (opts.footer) {
|
|
1362
|
+
const foot = doc.createElement("div");
|
|
1363
|
+
foot.className = "vela-dialog-footer";
|
|
1364
|
+
if (opts.footer instanceof Node) foot.appendChild(opts.footer);
|
|
1365
|
+
else opts.footer(foot);
|
|
1366
|
+
this.panel.appendChild(foot);
|
|
1367
|
+
this.footer = foot;
|
|
1368
|
+
} else {
|
|
1369
|
+
this.footer = null;
|
|
1370
|
+
}
|
|
1077
1371
|
this.positioner.appendChild(this.panel);
|
|
1078
1372
|
host.append(this.backdrop, this.positioner);
|
|
1373
|
+
if (opts.closeOnBackdrop) {
|
|
1374
|
+
const closeOn = (e) => {
|
|
1375
|
+
if (e.target !== this.backdrop && e.target !== this.positioner) return;
|
|
1376
|
+
if (isPopoverOpen() || eventDismissedPopover(e)) {
|
|
1377
|
+
closeOpenPopovers();
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
this.hide();
|
|
1381
|
+
};
|
|
1382
|
+
this.backdrop.addEventListener("pointerdown", closeOn);
|
|
1383
|
+
this.positioner.addEventListener("pointerdown", closeOn);
|
|
1384
|
+
}
|
|
1079
1385
|
this.ctrl = dialogController({
|
|
1080
1386
|
...opts,
|
|
1081
1387
|
// Mobile chrome (fullscreen presentation): opening must never land focus on
|
|
@@ -1109,7 +1415,14 @@ var Dialog = class {
|
|
|
1109
1415
|
hide() {
|
|
1110
1416
|
this.ctrl.connect(this.handle.service).setOpen(false);
|
|
1111
1417
|
}
|
|
1418
|
+
contains(node) {
|
|
1419
|
+
if (node == null) return false;
|
|
1420
|
+
if (this.panel.contains(node) || this.backdrop.contains(node) || this.positioner.contains(node)) return true;
|
|
1421
|
+
const el = node instanceof Element ? node : node.parentElement;
|
|
1422
|
+
return el?.closest(".vela-popover, .vela-menu") != null;
|
|
1423
|
+
}
|
|
1112
1424
|
destroy() {
|
|
1425
|
+
if (this.open) this.hide();
|
|
1113
1426
|
this.handle.stop();
|
|
1114
1427
|
this.backdrop.remove();
|
|
1115
1428
|
this.positioner.remove();
|
|
@@ -1396,219 +1709,10 @@ var Drawer = class {
|
|
|
1396
1709
|
}
|
|
1397
1710
|
};
|
|
1398
1711
|
|
|
1399
|
-
// src/ui/components/
|
|
1400
|
-
function
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
top: r.top + inset,
|
|
1404
|
-
right: r.right - inset,
|
|
1405
|
-
bottom: r.bottom - inset,
|
|
1406
|
-
width: r.width - inset * 2,
|
|
1407
|
-
height: r.height - inset * 2
|
|
1408
|
-
};
|
|
1409
|
-
}
|
|
1410
|
-
function viewportRect(width, height, inset) {
|
|
1411
|
-
return {
|
|
1412
|
-
left: inset,
|
|
1413
|
-
top: inset,
|
|
1414
|
-
right: width - inset,
|
|
1415
|
-
bottom: height - inset,
|
|
1416
|
-
width: width - inset * 2,
|
|
1417
|
-
height: height - inset * 2
|
|
1418
|
-
};
|
|
1419
|
-
}
|
|
1420
|
-
function intersectRects(a, b) {
|
|
1421
|
-
const left = Math.max(a.left, b.left);
|
|
1422
|
-
const top = Math.max(a.top, b.top);
|
|
1423
|
-
const right = Math.min(a.right, b.right);
|
|
1424
|
-
const bottom = Math.min(a.bottom, b.bottom);
|
|
1425
|
-
return { left, top, right, bottom, width: right - left, height: bottom - top };
|
|
1426
|
-
}
|
|
1427
|
-
function placePopover(a) {
|
|
1428
|
-
let left = a.align === "end" ? a.trigger.right - a.pop.width : a.trigger.left;
|
|
1429
|
-
const below = a.trigger.bottom + a.gap;
|
|
1430
|
-
const above = a.trigger.top - a.pop.height - a.gap;
|
|
1431
|
-
const fitsBelow = below + a.pop.height <= a.clamp.bottom;
|
|
1432
|
-
const fitsAbove = above >= a.clamp.top;
|
|
1433
|
-
let top = below;
|
|
1434
|
-
if (!fitsBelow && (fitsAbove || a.trigger.top - a.clamp.top > a.clamp.bottom - a.trigger.bottom)) {
|
|
1435
|
-
top = Math.max(a.clamp.top, above);
|
|
1436
|
-
}
|
|
1437
|
-
if (left + a.pop.width > a.clamp.right) left = a.clamp.right - a.pop.width;
|
|
1438
|
-
if (left < a.clamp.left) left = a.clamp.left;
|
|
1439
|
-
if (top + a.pop.height > a.clamp.bottom) top = a.clamp.bottom - a.pop.height;
|
|
1440
|
-
if (top < a.clamp.top) top = a.clamp.top;
|
|
1441
|
-
return {
|
|
1442
|
-
left: Math.round(left - a.originX),
|
|
1443
|
-
top: Math.round(top - a.originY)
|
|
1444
|
-
};
|
|
1445
|
-
}
|
|
1446
|
-
function popoverController(opts = {}) {
|
|
1447
|
-
return {
|
|
1448
|
-
gap: opts.gap ?? 4,
|
|
1449
|
-
align: opts.align ?? "start",
|
|
1450
|
-
matchWidth: opts.matchWidth ?? false,
|
|
1451
|
-
position: opts.position ?? "fixed",
|
|
1452
|
-
boundaryInset: opts.boundaryInset ?? 0,
|
|
1453
|
-
viewportInset: opts.viewportInset ?? 6,
|
|
1454
|
-
onClose: opts.onClose
|
|
1455
|
-
};
|
|
1456
|
-
}
|
|
1457
|
-
|
|
1458
|
-
// src/ui/components/popover/styles.ts
|
|
1459
|
-
var POPOVER_STYLE_ID = "vela-ui-popover";
|
|
1460
|
-
var POPOVER_CSS = `
|
|
1461
|
-
.vela-popover {
|
|
1462
|
-
position: fixed;
|
|
1463
|
-
z-index: var(--vela-z-popover);
|
|
1464
|
-
box-sizing: border-box;
|
|
1465
|
-
/* Never serve as a scroll anchor: the shell is portaled and re-placed between
|
|
1466
|
-
gestures, and anchoring against it jumps the scroller underneath. */
|
|
1467
|
-
overflow-anchor: none;
|
|
1468
|
-
}
|
|
1469
|
-
.vela-popover[data-position='absolute'] { position: absolute; }
|
|
1470
|
-
.vela-popover[hidden] { display: none !important; }
|
|
1471
|
-
`;
|
|
1472
|
-
|
|
1473
|
-
// src/ui/components/popover/view.ts
|
|
1474
|
-
var open = null;
|
|
1475
|
-
function closeOpenPopovers() {
|
|
1476
|
-
open?.hide();
|
|
1477
|
-
}
|
|
1478
|
-
function isPopoverOpen() {
|
|
1479
|
-
return open !== null;
|
|
1480
|
-
}
|
|
1481
|
-
function openPopoverTrigger() {
|
|
1482
|
-
return open?.trigger ?? null;
|
|
1483
|
-
}
|
|
1484
|
-
function toRect(r) {
|
|
1485
|
-
return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
|
|
1486
|
-
}
|
|
1487
|
-
var Popover = class {
|
|
1488
|
-
constructor(opts) {
|
|
1489
|
-
this.onOutside = null;
|
|
1490
|
-
this.onKey = null;
|
|
1491
|
-
this.onReflow = null;
|
|
1492
|
-
this.shown = false;
|
|
1493
|
-
const doc = opts.trigger.ownerDocument;
|
|
1494
|
-
injectStyles(POPOVER_STYLE_ID, POPOVER_CSS, doc);
|
|
1495
|
-
this.trigger = opts.trigger;
|
|
1496
|
-
this.host = opts.host ?? doc.body;
|
|
1497
|
-
this.ctrl = popoverController(opts);
|
|
1498
|
-
this.boundary = opts.boundary ?? "viewport";
|
|
1499
|
-
this.theme = opts.theme;
|
|
1500
|
-
this.el = doc.createElement("div");
|
|
1501
|
-
this.el.className = "vela-popover vela-ui-layer" + (opts.className ? ` ${opts.className}` : "");
|
|
1502
|
-
this.el.dataset.position = this.ctrl.position;
|
|
1503
|
-
if (opts.zIndex !== void 0) this.el.style.zIndex = String(opts.zIndex);
|
|
1504
|
-
this.el.addEventListener("pointerdown", (e) => e.stopPropagation());
|
|
1505
|
-
if (opts.content instanceof Node) this.el.appendChild(opts.content);
|
|
1506
|
-
else if (typeof opts.content === "function") opts.content(this.el);
|
|
1507
|
-
}
|
|
1508
|
-
get open() {
|
|
1509
|
-
return this.shown;
|
|
1510
|
-
}
|
|
1511
|
-
get position() {
|
|
1512
|
-
return this.ctrl.position;
|
|
1513
|
-
}
|
|
1514
|
-
get align() {
|
|
1515
|
-
return this.ctrl.align;
|
|
1516
|
-
}
|
|
1517
|
-
show() {
|
|
1518
|
-
if (this.shown) {
|
|
1519
|
-
this.place();
|
|
1520
|
-
return;
|
|
1521
|
-
}
|
|
1522
|
-
if (open && open !== this) open.hide();
|
|
1523
|
-
ensureUIHost(this.el, this.theme);
|
|
1524
|
-
this.host.appendChild(this.el);
|
|
1525
|
-
this.shown = true;
|
|
1526
|
-
open = this;
|
|
1527
|
-
this.place();
|
|
1528
|
-
const onOutside = (ev) => {
|
|
1529
|
-
const t = ev.target;
|
|
1530
|
-
if (this.el.contains(t) || this.trigger.contains(t)) return;
|
|
1531
|
-
this.hide();
|
|
1532
|
-
};
|
|
1533
|
-
const onKey = (ev) => {
|
|
1534
|
-
if (ev.key !== "Escape") return;
|
|
1535
|
-
ev.preventDefault();
|
|
1536
|
-
ev.stopPropagation();
|
|
1537
|
-
this.hide();
|
|
1538
|
-
};
|
|
1539
|
-
const onReflow = () => this.place();
|
|
1540
|
-
setTimeout(() => document.addEventListener("pointerdown", onOutside, true), 0);
|
|
1541
|
-
document.addEventListener("keydown", onKey, true);
|
|
1542
|
-
window.addEventListener("resize", onReflow, true);
|
|
1543
|
-
document.addEventListener("scroll", onReflow, true);
|
|
1544
|
-
this.onOutside = onOutside;
|
|
1545
|
-
this.onKey = onKey;
|
|
1546
|
-
this.onReflow = onReflow;
|
|
1547
|
-
}
|
|
1548
|
-
hide() {
|
|
1549
|
-
if (!this.shown) return;
|
|
1550
|
-
if (this.onOutside) document.removeEventListener("pointerdown", this.onOutside, true);
|
|
1551
|
-
if (this.onKey) document.removeEventListener("keydown", this.onKey, true);
|
|
1552
|
-
if (this.onReflow) {
|
|
1553
|
-
window.removeEventListener("resize", this.onReflow, true);
|
|
1554
|
-
document.removeEventListener("scroll", this.onReflow, true);
|
|
1555
|
-
}
|
|
1556
|
-
this.onOutside = null;
|
|
1557
|
-
this.onKey = null;
|
|
1558
|
-
this.onReflow = null;
|
|
1559
|
-
this.el.remove();
|
|
1560
|
-
this.shown = false;
|
|
1561
|
-
if (open === this) open = null;
|
|
1562
|
-
this.ctrl.onClose?.();
|
|
1563
|
-
}
|
|
1564
|
-
/** Show if closed, hide if this instance is the open popover. */
|
|
1565
|
-
toggle() {
|
|
1566
|
-
if (this.shown) this.hide();
|
|
1567
|
-
else this.show();
|
|
1568
|
-
}
|
|
1569
|
-
destroy() {
|
|
1570
|
-
this.hide();
|
|
1571
|
-
}
|
|
1572
|
-
reposition() {
|
|
1573
|
-
if (this.shown) this.place();
|
|
1574
|
-
}
|
|
1575
|
-
clampRect() {
|
|
1576
|
-
const view = viewportRect(window.innerWidth, window.innerHeight, this.ctrl.viewportInset);
|
|
1577
|
-
const bound = this.readBoundary();
|
|
1578
|
-
if (!bound) return view;
|
|
1579
|
-
const inset = insetRect(bound, this.ctrl.boundaryInset);
|
|
1580
|
-
return intersectRects(view, inset);
|
|
1581
|
-
}
|
|
1582
|
-
readBoundary() {
|
|
1583
|
-
const b = this.boundary;
|
|
1584
|
-
if (b === "viewport") return null;
|
|
1585
|
-
const raw = typeof b === "function" ? b() : b.getBoundingClientRect();
|
|
1586
|
-
return raw ? toRect(raw) : null;
|
|
1587
|
-
}
|
|
1588
|
-
place() {
|
|
1589
|
-
const ar = this.trigger.getBoundingClientRect();
|
|
1590
|
-
if (this.ctrl.matchWidth) {
|
|
1591
|
-
this.el.style.minWidth = `${Math.round(Math.max(this.el.offsetWidth, ar.width))}px`;
|
|
1592
|
-
}
|
|
1593
|
-
const origin = this.ctrl.position === "absolute" ? this.host.getBoundingClientRect() : { left: 0, top: 0 };
|
|
1594
|
-
const pos = placePopover({
|
|
1595
|
-
trigger: toRect(ar),
|
|
1596
|
-
pop: { width: this.el.offsetWidth, height: this.el.offsetHeight },
|
|
1597
|
-
gap: this.ctrl.gap,
|
|
1598
|
-
align: this.ctrl.align,
|
|
1599
|
-
clamp: this.clampRect(),
|
|
1600
|
-
originX: origin.left,
|
|
1601
|
-
originY: origin.top
|
|
1602
|
-
});
|
|
1603
|
-
this.el.style.left = `${pos.left}px`;
|
|
1604
|
-
this.el.style.top = `${pos.top}px`;
|
|
1605
|
-
}
|
|
1606
|
-
};
|
|
1607
|
-
|
|
1608
|
-
// src/ui/components/switch/controller.ts
|
|
1609
|
-
function switchController(opts = {}) {
|
|
1610
|
-
let checked = opts.checked ?? false;
|
|
1611
|
-
const disabled = opts.disabled ?? false;
|
|
1712
|
+
// src/ui/components/switch/controller.ts
|
|
1713
|
+
function switchController(opts = {}) {
|
|
1714
|
+
let checked = opts.checked ?? false;
|
|
1715
|
+
const disabled = opts.disabled ?? false;
|
|
1612
1716
|
return {
|
|
1613
1717
|
get checked() {
|
|
1614
1718
|
return checked;
|
|
@@ -1739,14 +1843,18 @@ function selectController(opts) {
|
|
|
1739
1843
|
}
|
|
1740
1844
|
|
|
1741
1845
|
// src/ui/components/select/styles.ts
|
|
1742
|
-
var SELECT_STYLE_ID = "vela-ui-select";
|
|
1846
|
+
var SELECT_STYLE_ID = "vela-ui-select-3";
|
|
1743
1847
|
var SELECT_CSS = `
|
|
1744
1848
|
.vela-select { position: relative; display: inline-block; min-width: 0; }
|
|
1745
1849
|
.vela-select[data-fill] { width: 100%; }
|
|
1850
|
+
/* Closed trigger matches NumberInput / TextField (100px). Long labels ellipsis;
|
|
1851
|
+
the open list still sizes to the longest item. */
|
|
1852
|
+
.vela-select:not([data-fill]) { width: 100px; flex: none; justify-self: start; }
|
|
1746
1853
|
.vela-select-trigger {
|
|
1747
1854
|
display: flex;
|
|
1748
1855
|
align-items: center;
|
|
1749
1856
|
width: 100%;
|
|
1857
|
+
min-width: 0;
|
|
1750
1858
|
box-sizing: border-box;
|
|
1751
1859
|
height: 34px;
|
|
1752
1860
|
padding: 0 26px 0 8px;
|
|
@@ -1763,10 +1871,6 @@ var SELECT_CSS = `
|
|
|
1763
1871
|
}
|
|
1764
1872
|
.vela-select-trigger:hover { border-color: var(--vela-fg-muted); }
|
|
1765
1873
|
.vela-select-trigger:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
|
|
1766
|
-
/* As a grid item (chart-settings rows dissolve via display:contents into a shared
|
|
1767
|
-
max-content column) the wrap must NOT stretch to the column: it sizes to its own
|
|
1768
|
-
widest option, like the native select it replaces. */
|
|
1769
|
-
.vela-select[data-size='sm'] { max-width: 200px; flex: 0 0 auto; justify-self: start; }
|
|
1770
1874
|
.vela-select[data-size='sm'] .vela-select-trigger {
|
|
1771
1875
|
height: 28px;
|
|
1772
1876
|
background: var(--vela-surface-elev);
|
|
@@ -1776,12 +1880,10 @@ var SELECT_CSS = `
|
|
|
1776
1880
|
box-shadow: none;
|
|
1777
1881
|
}
|
|
1778
1882
|
.vela-select[data-size='sm'] .vela-select-trigger:focus { box-shadow: none; border-color: var(--vela-fg-muted); }
|
|
1779
|
-
.vela-select-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1780
|
-
/*
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
.vela-select-sizer { visibility: hidden; height: 0; overflow: hidden; font-size: 14px; }
|
|
1784
|
-
.vela-select[data-size='sm'] .vela-select-sizer { font-size: 13px; }
|
|
1883
|
+
.vela-select-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1884
|
+
/* Compact toolbar selects still hug the widest option (no 100px kit column). */
|
|
1885
|
+
.vela-select[data-size='sm']:not([data-fill]) { width: auto; max-width: 200px; }
|
|
1886
|
+
.vela-select-sizer { visibility: hidden; height: 0; overflow: hidden; font-size: 13px; }
|
|
1785
1887
|
.vela-select-sizer span { display: block; height: 0; white-space: nowrap; padding: 0 26px 0 8px; border-inline: 1px solid transparent; }
|
|
1786
1888
|
.vela-select-chevron {
|
|
1787
1889
|
position: absolute;
|
|
@@ -1796,6 +1898,7 @@ var SELECT_CSS = `
|
|
|
1796
1898
|
}
|
|
1797
1899
|
.vela-select-chevron .vela-icon, .vela-select-chevron svg { width: 12px; height: 12px; display: block; }
|
|
1798
1900
|
.vela-select-list {
|
|
1901
|
+
width: max-content;
|
|
1799
1902
|
background: var(--vela-bg);
|
|
1800
1903
|
color: var(--vela-fg);
|
|
1801
1904
|
border: none;
|
|
@@ -1806,11 +1909,11 @@ var SELECT_CSS = `
|
|
|
1806
1909
|
overflow: hidden;
|
|
1807
1910
|
}
|
|
1808
1911
|
.vela-select-list[data-size='sm'] { font-size: 13px; background: var(--vela-surface-overlay); }
|
|
1809
|
-
.vela-select-items { max-height: none; overflow: hidden; }
|
|
1912
|
+
.vela-select-items { width: max-content; min-width: 100%; max-height: none; overflow: hidden; }
|
|
1810
1913
|
.vela-select-list.is-scroll { display: flex; align-items: stretch; gap: 2px; }
|
|
1811
1914
|
.vela-select-list.is-scroll .vela-select-items {
|
|
1812
1915
|
flex: 1 1 auto;
|
|
1813
|
-
min-width:
|
|
1916
|
+
min-width: min-content;
|
|
1814
1917
|
max-height: 240px;
|
|
1815
1918
|
overflow-y: auto;
|
|
1816
1919
|
scrollbar-width: none;
|
|
@@ -1820,7 +1923,9 @@ var SELECT_CSS = `
|
|
|
1820
1923
|
.vela-select-thumb { width: 3px; border-radius: 2px; background: var(--vela-scroll); }
|
|
1821
1924
|
.vela-select-item {
|
|
1822
1925
|
display: block;
|
|
1823
|
-
width:
|
|
1926
|
+
width: auto;
|
|
1927
|
+
min-width: 100%;
|
|
1928
|
+
white-space: nowrap;
|
|
1824
1929
|
text-align: left;
|
|
1825
1930
|
padding: 7px 10px;
|
|
1826
1931
|
border: none;
|
|
@@ -1947,15 +2052,17 @@ var Select = class {
|
|
|
1947
2052
|
chevron.appendChild(iconEl("chevron-down", doc));
|
|
1948
2053
|
btn.append(label, chevron);
|
|
1949
2054
|
wrap.appendChild(btn);
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
const
|
|
1955
|
-
|
|
1956
|
-
|
|
2055
|
+
if (this.ctrl.size === "sm") {
|
|
2056
|
+
const sizer = doc.createElement("div");
|
|
2057
|
+
sizer.className = "vela-select-sizer";
|
|
2058
|
+
sizer.setAttribute("aria-hidden", "true");
|
|
2059
|
+
for (const o of this.ctrl.options) {
|
|
2060
|
+
const s = doc.createElement("span");
|
|
2061
|
+
s.textContent = o.label;
|
|
2062
|
+
sizer.appendChild(s);
|
|
2063
|
+
}
|
|
2064
|
+
wrap.appendChild(sizer);
|
|
1957
2065
|
}
|
|
1958
|
-
wrap.appendChild(sizer);
|
|
1959
2066
|
btn.addEventListener("pointerdown", (e) => {
|
|
1960
2067
|
e.preventDefault();
|
|
1961
2068
|
btn.focus({ preventScroll: true });
|
|
@@ -2061,10 +2168,12 @@ function numberInputController(opts) {
|
|
|
2061
2168
|
}
|
|
2062
2169
|
|
|
2063
2170
|
// src/ui/components/number-input/styles.ts
|
|
2064
|
-
var NUMBER_STYLE_ID = "vela-ui-number";
|
|
2171
|
+
var NUMBER_STYLE_ID = "vela-ui-number-3";
|
|
2065
2172
|
var NUMBER_CSS = `
|
|
2066
2173
|
.vela-num { position: relative; display: inline-block; min-width: 0; }
|
|
2067
2174
|
.vela-num[data-fill] { width: 100%; }
|
|
2175
|
+
.vela-num:not([data-fill]) { width: 100px; flex: none; justify-self: start; }
|
|
2176
|
+
.vela-num:not([data-fill])[data-compact] { width: 80px; }
|
|
2068
2177
|
.vela-num input {
|
|
2069
2178
|
width: 100%;
|
|
2070
2179
|
box-sizing: border-box;
|
|
@@ -2084,7 +2193,9 @@ var NUMBER_CSS = `
|
|
|
2084
2193
|
.vela-num input::-webkit-inner-spin-button, .vela-num input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
|
|
2085
2194
|
.vela-num input:hover { border-color: var(--vela-fg-muted); }
|
|
2086
2195
|
.vela-num input:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
|
|
2087
|
-
|
|
2196
|
+
/* The stepper gutter exists only while the steppers do (hover) \u2014 an idle field keeps
|
|
2197
|
+
its full width so long values aren't cut under an invisible arrow column. */
|
|
2198
|
+
.vela-num[data-steppers]:hover input { padding-right: 26px; }
|
|
2088
2199
|
.vela-num[data-size='sm'] { width: 64px; flex: none; }
|
|
2089
2200
|
.vela-num[data-size='sm'][data-compact] { width: 56px; }
|
|
2090
2201
|
.vela-num[data-size='sm'] input {
|
|
@@ -2140,7 +2251,7 @@ var NumberInput = class {
|
|
|
2140
2251
|
const wrap = doc.createElement("div");
|
|
2141
2252
|
wrap.className = "vela-num";
|
|
2142
2253
|
wrap.dataset.size = this.ctrl.size;
|
|
2143
|
-
if (this.ctrl.size !== "sm") wrap.dataset.fill = "";
|
|
2254
|
+
if (opts.fill ?? this.ctrl.size !== "sm") wrap.dataset.fill = "";
|
|
2144
2255
|
if (opts.compact) wrap.dataset.compact = "";
|
|
2145
2256
|
if (this.ctrl.steppers) wrap.dataset.steppers = "";
|
|
2146
2257
|
const ni = doc.createElement("input");
|
|
@@ -2266,15 +2377,17 @@ function textFieldController(opts = {}) {
|
|
|
2266
2377
|
}
|
|
2267
2378
|
|
|
2268
2379
|
// src/ui/components/text-field/styles.ts
|
|
2269
|
-
var TEXT_STYLE_ID = "vela-ui-text";
|
|
2380
|
+
var TEXT_STYLE_ID = "vela-ui-text-2";
|
|
2270
2381
|
var TEXT_CSS = `
|
|
2271
2382
|
.vela-text {
|
|
2272
2383
|
display: inline-block;
|
|
2273
2384
|
min-width: 0;
|
|
2274
2385
|
}
|
|
2275
2386
|
.vela-text[data-fill] { width: 100%; }
|
|
2387
|
+
.vela-text:not([data-fill]) { width: 100px; flex: none; justify-self: start; }
|
|
2276
2388
|
.vela-text-field {
|
|
2277
2389
|
width: 100%;
|
|
2390
|
+
min-width: 0;
|
|
2278
2391
|
box-sizing: border-box;
|
|
2279
2392
|
height: 34px;
|
|
2280
2393
|
background: transparent;
|
|
@@ -2285,6 +2398,8 @@ var TEXT_CSS = `
|
|
|
2285
2398
|
font-size: 14px;
|
|
2286
2399
|
font-family: inherit;
|
|
2287
2400
|
outline: none;
|
|
2401
|
+
overflow: hidden;
|
|
2402
|
+
text-overflow: ellipsis;
|
|
2288
2403
|
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
2289
2404
|
}
|
|
2290
2405
|
.vela-text-field:hover { border-color: var(--vela-fg-muted); }
|
|
@@ -2310,6 +2425,7 @@ var TextField = class {
|
|
|
2310
2425
|
if (this.ctrl.fill) wrap.dataset.fill = "";
|
|
2311
2426
|
const ti = doc.createElement("input");
|
|
2312
2427
|
ti.type = "text";
|
|
2428
|
+
ti.size = 1;
|
|
2313
2429
|
if (opts.id) ti.id = opts.id;
|
|
2314
2430
|
ti.value = this.ctrl.value;
|
|
2315
2431
|
ti.className = "vela-text-field";
|
|
@@ -2612,6 +2728,624 @@ function closeColorPopover() {
|
|
|
2612
2728
|
closeOpenPopovers();
|
|
2613
2729
|
}
|
|
2614
2730
|
|
|
2731
|
+
// src/ui/components/text-area/controller.ts
|
|
2732
|
+
function textAreaController(opts = {}) {
|
|
2733
|
+
let value = opts.value ?? "";
|
|
2734
|
+
return {
|
|
2735
|
+
get value() {
|
|
2736
|
+
return value;
|
|
2737
|
+
},
|
|
2738
|
+
size: opts.size ?? "md",
|
|
2739
|
+
rows: opts.rows ?? 3,
|
|
2740
|
+
disabled: opts.disabled ?? false,
|
|
2741
|
+
commit(next) {
|
|
2742
|
+
if (next === value) return null;
|
|
2743
|
+
value = next;
|
|
2744
|
+
opts.onChange?.(next);
|
|
2745
|
+
return next;
|
|
2746
|
+
},
|
|
2747
|
+
sync(next) {
|
|
2748
|
+
value = next;
|
|
2749
|
+
}
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// src/ui/components/text-area/styles.ts
|
|
2754
|
+
var TEXTAREA_STYLE_ID = "vela-ui-textarea";
|
|
2755
|
+
var TEXTAREA_CSS = `
|
|
2756
|
+
.vela-textarea { display: block; width: 100%; min-width: 0; }
|
|
2757
|
+
.vela-textarea-field {
|
|
2758
|
+
display: block;
|
|
2759
|
+
width: 100%;
|
|
2760
|
+
box-sizing: border-box;
|
|
2761
|
+
min-height: 64px;
|
|
2762
|
+
background: transparent;
|
|
2763
|
+
border: 1px solid var(--vela-border-strong);
|
|
2764
|
+
border-radius: 6px;
|
|
2765
|
+
color: var(--vela-fg-bright);
|
|
2766
|
+
padding: 8px;
|
|
2767
|
+
font-size: 14px;
|
|
2768
|
+
font-family: inherit;
|
|
2769
|
+
line-height: 1.4;
|
|
2770
|
+
resize: vertical;
|
|
2771
|
+
outline: none;
|
|
2772
|
+
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
2773
|
+
}
|
|
2774
|
+
.vela-textarea-field:hover { border-color: var(--vela-fg-muted); }
|
|
2775
|
+
.vela-textarea-field:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
|
|
2776
|
+
.vela-textarea-field::placeholder { color: currentColor; opacity: 0.4; }
|
|
2777
|
+
.vela-textarea[data-autogrow] .vela-textarea-field { resize: none; overflow-y: hidden; min-height: 46px; }
|
|
2778
|
+
.vela-textarea[data-size='sm'] .vela-textarea-field {
|
|
2779
|
+
font-size: 13px;
|
|
2780
|
+
min-height: 46px;
|
|
2781
|
+
padding: 6px 9px;
|
|
2782
|
+
line-height: 18px;
|
|
2783
|
+
}
|
|
2784
|
+
`;
|
|
2785
|
+
|
|
2786
|
+
// src/ui/components/text-area/view.ts
|
|
2787
|
+
var TextArea = class {
|
|
2788
|
+
constructor(opts = {}) {
|
|
2789
|
+
const doc = document;
|
|
2790
|
+
injectStyles(TEXTAREA_STYLE_ID, TEXTAREA_CSS, doc);
|
|
2791
|
+
this.ctrl = textAreaController(opts);
|
|
2792
|
+
this.autoGrow = opts.autoGrow === true;
|
|
2793
|
+
this.maxLines = opts.maxLines ?? 4;
|
|
2794
|
+
const wrap = doc.createElement("div");
|
|
2795
|
+
wrap.className = "vela-textarea";
|
|
2796
|
+
wrap.dataset.size = this.ctrl.size;
|
|
2797
|
+
if (this.autoGrow) wrap.dataset.autogrow = "";
|
|
2798
|
+
const ta = doc.createElement("textarea");
|
|
2799
|
+
ta.className = "vela-textarea-field";
|
|
2800
|
+
if (opts.id) ta.id = opts.id;
|
|
2801
|
+
if (opts.placeholder) ta.placeholder = opts.placeholder;
|
|
2802
|
+
ta.rows = this.ctrl.rows;
|
|
2803
|
+
ta.value = this.ctrl.value;
|
|
2804
|
+
if (this.ctrl.disabled) ta.disabled = true;
|
|
2805
|
+
ta.addEventListener("blur", () => {
|
|
2806
|
+
this.ctrl.commit(ta.value);
|
|
2807
|
+
});
|
|
2808
|
+
if (this.autoGrow) {
|
|
2809
|
+
ta.addEventListener("input", () => this.grow());
|
|
2810
|
+
}
|
|
2811
|
+
wrap.appendChild(ta);
|
|
2812
|
+
this.el = wrap;
|
|
2813
|
+
this.input = ta;
|
|
2814
|
+
if (this.autoGrow) this.grow();
|
|
2815
|
+
}
|
|
2816
|
+
get value() {
|
|
2817
|
+
return this.ctrl.value;
|
|
2818
|
+
}
|
|
2819
|
+
setValue(v) {
|
|
2820
|
+
this.ctrl.sync(v);
|
|
2821
|
+
this.input.value = v;
|
|
2822
|
+
if (this.autoGrow) this.grow();
|
|
2823
|
+
}
|
|
2824
|
+
grow() {
|
|
2825
|
+
const ta = this.input;
|
|
2826
|
+
const lh = parseFloat(getComputedStyle(ta).lineHeight) || 18;
|
|
2827
|
+
const pad = ta.clientHeight - (ta.offsetHeight ? lh * (ta.rows || 1) : 0);
|
|
2828
|
+
const max = lh * this.maxLines + (Number.isFinite(pad) && pad > 0 ? pad : 12);
|
|
2829
|
+
ta.style.height = "auto";
|
|
2830
|
+
ta.style.height = `${Math.min(ta.scrollHeight, max)}px`;
|
|
2831
|
+
ta.style.overflowY = ta.scrollHeight > max ? "auto" : "hidden";
|
|
2832
|
+
}
|
|
2833
|
+
};
|
|
2834
|
+
|
|
2835
|
+
// src/ui/components/glyph-select/controller.ts
|
|
2836
|
+
function glyphSelectController(opts) {
|
|
2837
|
+
let value = opts.value;
|
|
2838
|
+
return {
|
|
2839
|
+
options: opts.options,
|
|
2840
|
+
get value() {
|
|
2841
|
+
return value;
|
|
2842
|
+
},
|
|
2843
|
+
setValue(v) {
|
|
2844
|
+
value = v;
|
|
2845
|
+
},
|
|
2846
|
+
optionOf(v) {
|
|
2847
|
+
return opts.options.find((o) => o.value === v);
|
|
2848
|
+
},
|
|
2849
|
+
pick(v) {
|
|
2850
|
+
value = v;
|
|
2851
|
+
opts.onChange?.(v);
|
|
2852
|
+
return v;
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
var WIDTH_FIELD_OPTIONS = [1, 2, 3, 4, 5];
|
|
2857
|
+
function lineWidthGlyph(width) {
|
|
2858
|
+
return `<svg width="22" height="14" viewBox="0 0 22 14" fill="none"><line x1="2" y1="7" x2="20" y2="7" stroke="currentColor" stroke-width="${width}" stroke-linecap="round"/></svg>`;
|
|
2859
|
+
}
|
|
2860
|
+
function widthFieldOptions() {
|
|
2861
|
+
return WIDTH_FIELD_OPTIONS.map((w) => ({ value: w, label: `${w}px`, glyph: lineWidthGlyph(w) }));
|
|
2862
|
+
}
|
|
2863
|
+
|
|
2864
|
+
// src/ui/components/glyph-select/styles.ts
|
|
2865
|
+
var GLYPH_SELECT_STYLE_ID = "vela-ui-glyph-select";
|
|
2866
|
+
var GLYPH_SELECT_CSS = `
|
|
2867
|
+
.vela-glyph-select {
|
|
2868
|
+
height: 34px;
|
|
2869
|
+
padding: 0 26px 0 8px;
|
|
2870
|
+
border: 1px solid var(--vela-border-strong);
|
|
2871
|
+
border-radius: 6px;
|
|
2872
|
+
background: transparent;
|
|
2873
|
+
cursor: pointer;
|
|
2874
|
+
display: inline-flex;
|
|
2875
|
+
align-items: center;
|
|
2876
|
+
gap: 8px;
|
|
2877
|
+
flex: none;
|
|
2878
|
+
justify-self: start;
|
|
2879
|
+
color: var(--vela-fg-bright);
|
|
2880
|
+
font-size: 14px;
|
|
2881
|
+
font-family: inherit;
|
|
2882
|
+
outline: none;
|
|
2883
|
+
position: relative;
|
|
2884
|
+
transition: border-color 0.12s ease, box-shadow 0.12s ease;
|
|
2885
|
+
}
|
|
2886
|
+
.vela-glyph-select:hover { border-color: var(--vela-fg-muted); }
|
|
2887
|
+
.vela-glyph-select:focus { border-color: var(--vela-focus); box-shadow: 0 0 0 3px var(--vela-focus-soft); }
|
|
2888
|
+
.vela-glyph-select-caret {
|
|
2889
|
+
position: absolute;
|
|
2890
|
+
right: 8px;
|
|
2891
|
+
top: 50%;
|
|
2892
|
+
transform: translateY(-50%);
|
|
2893
|
+
display: flex;
|
|
2894
|
+
opacity: 0.55;
|
|
2895
|
+
color: inherit;
|
|
2896
|
+
line-height: 0;
|
|
2897
|
+
pointer-events: none;
|
|
2898
|
+
}
|
|
2899
|
+
.vela-glyph-select-pop {
|
|
2900
|
+
background: var(--vela-bg);
|
|
2901
|
+
border: none;
|
|
2902
|
+
border-radius: 6px;
|
|
2903
|
+
box-shadow: var(--vela-shadow);
|
|
2904
|
+
padding: 4px;
|
|
2905
|
+
display: flex;
|
|
2906
|
+
flex-direction: column;
|
|
2907
|
+
gap: 1px;
|
|
2908
|
+
color: var(--vela-fg);
|
|
2909
|
+
font: 14px var(--vela-font);
|
|
2910
|
+
}
|
|
2911
|
+
.vela-glyph-select-item {
|
|
2912
|
+
display: flex;
|
|
2913
|
+
align-items: center;
|
|
2914
|
+
gap: 8px;
|
|
2915
|
+
min-width: 96px;
|
|
2916
|
+
padding: 7px 10px;
|
|
2917
|
+
border: none;
|
|
2918
|
+
border-radius: 4px;
|
|
2919
|
+
background: transparent;
|
|
2920
|
+
color: inherit;
|
|
2921
|
+
cursor: pointer;
|
|
2922
|
+
text-align: left;
|
|
2923
|
+
font: inherit;
|
|
2924
|
+
}
|
|
2925
|
+
.vela-glyph-select-item:hover { background: var(--vela-hover); }
|
|
2926
|
+
.vela-glyph-select-item[data-active='1'] { background: var(--vela-hover-strong); color: var(--vela-fg-bright); }
|
|
2927
|
+
`;
|
|
2928
|
+
|
|
2929
|
+
// src/ui/components/glyph-select/view.ts
|
|
2930
|
+
var GlyphSelect = class {
|
|
2931
|
+
constructor(opts) {
|
|
2932
|
+
injectStyles(GLYPH_SELECT_STYLE_ID, GLYPH_SELECT_CSS, document);
|
|
2933
|
+
this.ctrl = glyphSelectController(opts);
|
|
2934
|
+
this.theme = opts.theme;
|
|
2935
|
+
this.get = opts.get ?? (() => this.ctrl.value);
|
|
2936
|
+
const trigger = document.createElement("button");
|
|
2937
|
+
trigger.type = "button";
|
|
2938
|
+
trigger.className = "vela-glyph-select";
|
|
2939
|
+
if (opts.title) trigger.title = opts.title;
|
|
2940
|
+
this.el = trigger;
|
|
2941
|
+
this.paint();
|
|
2942
|
+
trigger.addEventListener("pointerdown", (e) => {
|
|
2943
|
+
e.preventDefault();
|
|
2944
|
+
trigger.focus({ preventScroll: true });
|
|
2945
|
+
});
|
|
2946
|
+
trigger.addEventListener("click", (e) => {
|
|
2947
|
+
e.stopPropagation();
|
|
2948
|
+
this.toggle();
|
|
2949
|
+
});
|
|
2950
|
+
trigger.addEventListener("vela-sync", () => {
|
|
2951
|
+
this.ctrl.setValue(this.get());
|
|
2952
|
+
this.paint();
|
|
2953
|
+
});
|
|
2954
|
+
}
|
|
2955
|
+
setValue(v) {
|
|
2956
|
+
this.ctrl.setValue(v);
|
|
2957
|
+
this.paint();
|
|
2958
|
+
}
|
|
2959
|
+
paint() {
|
|
2960
|
+
const cur = this.get();
|
|
2961
|
+
const opt = this.ctrl.optionOf(cur);
|
|
2962
|
+
const glyph = document.createElement("span");
|
|
2963
|
+
glyph.style.display = "flex";
|
|
2964
|
+
glyph.innerHTML = opt?.glyph ?? "";
|
|
2965
|
+
const caret = document.createElement("span");
|
|
2966
|
+
caret.className = "vela-glyph-select-caret";
|
|
2967
|
+
caret.appendChild(iconEl("chevron-down", this.el.ownerDocument));
|
|
2968
|
+
this.el.replaceChildren(glyph, caret);
|
|
2969
|
+
}
|
|
2970
|
+
toggle() {
|
|
2971
|
+
if (openPopoverTrigger() === this.el) {
|
|
2972
|
+
closeOpenPopovers();
|
|
2973
|
+
return;
|
|
2974
|
+
}
|
|
2975
|
+
const current = this.get();
|
|
2976
|
+
const pop = new Popover({
|
|
2977
|
+
trigger: this.el,
|
|
2978
|
+
theme: this.theme,
|
|
2979
|
+
align: "end",
|
|
2980
|
+
gap: 6,
|
|
2981
|
+
className: "vela-glyph-select-pop",
|
|
2982
|
+
content: (el) => {
|
|
2983
|
+
el.style.font = `var(--vela-font-size-md) ${this.theme.fontFamily}`;
|
|
2984
|
+
for (const o of this.ctrl.options) {
|
|
2985
|
+
const item = document.createElement("button");
|
|
2986
|
+
item.type = "button";
|
|
2987
|
+
item.className = "vela-glyph-select-item";
|
|
2988
|
+
item.dataset.active = o.value === current ? "1" : "0";
|
|
2989
|
+
item.innerHTML = `<span style="display:flex;flex:none;">${o.glyph}</span><span style="flex:1;font-variant-numeric:tabular-nums;">${o.label}</span>`;
|
|
2990
|
+
item.addEventListener("click", (ev) => {
|
|
2991
|
+
ev.stopPropagation();
|
|
2992
|
+
pop.hide();
|
|
2993
|
+
this.ctrl.pick(o.value);
|
|
2994
|
+
this.paint();
|
|
2995
|
+
});
|
|
2996
|
+
el.appendChild(item);
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
});
|
|
3000
|
+
pop.show();
|
|
3001
|
+
}
|
|
3002
|
+
};
|
|
3003
|
+
function widthField(theme, getVal, onVal) {
|
|
3004
|
+
return new GlyphSelect({
|
|
3005
|
+
theme,
|
|
3006
|
+
options: widthFieldOptions(),
|
|
3007
|
+
value: getVal(),
|
|
3008
|
+
get: getVal,
|
|
3009
|
+
onChange: onVal
|
|
3010
|
+
}).el;
|
|
3011
|
+
}
|
|
3012
|
+
function closeWidthPopover() {
|
|
3013
|
+
closeOpenPopovers();
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
// src/ui/components/field/controller.ts
|
|
3017
|
+
var FIELD_GAP_PX = 16;
|
|
3018
|
+
function fieldGridColumns(variant, mobile) {
|
|
3019
|
+
if (variant === "inputs") return mobile ? "minmax(0,1fr) auto" : "max-content 1fr";
|
|
3020
|
+
return mobile ? "minmax(0,1fr) max-content" : "max-content max-content";
|
|
3021
|
+
}
|
|
3022
|
+
|
|
3023
|
+
// src/ui/components/field/styles.ts
|
|
3024
|
+
var FIELD_STYLE_ID = "vela-ui-field";
|
|
3025
|
+
var FIELD_CSS = `
|
|
3026
|
+
.vela-field-grid {
|
|
3027
|
+
display: grid;
|
|
3028
|
+
align-items: center;
|
|
3029
|
+
align-content: start;
|
|
3030
|
+
column-gap: ${FIELD_GAP_PX}px;
|
|
3031
|
+
row-gap: ${FIELD_GAP_PX}px;
|
|
3032
|
+
}
|
|
3033
|
+
.vela-field-grid[data-variant='inputs'] { grid-template-columns: max-content 1fr; }
|
|
3034
|
+
.vela-field-grid[data-variant='inputs'][data-mobile] { grid-template-columns: minmax(0,1fr) auto; }
|
|
3035
|
+
.vela-field-grid[data-variant='settings'] {
|
|
3036
|
+
grid-template-columns: max-content max-content;
|
|
3037
|
+
column-gap: 12px;
|
|
3038
|
+
}
|
|
3039
|
+
.vela-field-grid[data-variant='settings'][data-mobile] { grid-template-columns: minmax(0,1fr) max-content; }
|
|
3040
|
+
.vela-field-row { display: contents; }
|
|
3041
|
+
.vela-field-span { grid-column: 1 / -1; }
|
|
3042
|
+
.vela-field-label {
|
|
3043
|
+
opacity: 0.9;
|
|
3044
|
+
white-space: nowrap;
|
|
3045
|
+
overflow: hidden;
|
|
3046
|
+
text-overflow: ellipsis;
|
|
3047
|
+
font-size: 14px;
|
|
3048
|
+
}
|
|
3049
|
+
.vela-field-label[data-size='sm'] { font-size: inherit; opacity: 0.85; }
|
|
3050
|
+
.vela-field-cell { display: flex; align-items: center; gap: 8px; justify-self: start; }
|
|
3051
|
+
.vela-field-bool { display: flex; align-items: center; gap: 8px; min-height: 22px; }
|
|
3052
|
+
.vela-field-stacked { display: flex; flex-direction: column; gap: 8px; }
|
|
3053
|
+
.vela-field-stacked-head { display: flex; align-items: center; justify-content: center; gap: 8px; }
|
|
3054
|
+
.vela-field-inline { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; }
|
|
3055
|
+
.vela-field-inline-item { display: flex; align-items: center; gap: 8px; }
|
|
3056
|
+
.vela-field-toggle-label { display: flex; align-items: center; gap: 8px; }
|
|
3057
|
+
.vela-field-section {
|
|
3058
|
+
grid-column: 1 / -1;
|
|
3059
|
+
margin: 24px 0 0;
|
|
3060
|
+
padding-bottom: 8px;
|
|
3061
|
+
font-size: var(--vela-font-size-sm);
|
|
3062
|
+
font-weight: 700;
|
|
3063
|
+
letter-spacing: 0.05em;
|
|
3064
|
+
text-transform: uppercase;
|
|
3065
|
+
color: var(--vela-fg-muted);
|
|
3066
|
+
}
|
|
3067
|
+
.vela-field-section[data-variant='inputs'] {
|
|
3068
|
+
margin: 0;
|
|
3069
|
+
padding: 20px 0 8px;
|
|
3070
|
+
font-size: 11px;
|
|
3071
|
+
font-weight: 600;
|
|
3072
|
+
}
|
|
3073
|
+
.vela-field-section[data-variant='inputs'][data-first] { padding-top: 4px; }
|
|
3074
|
+
.vela-field-sep { grid-column: 1 / -1; height: 14px; }
|
|
3075
|
+
.vela-field-dim { opacity: 0.4 !important; pointer-events: none !important; }
|
|
3076
|
+
`;
|
|
3077
|
+
|
|
3078
|
+
// src/ui/components/field/view.ts
|
|
3079
|
+
function ensureFieldStyles(doc = document) {
|
|
3080
|
+
injectStyles(FIELD_STYLE_ID, FIELD_CSS, doc);
|
|
3081
|
+
}
|
|
3082
|
+
function fieldGrid(opts = {}) {
|
|
3083
|
+
ensureFieldStyles();
|
|
3084
|
+
const el = document.createElement("div");
|
|
3085
|
+
el.className = "vela-field-grid";
|
|
3086
|
+
const variant = opts.variant ?? "inputs";
|
|
3087
|
+
el.dataset.variant = variant;
|
|
3088
|
+
if (opts.mobile) el.dataset.mobile = "";
|
|
3089
|
+
el.style.gridTemplateColumns = fieldGridColumns(variant, opts.mobile === true);
|
|
3090
|
+
return el;
|
|
3091
|
+
}
|
|
3092
|
+
function fieldSection(title, opts = {}) {
|
|
3093
|
+
ensureFieldStyles();
|
|
3094
|
+
const el = document.createElement("div");
|
|
3095
|
+
el.className = "vela-field-section";
|
|
3096
|
+
if (opts.variant) el.dataset.variant = opts.variant;
|
|
3097
|
+
if (opts.first) el.dataset.first = "";
|
|
3098
|
+
el.textContent = title;
|
|
3099
|
+
return el;
|
|
3100
|
+
}
|
|
3101
|
+
function fieldSeparator() {
|
|
3102
|
+
ensureFieldStyles();
|
|
3103
|
+
const el = document.createElement("div");
|
|
3104
|
+
el.className = "vela-field-sep";
|
|
3105
|
+
return el;
|
|
3106
|
+
}
|
|
3107
|
+
function labelEl(text, opts) {
|
|
3108
|
+
const el = opts.id ? document.createElement("label") : document.createElement("span");
|
|
3109
|
+
if (opts.id && el instanceof HTMLLabelElement) el.htmlFor = opts.id;
|
|
3110
|
+
el.className = "vela-field-label";
|
|
3111
|
+
if (opts.size) el.dataset.size = opts.size;
|
|
3112
|
+
el.textContent = text;
|
|
3113
|
+
return el;
|
|
3114
|
+
}
|
|
3115
|
+
function asList(control) {
|
|
3116
|
+
if (!control) return [];
|
|
3117
|
+
return Array.isArray(control) ? control : [control];
|
|
3118
|
+
}
|
|
3119
|
+
function applyClass(el, extra) {
|
|
3120
|
+
if (extra) {
|
|
3121
|
+
for (const c of extra.split(/\s+/)) if (c) el.classList.add(c);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
function dimControls(controls, on) {
|
|
3125
|
+
for (const c of controls) {
|
|
3126
|
+
if (c.dataset.sdSelfGated === "1") continue;
|
|
3127
|
+
c.classList.toggle("vela-field-dim", !on);
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
function fieldRow(opts) {
|
|
3131
|
+
ensureFieldStyles();
|
|
3132
|
+
const size = opts.labelSize;
|
|
3133
|
+
if (opts.inline) {
|
|
3134
|
+
const wrap2 = document.createElement("div");
|
|
3135
|
+
wrap2.className = "vela-field-span vela-field-inline";
|
|
3136
|
+
for (const item of opts.inline) {
|
|
3137
|
+
const it = document.createElement("div");
|
|
3138
|
+
it.className = "vela-field-inline-item";
|
|
3139
|
+
if (item.toggleFirst) it.style.flexDirection = "row-reverse";
|
|
3140
|
+
if (item.label) {
|
|
3141
|
+
const lbl = labelEl(item.label, { id: item.id, size });
|
|
3142
|
+
if (item.toggleFirst) lbl.style.cursor = "pointer";
|
|
3143
|
+
it.appendChild(lbl);
|
|
3144
|
+
}
|
|
3145
|
+
if (item.fit) it.appendChild(item.control);
|
|
3146
|
+
else {
|
|
3147
|
+
const cw = document.createElement("div");
|
|
3148
|
+
cw.style.width = "100px";
|
|
3149
|
+
cw.appendChild(item.control);
|
|
3150
|
+
it.appendChild(cw);
|
|
3151
|
+
}
|
|
3152
|
+
wrap2.appendChild(it);
|
|
3153
|
+
}
|
|
3154
|
+
if (opts.info) wrap2.appendChild(opts.info);
|
|
3155
|
+
applyClass(wrap2, opts.className);
|
|
3156
|
+
return wrap2;
|
|
3157
|
+
}
|
|
3158
|
+
if (opts.bool) {
|
|
3159
|
+
const wrap2 = document.createElement("div");
|
|
3160
|
+
wrap2.className = "vela-field-span vela-field-bool";
|
|
3161
|
+
const sw = new Switch({
|
|
3162
|
+
id: opts.toggle?.id ?? opts.id,
|
|
3163
|
+
checked: opts.toggle?.checked ?? false,
|
|
3164
|
+
size: "md",
|
|
3165
|
+
tone: "bright",
|
|
3166
|
+
onChange: (v) => opts.toggle?.onChange(v)
|
|
3167
|
+
});
|
|
3168
|
+
wrap2.append(sw.el, labelEl(opts.label, { size }));
|
|
3169
|
+
if (opts.info) wrap2.appendChild(opts.info);
|
|
3170
|
+
if (opts.toggle?.get) {
|
|
3171
|
+
const get = opts.toggle.get;
|
|
3172
|
+
wrap2.addEventListener("vela-sync", () => {
|
|
3173
|
+
sw.setChecked(get());
|
|
3174
|
+
});
|
|
3175
|
+
}
|
|
3176
|
+
applyClass(wrap2, opts.className);
|
|
3177
|
+
return wrap2;
|
|
3178
|
+
}
|
|
3179
|
+
if (opts.stacked) {
|
|
3180
|
+
const wrap2 = document.createElement("div");
|
|
3181
|
+
wrap2.className = "vela-field-span vela-field-stacked";
|
|
3182
|
+
const head = document.createElement("div");
|
|
3183
|
+
head.className = "vela-field-stacked-head";
|
|
3184
|
+
head.appendChild(labelEl(opts.label, { id: opts.id, size }));
|
|
3185
|
+
if (opts.info) head.appendChild(opts.info);
|
|
3186
|
+
wrap2.appendChild(head);
|
|
3187
|
+
for (const c of asList(opts.control)) wrap2.appendChild(c);
|
|
3188
|
+
applyClass(wrap2, opts.className);
|
|
3189
|
+
return wrap2;
|
|
3190
|
+
}
|
|
3191
|
+
const wrap = document.createElement("div");
|
|
3192
|
+
wrap.className = "vela-field-row";
|
|
3193
|
+
const controls = asList(opts.control);
|
|
3194
|
+
if (opts.toggle) {
|
|
3195
|
+
const left = document.createElement("div");
|
|
3196
|
+
left.className = "vela-field-toggle-label";
|
|
3197
|
+
const sw = new Switch({
|
|
3198
|
+
id: opts.toggle.id ?? opts.id,
|
|
3199
|
+
checked: opts.toggle.checked,
|
|
3200
|
+
size: "md",
|
|
3201
|
+
tone: "bright",
|
|
3202
|
+
onChange: (v) => {
|
|
3203
|
+
opts.toggle.onChange(v);
|
|
3204
|
+
if (controls.length > 0) dimControls(controls, v);
|
|
3205
|
+
}
|
|
3206
|
+
});
|
|
3207
|
+
left.append(sw.el, labelEl(opts.label, { size }));
|
|
3208
|
+
wrap.appendChild(left);
|
|
3209
|
+
if (controls.length > 0) dimControls(controls, opts.toggle.checked);
|
|
3210
|
+
if (opts.toggle.get) {
|
|
3211
|
+
const get = opts.toggle.get;
|
|
3212
|
+
wrap.addEventListener("vela-sync", () => {
|
|
3213
|
+
const v = get();
|
|
3214
|
+
sw.setChecked(v);
|
|
3215
|
+
if (controls.length > 0) dimControls(controls, v);
|
|
3216
|
+
});
|
|
3217
|
+
}
|
|
3218
|
+
} else {
|
|
3219
|
+
wrap.appendChild(labelEl(opts.label, { id: opts.id, size }));
|
|
3220
|
+
}
|
|
3221
|
+
const cell = document.createElement("div");
|
|
3222
|
+
cell.className = "vela-field-cell";
|
|
3223
|
+
if (opts.controlWidth != null && controls.length === 1 && !opts.fit) {
|
|
3224
|
+
const cw = document.createElement("div");
|
|
3225
|
+
cw.style.width = `${opts.controlWidth}px`;
|
|
3226
|
+
cw.appendChild(controls[0]);
|
|
3227
|
+
cell.appendChild(cw);
|
|
3228
|
+
} else {
|
|
3229
|
+
for (const c of controls) cell.appendChild(c);
|
|
3230
|
+
}
|
|
3231
|
+
if (opts.info) cell.appendChild(opts.info);
|
|
3232
|
+
wrap.appendChild(cell);
|
|
3233
|
+
applyClass(wrap, opts.className);
|
|
3234
|
+
return wrap;
|
|
3235
|
+
}
|
|
3236
|
+
function buildFieldControl(desc) {
|
|
3237
|
+
if (desc.kind === "number") {
|
|
3238
|
+
const ni = new NumberInput({
|
|
3239
|
+
id: desc.id,
|
|
3240
|
+
value: desc.value,
|
|
3241
|
+
min: desc.min,
|
|
3242
|
+
max: desc.max,
|
|
3243
|
+
step: desc.step,
|
|
3244
|
+
integer: desc.integer,
|
|
3245
|
+
size: "md",
|
|
3246
|
+
fill: desc.fill,
|
|
3247
|
+
compact: desc.compact,
|
|
3248
|
+
commit: desc.commit,
|
|
3249
|
+
steppers: desc.steppers,
|
|
3250
|
+
clamp: desc.clamp,
|
|
3251
|
+
placeholder: desc.placeholder,
|
|
3252
|
+
emptyValue: desc.emptyValue,
|
|
3253
|
+
title: desc.title,
|
|
3254
|
+
onChange: desc.onChange
|
|
3255
|
+
});
|
|
3256
|
+
if (desc.sync) {
|
|
3257
|
+
const sync = desc.sync;
|
|
3258
|
+
ni.el.addEventListener("vela-sync", () => {
|
|
3259
|
+
ni.setValue(sync());
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3262
|
+
return { el: ni.el, setValue: (v) => ni.setValue(Number(v)) };
|
|
3263
|
+
}
|
|
3264
|
+
if (desc.kind === "select") {
|
|
3265
|
+
const sel = new Select({
|
|
3266
|
+
id: desc.id,
|
|
3267
|
+
options: desc.options,
|
|
3268
|
+
value: desc.value,
|
|
3269
|
+
size: "md",
|
|
3270
|
+
fill: desc.fill,
|
|
3271
|
+
theme: desc.theme,
|
|
3272
|
+
list: desc.list,
|
|
3273
|
+
onChange: desc.onChange
|
|
3274
|
+
});
|
|
3275
|
+
if (desc.title) sel.el.title = desc.title;
|
|
3276
|
+
if (desc.sync) {
|
|
3277
|
+
const sync = desc.sync;
|
|
3278
|
+
sel.el.addEventListener("vela-sync", () => {
|
|
3279
|
+
sel.setValue(sync());
|
|
3280
|
+
});
|
|
3281
|
+
}
|
|
3282
|
+
return { el: sel.el, setValue: (v) => sel.setValue(String(v)) };
|
|
3283
|
+
}
|
|
3284
|
+
if (desc.kind === "switch") {
|
|
3285
|
+
const sw = new Switch({
|
|
3286
|
+
id: desc.id,
|
|
3287
|
+
checked: desc.checked,
|
|
3288
|
+
size: "md",
|
|
3289
|
+
tone: "bright",
|
|
3290
|
+
onChange: desc.onChange
|
|
3291
|
+
});
|
|
3292
|
+
if (desc.sync) {
|
|
3293
|
+
const sync = desc.sync;
|
|
3294
|
+
sw.el.addEventListener("vela-sync", () => {
|
|
3295
|
+
sw.setChecked(sync());
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
return { el: sw.el, setValue: (v) => sw.setChecked(Boolean(v)) };
|
|
3299
|
+
}
|
|
3300
|
+
if (desc.kind === "color") {
|
|
3301
|
+
const el2 = colorField(desc.theme, desc.get, desc.onChange, {
|
|
3302
|
+
shape: "circle",
|
|
3303
|
+
id: desc.id,
|
|
3304
|
+
popover: desc.popover
|
|
3305
|
+
});
|
|
3306
|
+
if (desc.title) el2.title = desc.title;
|
|
3307
|
+
return { el: el2 };
|
|
3308
|
+
}
|
|
3309
|
+
if (desc.kind === "text") {
|
|
3310
|
+
const tf = new TextField({
|
|
3311
|
+
id: desc.id,
|
|
3312
|
+
value: desc.value,
|
|
3313
|
+
size: "md",
|
|
3314
|
+
fill: desc.fill,
|
|
3315
|
+
onChange: desc.onChange
|
|
3316
|
+
});
|
|
3317
|
+
if (desc.placeholder) tf.input.placeholder = desc.placeholder;
|
|
3318
|
+
if (desc.sync) {
|
|
3319
|
+
const sync = desc.sync;
|
|
3320
|
+
tf.el.addEventListener("vela-sync", () => {
|
|
3321
|
+
tf.setValue(sync());
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
return { el: tf.el, setValue: (v) => tf.setValue(String(v)) };
|
|
3325
|
+
}
|
|
3326
|
+
if (desc.kind === "textarea") {
|
|
3327
|
+
const ta = new TextArea({
|
|
3328
|
+
id: desc.id,
|
|
3329
|
+
value: desc.value,
|
|
3330
|
+
rows: desc.rows,
|
|
3331
|
+
autoGrow: desc.autoGrow,
|
|
3332
|
+
maxLines: desc.maxLines,
|
|
3333
|
+
placeholder: desc.placeholder,
|
|
3334
|
+
onChange: desc.onChange
|
|
3335
|
+
});
|
|
3336
|
+
if (desc.sync) {
|
|
3337
|
+
const sync = desc.sync;
|
|
3338
|
+
ta.el.addEventListener("vela-sync", () => {
|
|
3339
|
+
ta.setValue(sync());
|
|
3340
|
+
});
|
|
3341
|
+
}
|
|
3342
|
+
return { el: ta.el, setValue: (v) => ta.setValue(String(v)) };
|
|
3343
|
+
}
|
|
3344
|
+
const el = widthField(desc.theme, desc.get, desc.onChange);
|
|
3345
|
+
if (desc.title) el.title = desc.title;
|
|
3346
|
+
return { el };
|
|
3347
|
+
}
|
|
3348
|
+
|
|
2615
3349
|
Object.defineProperty(exports, "normalizeProps", {
|
|
2616
3350
|
enumerable: true,
|
|
2617
3351
|
get: function () { return vanilla.normalizeProps; }
|
|
@@ -2623,22 +3357,30 @@ Object.defineProperty(exports, "spreadProps", {
|
|
|
2623
3357
|
exports.ColorField = ColorField;
|
|
2624
3358
|
exports.Dialog = Dialog;
|
|
2625
3359
|
exports.Drawer = Drawer;
|
|
3360
|
+
exports.FIELD_FOCUS_CSS = FIELD_FOCUS_CSS;
|
|
3361
|
+
exports.FIELD_FOCUS_RING = FIELD_FOCUS_RING;
|
|
3362
|
+
exports.FIELD_GAP_PX = FIELD_GAP_PX;
|
|
3363
|
+
exports.GlyphSelect = GlyphSelect;
|
|
2626
3364
|
exports.KeymapManager = KeymapManager;
|
|
2627
3365
|
exports.Menu = Menu;
|
|
2628
3366
|
exports.NumberInput = NumberInput;
|
|
2629
3367
|
exports.Popover = Popover;
|
|
2630
3368
|
exports.Select = Select;
|
|
2631
3369
|
exports.Switch = Switch;
|
|
3370
|
+
exports.TextArea = TextArea;
|
|
2632
3371
|
exports.TextField = TextField;
|
|
2633
3372
|
exports.Tooltip = Tooltip;
|
|
3373
|
+
exports.WIDTH_FIELD_OPTIONS = WIDTH_FIELD_OPTIONS;
|
|
2634
3374
|
exports.applyPlotOverlayTokens = applyPlotOverlayTokens;
|
|
2635
3375
|
exports.applyThemeTokens = applyThemeTokens;
|
|
2636
3376
|
exports.blendOver = blendOver;
|
|
2637
3377
|
exports.buildColorPicker = buildColorPicker;
|
|
3378
|
+
exports.buildFieldControl = buildFieldControl;
|
|
2638
3379
|
exports.buildPalette = buildPalette;
|
|
2639
3380
|
exports.clampNumber = clampNumber;
|
|
2640
3381
|
exports.closeColorPopover = closeColorPopover;
|
|
2641
3382
|
exports.closeOpenPopovers = closeOpenPopovers;
|
|
3383
|
+
exports.closeWidthPopover = closeWidthPopover;
|
|
2642
3384
|
exports.colorField = colorField;
|
|
2643
3385
|
exports.colorPickerController = colorPickerController;
|
|
2644
3386
|
exports.combineColor = combineColor;
|
|
@@ -2646,7 +3388,14 @@ exports.decorateSelectScroll = decorateSelectScroll;
|
|
|
2646
3388
|
exports.dialogController = dialogController;
|
|
2647
3389
|
exports.drawerController = drawerController;
|
|
2648
3390
|
exports.ensureUIHost = ensureUIHost;
|
|
3391
|
+
exports.eventDismissedPopover = eventDismissedPopover;
|
|
3392
|
+
exports.fieldGrid = fieldGrid;
|
|
3393
|
+
exports.fieldGridColumns = fieldGridColumns;
|
|
3394
|
+
exports.fieldRow = fieldRow;
|
|
3395
|
+
exports.fieldSection = fieldSection;
|
|
3396
|
+
exports.fieldSeparator = fieldSeparator;
|
|
2649
3397
|
exports.fillSelectList = fillSelectList;
|
|
3398
|
+
exports.glyphSelectController = glyphSelectController;
|
|
2650
3399
|
exports.hslHex = hslHex;
|
|
2651
3400
|
exports.iconEl = iconEl;
|
|
2652
3401
|
exports.iconMarkup = iconMarkup;
|
|
@@ -2654,11 +3403,13 @@ exports.injectStyles = injectStyles;
|
|
|
2654
3403
|
exports.insetRect = insetRect;
|
|
2655
3404
|
exports.intersectRects = intersectRects;
|
|
2656
3405
|
exports.isPopoverOpen = isPopoverOpen;
|
|
3406
|
+
exports.lineWidthGlyph = lineWidthGlyph;
|
|
2657
3407
|
exports.menuController = menuController;
|
|
2658
3408
|
exports.nextUid = nextUid;
|
|
2659
3409
|
exports.numberInputController = numberInputController;
|
|
2660
3410
|
exports.openPopoverTrigger = openPopoverTrigger;
|
|
2661
3411
|
exports.openSelectList = openSelectList;
|
|
3412
|
+
exports.overlayScrollbarCss = overlayScrollbarCss;
|
|
2662
3413
|
exports.placePopover = placePopover;
|
|
2663
3414
|
exports.popoverController = popoverController;
|
|
2664
3415
|
exports.registerIcon = registerIcon;
|
|
@@ -2669,9 +3420,12 @@ exports.splitColor = splitColor;
|
|
|
2669
3420
|
exports.svg16 = svg16;
|
|
2670
3421
|
exports.svg24 = svg24;
|
|
2671
3422
|
exports.switchController = switchController;
|
|
3423
|
+
exports.textAreaController = textAreaController;
|
|
2672
3424
|
exports.textFieldController = textFieldController;
|
|
2673
3425
|
exports.toggleSelectList = toggleSelectList;
|
|
2674
3426
|
exports.tooltipController = tooltipController;
|
|
2675
3427
|
exports.transparencyChecker = transparencyChecker;
|
|
2676
3428
|
exports.viewportRect = viewportRect;
|
|
3429
|
+
exports.widthField = widthField;
|
|
3430
|
+
exports.widthFieldOptions = widthFieldOptions;
|
|
2677
3431
|
exports.withAlpha = withAlpha;
|