symfony-expression-editor 0.1.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/index.cjs +99 -24
- package/dist/index.d.cts +3 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +99 -24
- package/example.html +24 -5
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -870,14 +870,14 @@ const replaceNext = /* @__PURE__ */ searchCommand((view$1, { query }) => {
|
|
|
870
870
|
next = query.nextMatch(state$1, next.from, next.to);
|
|
871
871
|
effects.push(view.EditorView.announce.of(state$1.phrase("replaced match on line $", state$1.doc.lineAt(from).number) + "."));
|
|
872
872
|
}
|
|
873
|
+
let changeSet = view$1.state.changes(changes);
|
|
873
874
|
if (next) {
|
|
874
|
-
|
|
875
|
-
selection = state.EditorSelection.single(next.from - off, next.to - off);
|
|
875
|
+
selection = state.EditorSelection.single(next.from, next.to).map(changeSet);
|
|
876
876
|
effects.push(announceMatch(view$1, next));
|
|
877
877
|
effects.push(state$1.facet(searchConfigFacet).scrollToMatch(selection.main, view$1));
|
|
878
878
|
}
|
|
879
879
|
view$1.dispatch({
|
|
880
|
-
changes,
|
|
880
|
+
changes: changeSet,
|
|
881
881
|
selection,
|
|
882
882
|
effects,
|
|
883
883
|
userEvent: "input.replace"
|
|
@@ -1148,6 +1148,22 @@ const searchExtensions = [
|
|
|
1148
1148
|
baseTheme$2
|
|
1149
1149
|
];
|
|
1150
1150
|
|
|
1151
|
+
var __defProp = Object.defineProperty;
|
|
1152
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
1153
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
1154
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
1155
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1156
|
+
var __spreadValues = (a, b) => {
|
|
1157
|
+
for (var prop in b || (b = {}))
|
|
1158
|
+
if (__hasOwnProp.call(b, prop))
|
|
1159
|
+
__defNormalProp(a, prop, b[prop]);
|
|
1160
|
+
if (__getOwnPropSymbols)
|
|
1161
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
1162
|
+
if (__propIsEnum.call(b, prop))
|
|
1163
|
+
__defNormalProp(a, prop, b[prop]);
|
|
1164
|
+
}
|
|
1165
|
+
return a;
|
|
1166
|
+
};
|
|
1151
1167
|
class SelectedDiagnostic {
|
|
1152
1168
|
constructor(from, to, diagnostic) {
|
|
1153
1169
|
this.from = from;
|
|
@@ -1162,12 +1178,12 @@ class LintState {
|
|
|
1162
1178
|
this.selected = selected;
|
|
1163
1179
|
}
|
|
1164
1180
|
static init(diagnostics, panel, state$1) {
|
|
1165
|
-
let markedDiagnostics = diagnostics;
|
|
1166
1181
|
let diagnosticFilter = state$1.facet(lintConfig).markerFilter;
|
|
1167
1182
|
if (diagnosticFilter)
|
|
1168
|
-
|
|
1183
|
+
diagnostics = diagnosticFilter(diagnostics, state$1);
|
|
1169
1184
|
let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
|
|
1170
1185
|
let deco = new state.RangeSetBuilder(), active = [], pos = 0;
|
|
1186
|
+
let scan = state$1.doc.iter(), scanPos = 0, docLen = state$1.doc.length;
|
|
1171
1187
|
for (let i = 0; ; ) {
|
|
1172
1188
|
let next = i == sorted.length ? null : sorted[i];
|
|
1173
1189
|
if (!next && !active.length)
|
|
@@ -1178,6 +1194,8 @@ class LintState {
|
|
|
1178
1194
|
to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
|
|
1179
1195
|
} else {
|
|
1180
1196
|
from = next.from;
|
|
1197
|
+
if (from > docLen)
|
|
1198
|
+
break;
|
|
1181
1199
|
to = next.to;
|
|
1182
1200
|
active.push(next);
|
|
1183
1201
|
i++;
|
|
@@ -1193,8 +1211,31 @@ class LintState {
|
|
|
1193
1211
|
break;
|
|
1194
1212
|
}
|
|
1195
1213
|
}
|
|
1214
|
+
to = Math.min(to, docLen);
|
|
1215
|
+
let widget = false;
|
|
1216
|
+
if (active.some((d) => d.from == from && (d.to == to || to == docLen))) {
|
|
1217
|
+
widget = from == to;
|
|
1218
|
+
if (!widget && to - from < 10) {
|
|
1219
|
+
let behind = from - (scanPos + scan.value.length);
|
|
1220
|
+
if (behind > 0) {
|
|
1221
|
+
scan.next(behind);
|
|
1222
|
+
scanPos = from;
|
|
1223
|
+
}
|
|
1224
|
+
for (let check = from; ; ) {
|
|
1225
|
+
if (check >= to) {
|
|
1226
|
+
widget = true;
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
if (!scan.lineBreak && scanPos + scan.value.length > check)
|
|
1230
|
+
break;
|
|
1231
|
+
check = scanPos + scan.value.length;
|
|
1232
|
+
scanPos += scan.value.length;
|
|
1233
|
+
scan.next();
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1196
1237
|
let sev = maxSeverity(active);
|
|
1197
|
-
if (
|
|
1238
|
+
if (widget) {
|
|
1198
1239
|
deco.add(from, from, view.Decoration.widget({
|
|
1199
1240
|
widget: new DiagnosticWidget(sev),
|
|
1200
1241
|
diagnostics: active.slice()
|
|
@@ -1208,6 +1249,8 @@ class LintState {
|
|
|
1208
1249
|
}));
|
|
1209
1250
|
}
|
|
1210
1251
|
pos = to;
|
|
1252
|
+
if (pos == docLen)
|
|
1253
|
+
break;
|
|
1211
1254
|
for (let i2 = 0; i2 < active.length; i2++)
|
|
1212
1255
|
if (active[i2].to <= pos)
|
|
1213
1256
|
active.splice(i2--, 1);
|
|
@@ -1340,17 +1383,27 @@ const lintKeymap = [
|
|
|
1340
1383
|
];
|
|
1341
1384
|
const lintConfig = /* @__PURE__ */ state.Facet.define({
|
|
1342
1385
|
combine(input) {
|
|
1343
|
-
return
|
|
1386
|
+
return __spreadValues({
|
|
1387
|
+
sources: input.map((i) => i.source).filter((x) => x != null)
|
|
1388
|
+
}, state.combineConfig(input.map((i) => i.config), {
|
|
1344
1389
|
delay: 750,
|
|
1345
1390
|
markerFilter: null,
|
|
1346
1391
|
tooltipFilter: null,
|
|
1347
1392
|
needsRefresh: null,
|
|
1348
1393
|
hideOn: () => null
|
|
1349
1394
|
}, {
|
|
1350
|
-
|
|
1395
|
+
delay: Math.max,
|
|
1396
|
+
markerFilter: combineFilter,
|
|
1397
|
+
tooltipFilter: combineFilter,
|
|
1398
|
+
needsRefresh: (a, b) => !a ? b : !b ? a : (u) => a(u) || b(u),
|
|
1399
|
+
hideOn: (a, b) => !a ? b : !b ? a : (t, x, y) => a(t, x, y) || b(t, x, y),
|
|
1400
|
+
autoPanel: (a, b) => a || b
|
|
1351
1401
|
}));
|
|
1352
1402
|
}
|
|
1353
1403
|
});
|
|
1404
|
+
function combineFilter(a, b) {
|
|
1405
|
+
return !a ? b : !b ? a : (d, s) => b(a(d, s), s);
|
|
1406
|
+
}
|
|
1354
1407
|
function assignKeys(actions) {
|
|
1355
1408
|
let assigned = [];
|
|
1356
1409
|
if (actions)
|
|
@@ -1385,9 +1438,10 @@ function renderDiagnostic(view, diagnostic, inPanel) {
|
|
|
1385
1438
|
crelt("u", name.slice(keyIndex, keyIndex + 1)),
|
|
1386
1439
|
name.slice(keyIndex + 1)
|
|
1387
1440
|
];
|
|
1441
|
+
let markClass = action.markClass ? " " + action.markClass : "";
|
|
1388
1442
|
return crelt("button", {
|
|
1389
1443
|
type: "button",
|
|
1390
|
-
class: "cm-diagnosticAction",
|
|
1444
|
+
class: "cm-diagnosticAction" + markClass,
|
|
1391
1445
|
onclick: click,
|
|
1392
1446
|
onmousedown: click,
|
|
1393
1447
|
"aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
|
|
@@ -1749,7 +1803,8 @@ const bootstrap = view.EditorView.theme({
|
|
|
1749
1803
|
padding: "0 0.75rem"
|
|
1750
1804
|
},
|
|
1751
1805
|
".cm-content": {
|
|
1752
|
-
padding: "0.375rem 0"
|
|
1806
|
+
padding: "0.375rem 0",
|
|
1807
|
+
animation: "fade-to-colors 0.5s ease-out 0s forwards"
|
|
1753
1808
|
},
|
|
1754
1809
|
"&:not(.cm-focused) .cm-selectionBackground, &:not(.cm-focused) .cm-activeLine, &:not(.cm-focused) .cm-activeLineGutter": {
|
|
1755
1810
|
backgroundColor: "transparent"
|
|
@@ -1764,13 +1819,17 @@ const bootstrap = view.EditorView.theme({
|
|
|
1764
1819
|
overflow: "auto",
|
|
1765
1820
|
height: "100%",
|
|
1766
1821
|
lineHeight: 1.85,
|
|
1767
|
-
flexGrow: 1
|
|
1822
|
+
flexGrow: 1,
|
|
1823
|
+
animation: "fade 0.5s ease-out 0s forwards"
|
|
1768
1824
|
},
|
|
1769
1825
|
".cm-gutters": {
|
|
1770
1826
|
height: "100% !important",
|
|
1771
1827
|
color: "var(--bs-tertiary-color)",
|
|
1772
1828
|
backgroundColor: "var(--bs-tertiary-bg)"
|
|
1773
|
-
}
|
|
1829
|
+
},
|
|
1830
|
+
"@keyframes slide-left": { from: { transform: "translateX(-100%)", opacity: 0 } },
|
|
1831
|
+
"@keyframes fade-to-colors": { from: { filter: "grayscale(100%)" } },
|
|
1832
|
+
"@keyframes fade": { from: { opacity: 0 } }
|
|
1774
1833
|
});
|
|
1775
1834
|
|
|
1776
1835
|
const darkTheme = view.EditorView.theme({
|
|
@@ -1843,41 +1902,57 @@ var light = [
|
|
|
1843
1902
|
|
|
1844
1903
|
const getTheme = (el) => ({ light, dark })[el.dataset.bsTheme || "light"];
|
|
1845
1904
|
const mutationObserver = new MutationObserver(
|
|
1846
|
-
(changes) => changes.map((mutation) => mutation.target).filter((mutationTarget) => mutationTarget instanceof HTMLElement).map((themeContainer) =>
|
|
1905
|
+
(changes) => changes.map((mutation) => mutation.target).filter((mutationTarget) => mutationTarget instanceof HTMLElement).map((themeContainer) => editors.get(themeContainer).forEach((editor) => setTheme(themeContainer, editor)))
|
|
1847
1906
|
);
|
|
1907
|
+
const setTheme = (themeContainer, editor) => editor.dispatch({ effects: themes.get(editor).reconfigure(getTheme(themeContainer)) });
|
|
1848
1908
|
const themes = /* @__PURE__ */ new WeakMap();
|
|
1849
1909
|
const editors = /* @__PURE__ */ new WeakMap();
|
|
1850
1910
|
class ExpressionEditor extends HTMLTextAreaElement {
|
|
1851
1911
|
constructor() {
|
|
1852
1912
|
super();
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
mutationObserver.observe(themeContainer, { attributes: true, attributeFilter: ["data-bs-theme"] });
|
|
1856
|
-
themes.set(themeContainer, new state.Compartment());
|
|
1857
|
-
editors.set(themeContainer, /* @__PURE__ */ new Set());
|
|
1858
|
-
}
|
|
1913
|
+
this.theme = new state.Compartment();
|
|
1914
|
+
this.instanceStyles = new state.Compartment();
|
|
1859
1915
|
this.dom = document.createElement("div");
|
|
1916
|
+
const shadow = this.dom.attachShadow({ mode: "closed" });
|
|
1860
1917
|
this.editorView = new view.EditorView({
|
|
1861
1918
|
extensions: [
|
|
1862
|
-
|
|
1919
|
+
this.instanceStyles.of(view.EditorView.theme({})),
|
|
1863
1920
|
basicSetup({
|
|
1864
1921
|
useLineNumbers: JSON.parse(this.dataset.lineNumbers || "false")
|
|
1865
1922
|
}),
|
|
1866
|
-
|
|
1923
|
+
this.theme.of(light),
|
|
1867
1924
|
view.keymap.of([...commands.defaultKeymap, { key: "Tab", run: autocomplete.acceptCompletion }]),
|
|
1868
|
-
codemirrorLangEl.expressionlanguage(JSON.parse(this.dataset.config || "{}")
|
|
1925
|
+
codemirrorLangEl.expressionlanguage(JSON.parse(this.dataset.config || "{}")),
|
|
1869
1926
|
view.EditorView.updateListener.of((e) => {
|
|
1870
1927
|
if (e.docChanged) {
|
|
1871
1928
|
this.value = e.state.doc.toString();
|
|
1872
1929
|
}
|
|
1873
1930
|
})
|
|
1874
1931
|
],
|
|
1875
|
-
|
|
1932
|
+
root: shadow,
|
|
1933
|
+
parent: shadow,
|
|
1876
1934
|
doc: this.value.trim()
|
|
1877
1935
|
});
|
|
1936
|
+
themes.set(this.editorView, this.theme);
|
|
1937
|
+
}
|
|
1938
|
+
connectedCallback() {
|
|
1939
|
+
const themeContainer = this.closest("[data-bs-theme]") || document.documentElement;
|
|
1940
|
+
if (!editors.has(themeContainer)) {
|
|
1941
|
+
editors.set(themeContainer, /* @__PURE__ */ new Set());
|
|
1942
|
+
mutationObserver.observe(themeContainer, { attributes: true, attributeFilter: ["data-bs-theme"] });
|
|
1943
|
+
}
|
|
1944
|
+
if (editors.get(themeContainer).has(this.editorView)) {
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
setTheme(themeContainer, this.editorView);
|
|
1878
1948
|
editors.get(themeContainer).add(this.editorView);
|
|
1949
|
+
this.editorView.dispatch({
|
|
1950
|
+
effects: this.instanceStyles.reconfigure(view.EditorView.theme({
|
|
1951
|
+
"&": { minHeight: this.getBoundingClientRect().height + "px" },
|
|
1952
|
+
"& .cm-gutter": { minHeight: `${this.getBoundingClientRect().height - 2}px` }
|
|
1953
|
+
}))
|
|
1954
|
+
});
|
|
1879
1955
|
this.replaceWith(this.dom);
|
|
1880
|
-
this.hidden = true;
|
|
1881
1956
|
this.dom.appendChild(this);
|
|
1882
1957
|
}
|
|
1883
1958
|
}
|
package/dist/index.d.cts
CHANGED
package/dist/index.d.mts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -868,14 +868,14 @@ const replaceNext = /* @__PURE__ */ searchCommand((view, { query }) => {
|
|
|
868
868
|
next = query.nextMatch(state, next.from, next.to);
|
|
869
869
|
effects.push(EditorView.announce.of(state.phrase("replaced match on line $", state.doc.lineAt(from).number) + "."));
|
|
870
870
|
}
|
|
871
|
+
let changeSet = view.state.changes(changes);
|
|
871
872
|
if (next) {
|
|
872
|
-
|
|
873
|
-
selection = EditorSelection.single(next.from - off, next.to - off);
|
|
873
|
+
selection = EditorSelection.single(next.from, next.to).map(changeSet);
|
|
874
874
|
effects.push(announceMatch(view, next));
|
|
875
875
|
effects.push(state.facet(searchConfigFacet).scrollToMatch(selection.main, view));
|
|
876
876
|
}
|
|
877
877
|
view.dispatch({
|
|
878
|
-
changes,
|
|
878
|
+
changes: changeSet,
|
|
879
879
|
selection,
|
|
880
880
|
effects,
|
|
881
881
|
userEvent: "input.replace"
|
|
@@ -1146,6 +1146,22 @@ const searchExtensions = [
|
|
|
1146
1146
|
baseTheme$2
|
|
1147
1147
|
];
|
|
1148
1148
|
|
|
1149
|
+
var __defProp = Object.defineProperty;
|
|
1150
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
1151
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
1152
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
1153
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1154
|
+
var __spreadValues = (a, b) => {
|
|
1155
|
+
for (var prop in b || (b = {}))
|
|
1156
|
+
if (__hasOwnProp.call(b, prop))
|
|
1157
|
+
__defNormalProp(a, prop, b[prop]);
|
|
1158
|
+
if (__getOwnPropSymbols)
|
|
1159
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
1160
|
+
if (__propIsEnum.call(b, prop))
|
|
1161
|
+
__defNormalProp(a, prop, b[prop]);
|
|
1162
|
+
}
|
|
1163
|
+
return a;
|
|
1164
|
+
};
|
|
1149
1165
|
class SelectedDiagnostic {
|
|
1150
1166
|
constructor(from, to, diagnostic) {
|
|
1151
1167
|
this.from = from;
|
|
@@ -1160,12 +1176,12 @@ class LintState {
|
|
|
1160
1176
|
this.selected = selected;
|
|
1161
1177
|
}
|
|
1162
1178
|
static init(diagnostics, panel, state) {
|
|
1163
|
-
let markedDiagnostics = diagnostics;
|
|
1164
1179
|
let diagnosticFilter = state.facet(lintConfig).markerFilter;
|
|
1165
1180
|
if (diagnosticFilter)
|
|
1166
|
-
|
|
1181
|
+
diagnostics = diagnosticFilter(diagnostics, state);
|
|
1167
1182
|
let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
|
|
1168
1183
|
let deco = new RangeSetBuilder(), active = [], pos = 0;
|
|
1184
|
+
let scan = state.doc.iter(), scanPos = 0, docLen = state.doc.length;
|
|
1169
1185
|
for (let i = 0; ; ) {
|
|
1170
1186
|
let next = i == sorted.length ? null : sorted[i];
|
|
1171
1187
|
if (!next && !active.length)
|
|
@@ -1176,6 +1192,8 @@ class LintState {
|
|
|
1176
1192
|
to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
|
|
1177
1193
|
} else {
|
|
1178
1194
|
from = next.from;
|
|
1195
|
+
if (from > docLen)
|
|
1196
|
+
break;
|
|
1179
1197
|
to = next.to;
|
|
1180
1198
|
active.push(next);
|
|
1181
1199
|
i++;
|
|
@@ -1191,8 +1209,31 @@ class LintState {
|
|
|
1191
1209
|
break;
|
|
1192
1210
|
}
|
|
1193
1211
|
}
|
|
1212
|
+
to = Math.min(to, docLen);
|
|
1213
|
+
let widget = false;
|
|
1214
|
+
if (active.some((d) => d.from == from && (d.to == to || to == docLen))) {
|
|
1215
|
+
widget = from == to;
|
|
1216
|
+
if (!widget && to - from < 10) {
|
|
1217
|
+
let behind = from - (scanPos + scan.value.length);
|
|
1218
|
+
if (behind > 0) {
|
|
1219
|
+
scan.next(behind);
|
|
1220
|
+
scanPos = from;
|
|
1221
|
+
}
|
|
1222
|
+
for (let check = from; ; ) {
|
|
1223
|
+
if (check >= to) {
|
|
1224
|
+
widget = true;
|
|
1225
|
+
break;
|
|
1226
|
+
}
|
|
1227
|
+
if (!scan.lineBreak && scanPos + scan.value.length > check)
|
|
1228
|
+
break;
|
|
1229
|
+
check = scanPos + scan.value.length;
|
|
1230
|
+
scanPos += scan.value.length;
|
|
1231
|
+
scan.next();
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1194
1235
|
let sev = maxSeverity(active);
|
|
1195
|
-
if (
|
|
1236
|
+
if (widget) {
|
|
1196
1237
|
deco.add(from, from, Decoration.widget({
|
|
1197
1238
|
widget: new DiagnosticWidget(sev),
|
|
1198
1239
|
diagnostics: active.slice()
|
|
@@ -1206,6 +1247,8 @@ class LintState {
|
|
|
1206
1247
|
}));
|
|
1207
1248
|
}
|
|
1208
1249
|
pos = to;
|
|
1250
|
+
if (pos == docLen)
|
|
1251
|
+
break;
|
|
1209
1252
|
for (let i2 = 0; i2 < active.length; i2++)
|
|
1210
1253
|
if (active[i2].to <= pos)
|
|
1211
1254
|
active.splice(i2--, 1);
|
|
@@ -1338,17 +1381,27 @@ const lintKeymap = [
|
|
|
1338
1381
|
];
|
|
1339
1382
|
const lintConfig = /* @__PURE__ */ Facet.define({
|
|
1340
1383
|
combine(input) {
|
|
1341
|
-
return
|
|
1384
|
+
return __spreadValues({
|
|
1385
|
+
sources: input.map((i) => i.source).filter((x) => x != null)
|
|
1386
|
+
}, combineConfig(input.map((i) => i.config), {
|
|
1342
1387
|
delay: 750,
|
|
1343
1388
|
markerFilter: null,
|
|
1344
1389
|
tooltipFilter: null,
|
|
1345
1390
|
needsRefresh: null,
|
|
1346
1391
|
hideOn: () => null
|
|
1347
1392
|
}, {
|
|
1348
|
-
|
|
1393
|
+
delay: Math.max,
|
|
1394
|
+
markerFilter: combineFilter,
|
|
1395
|
+
tooltipFilter: combineFilter,
|
|
1396
|
+
needsRefresh: (a, b) => !a ? b : !b ? a : (u) => a(u) || b(u),
|
|
1397
|
+
hideOn: (a, b) => !a ? b : !b ? a : (t, x, y) => a(t, x, y) || b(t, x, y),
|
|
1398
|
+
autoPanel: (a, b) => a || b
|
|
1349
1399
|
}));
|
|
1350
1400
|
}
|
|
1351
1401
|
});
|
|
1402
|
+
function combineFilter(a, b) {
|
|
1403
|
+
return !a ? b : !b ? a : (d, s) => b(a(d, s), s);
|
|
1404
|
+
}
|
|
1352
1405
|
function assignKeys(actions) {
|
|
1353
1406
|
let assigned = [];
|
|
1354
1407
|
if (actions)
|
|
@@ -1383,9 +1436,10 @@ function renderDiagnostic(view, diagnostic, inPanel) {
|
|
|
1383
1436
|
crelt("u", name.slice(keyIndex, keyIndex + 1)),
|
|
1384
1437
|
name.slice(keyIndex + 1)
|
|
1385
1438
|
];
|
|
1439
|
+
let markClass = action.markClass ? " " + action.markClass : "";
|
|
1386
1440
|
return crelt("button", {
|
|
1387
1441
|
type: "button",
|
|
1388
|
-
class: "cm-diagnosticAction",
|
|
1442
|
+
class: "cm-diagnosticAction" + markClass,
|
|
1389
1443
|
onclick: click,
|
|
1390
1444
|
onmousedown: click,
|
|
1391
1445
|
"aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
|
|
@@ -1747,7 +1801,8 @@ const bootstrap = EditorView.theme({
|
|
|
1747
1801
|
padding: "0 0.75rem"
|
|
1748
1802
|
},
|
|
1749
1803
|
".cm-content": {
|
|
1750
|
-
padding: "0.375rem 0"
|
|
1804
|
+
padding: "0.375rem 0",
|
|
1805
|
+
animation: "fade-to-colors 0.5s ease-out 0s forwards"
|
|
1751
1806
|
},
|
|
1752
1807
|
"&:not(.cm-focused) .cm-selectionBackground, &:not(.cm-focused) .cm-activeLine, &:not(.cm-focused) .cm-activeLineGutter": {
|
|
1753
1808
|
backgroundColor: "transparent"
|
|
@@ -1762,13 +1817,17 @@ const bootstrap = EditorView.theme({
|
|
|
1762
1817
|
overflow: "auto",
|
|
1763
1818
|
height: "100%",
|
|
1764
1819
|
lineHeight: 1.85,
|
|
1765
|
-
flexGrow: 1
|
|
1820
|
+
flexGrow: 1,
|
|
1821
|
+
animation: "fade 0.5s ease-out 0s forwards"
|
|
1766
1822
|
},
|
|
1767
1823
|
".cm-gutters": {
|
|
1768
1824
|
height: "100% !important",
|
|
1769
1825
|
color: "var(--bs-tertiary-color)",
|
|
1770
1826
|
backgroundColor: "var(--bs-tertiary-bg)"
|
|
1771
|
-
}
|
|
1827
|
+
},
|
|
1828
|
+
"@keyframes slide-left": { from: { transform: "translateX(-100%)", opacity: 0 } },
|
|
1829
|
+
"@keyframes fade-to-colors": { from: { filter: "grayscale(100%)" } },
|
|
1830
|
+
"@keyframes fade": { from: { opacity: 0 } }
|
|
1772
1831
|
});
|
|
1773
1832
|
|
|
1774
1833
|
const darkTheme = EditorView.theme({
|
|
@@ -1841,41 +1900,57 @@ var light = [
|
|
|
1841
1900
|
|
|
1842
1901
|
const getTheme = (el) => ({ light, dark })[el.dataset.bsTheme || "light"];
|
|
1843
1902
|
const mutationObserver = new MutationObserver(
|
|
1844
|
-
(changes) => changes.map((mutation) => mutation.target).filter((mutationTarget) => mutationTarget instanceof HTMLElement).map((themeContainer) =>
|
|
1903
|
+
(changes) => changes.map((mutation) => mutation.target).filter((mutationTarget) => mutationTarget instanceof HTMLElement).map((themeContainer) => editors.get(themeContainer).forEach((editor) => setTheme(themeContainer, editor)))
|
|
1845
1904
|
);
|
|
1905
|
+
const setTheme = (themeContainer, editor) => editor.dispatch({ effects: themes.get(editor).reconfigure(getTheme(themeContainer)) });
|
|
1846
1906
|
const themes = /* @__PURE__ */ new WeakMap();
|
|
1847
1907
|
const editors = /* @__PURE__ */ new WeakMap();
|
|
1848
1908
|
class ExpressionEditor extends HTMLTextAreaElement {
|
|
1849
1909
|
constructor() {
|
|
1850
1910
|
super();
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
mutationObserver.observe(themeContainer, { attributes: true, attributeFilter: ["data-bs-theme"] });
|
|
1854
|
-
themes.set(themeContainer, new Compartment());
|
|
1855
|
-
editors.set(themeContainer, /* @__PURE__ */ new Set());
|
|
1856
|
-
}
|
|
1911
|
+
this.theme = new Compartment();
|
|
1912
|
+
this.instanceStyles = new Compartment();
|
|
1857
1913
|
this.dom = document.createElement("div");
|
|
1914
|
+
const shadow = this.dom.attachShadow({ mode: "closed" });
|
|
1858
1915
|
this.editorView = new EditorView({
|
|
1859
1916
|
extensions: [
|
|
1860
|
-
|
|
1917
|
+
this.instanceStyles.of(EditorView.theme({})),
|
|
1861
1918
|
basicSetup({
|
|
1862
1919
|
useLineNumbers: JSON.parse(this.dataset.lineNumbers || "false")
|
|
1863
1920
|
}),
|
|
1864
|
-
|
|
1921
|
+
this.theme.of(light),
|
|
1865
1922
|
keymap.of([...defaultKeymap, { key: "Tab", run: acceptCompletion }]),
|
|
1866
|
-
expressionlanguage(JSON.parse(this.dataset.config || "{}")
|
|
1923
|
+
expressionlanguage(JSON.parse(this.dataset.config || "{}")),
|
|
1867
1924
|
EditorView.updateListener.of((e) => {
|
|
1868
1925
|
if (e.docChanged) {
|
|
1869
1926
|
this.value = e.state.doc.toString();
|
|
1870
1927
|
}
|
|
1871
1928
|
})
|
|
1872
1929
|
],
|
|
1873
|
-
|
|
1930
|
+
root: shadow,
|
|
1931
|
+
parent: shadow,
|
|
1874
1932
|
doc: this.value.trim()
|
|
1875
1933
|
});
|
|
1934
|
+
themes.set(this.editorView, this.theme);
|
|
1935
|
+
}
|
|
1936
|
+
connectedCallback() {
|
|
1937
|
+
const themeContainer = this.closest("[data-bs-theme]") || document.documentElement;
|
|
1938
|
+
if (!editors.has(themeContainer)) {
|
|
1939
|
+
editors.set(themeContainer, /* @__PURE__ */ new Set());
|
|
1940
|
+
mutationObserver.observe(themeContainer, { attributes: true, attributeFilter: ["data-bs-theme"] });
|
|
1941
|
+
}
|
|
1942
|
+
if (editors.get(themeContainer).has(this.editorView)) {
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
setTheme(themeContainer, this.editorView);
|
|
1876
1946
|
editors.get(themeContainer).add(this.editorView);
|
|
1947
|
+
this.editorView.dispatch({
|
|
1948
|
+
effects: this.instanceStyles.reconfigure(EditorView.theme({
|
|
1949
|
+
"&": { minHeight: this.getBoundingClientRect().height + "px" },
|
|
1950
|
+
"& .cm-gutter": { minHeight: `${this.getBoundingClientRect().height - 2}px` }
|
|
1951
|
+
}))
|
|
1952
|
+
});
|
|
1877
1953
|
this.replaceWith(this.dom);
|
|
1878
|
-
this.hidden = true;
|
|
1879
1954
|
this.dom.appendChild(this);
|
|
1880
1955
|
}
|
|
1881
1956
|
}
|
package/example.html
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<style>
|
|
4
|
+
textarea[is="expression-editor"] {
|
|
5
|
+
animation: fadeTextIn 1.0s ease-in-out 0s forwards;
|
|
6
|
+
font-family: monospace;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
@keyframes fadeTextIn { from { color: rgba(var(--bs-body-color-rgb), 0); } }
|
|
10
|
+
</style>
|
|
11
|
+
</head>
|
|
2
12
|
<body class="bg-body-secondary container">
|
|
3
13
|
<script type="importmap">
|
|
4
14
|
{
|
|
@@ -34,13 +44,22 @@
|
|
|
34
44
|
<input class="form-check-input" type="checkbox" role="switch" id="light-switch" onchange="this.closest('.section').dataset.bsTheme=this.checked ? 'light' : 'dark'">
|
|
35
45
|
<label class="form-check-label" for="light-switch">Lights</label>
|
|
36
46
|
</div>
|
|
37
|
-
<div>
|
|
38
|
-
<textarea rows="
|
|
39
|
-
x == x + 1
|
|
47
|
+
<div id="template">
|
|
48
|
+
<textarea rows="3" class="form-control" is="expression-editor" data-line-numbers="true" data-config="{"identifiers":[{"name":"x"}]}">x == x + 1
|
|
40
49
|
&& 'foobar' starts with 'foo'
|
|
41
|
-
&& x == x + 1
|
|
42
|
-
|
|
50
|
+
&& x == x + 1</textarea>
|
|
51
|
+
</div>
|
|
52
|
+
<div>
|
|
53
|
+
<button type="button" class="btn btn-sm btn-secondary mt-3" id="clone">Clone</button>
|
|
43
54
|
</div>
|
|
44
55
|
</div>
|
|
56
|
+
<script type="module">
|
|
57
|
+
document.getElementById('clone').addEventListener('click', function () {
|
|
58
|
+
const template = document.getElementById('template');
|
|
59
|
+
const clone = template.cloneNode(true);
|
|
60
|
+
clone.removeAttribute('id');
|
|
61
|
+
template.parentNode.insertBefore(clone, this.parentElement);
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
45
64
|
</body>
|
|
46
65
|
</html>
|
package/package.json
CHANGED
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
"build": "pkgroll",
|
|
18
18
|
"dev": "pkgroll --watch",
|
|
19
19
|
"pretest": "npm run-script build",
|
|
20
|
-
"test": "
|
|
20
|
+
"test": "playwright test",
|
|
21
|
+
"test:e2e": "playwright test"
|
|
21
22
|
},
|
|
22
23
|
"description": "Advanced editor for Symfony Expression Language",
|
|
23
24
|
"dependencies": {
|
|
@@ -32,7 +33,10 @@
|
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"pkgroll": "^2.11.2",
|
|
35
|
-
"typescript": "^5.8.2"
|
|
36
|
+
"typescript": "^5.8.2",
|
|
37
|
+
"@types/node": "^20.11.0",
|
|
38
|
+
"@playwright/test": "^1.40.0",
|
|
39
|
+
"playwright": "^1.40.0"
|
|
36
40
|
},
|
|
37
41
|
"license": "MIT",
|
|
38
42
|
"repository": {
|
|
@@ -43,5 +47,5 @@
|
|
|
43
47
|
"access": "public",
|
|
44
48
|
"registry": "https://registry.npmjs.org/"
|
|
45
49
|
},
|
|
46
|
-
"version": "0.
|
|
50
|
+
"version": "1.0.0"
|
|
47
51
|
}
|