Skip to content

Commit 3e7e651

Browse files
Complete method arguments visual editing feature
1 parent 821c22d commit 3e7e651

File tree

8 files changed

+315
-47
lines changed

8 files changed

+315
-47
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "cache-visual-editor",
33
"printableName": "Cache Visual Editor",
44
"packageName": "VisualEditor",
5-
"version": "0.8.7",
5+
"version": "0.9.0",
66
"description": "Visual class editor for InterSystems Caché",
77
"main": "index.js",
88
"keywords": [

source/client/js/classEditor/class/code.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import { block, toggle } from "../../domUtils";
1+
import { block, toggle, autoSizeInput } from "../../domUtils";
22
import { addChange } from "../changes";
33
import { Toast } from "../../toast";
44
import { updateGrid } from "../index";
5-
import { getKeywordView } from "./keyword";
65
import { getFormalSpecEditor } from "./formalSpecEditor";
76

87
/**
@@ -34,6 +33,11 @@ export function getCodeCaptionView ({ manifest, name, data, savePath }) {
3433
useRoutinesBlock = block(`div`, `property-block`),
3534
nb = block(`div`, `name-block`),
3635
vb = block(`div`, `value-block`),
36+
signatureElem = block(`div`, `methodSignature`),
37+
typeInput = autoSizeInput({
38+
placeholder: `Type`,
39+
value: returnTypeProp ? data[returnTypeProp] : data[`returnType`] || ""
40+
}),
3741
editor;
3842

3943
function saveChanges () {
@@ -45,17 +49,19 @@ export function getCodeCaptionView ({ manifest, name, data, savePath }) {
4549
vb.appendChild(useRoutinesToggle);
4650
useRoutinesBlock.appendChild(nb);
4751
useRoutinesBlock.appendChild(vb);
48-
header.appendChild(getFormalSpecEditor({
52+
signatureElem.appendChild(block(`span`, `secondary`, `Takes `));
53+
signatureElem.appendChild(getFormalSpecEditor({
4954
formalSpec: data[`FormalSpec`],
5055
savePath: savePath.concat(`FormalSpec`)
5156
}));
52-
if (returnTypeProp)
53-
header.appendChild(getKeywordView({
54-
propManifest: manifest[returnTypeProp],
55-
propName: returnTypeProp,
56-
propData: data["ReturnType"],
57-
savePath: savePath.concat(returnTypeProp)
58-
}));
57+
if (returnTypeProp) {
58+
signatureElem.appendChild(block(`span`, `secondary`, ` Returns `));
59+
signatureElem.appendChild(typeInput);
60+
typeInput.addEventListener(`input`, () => {
61+
addChange(savePath.concat(returnTypeProp), typeInput.value);
62+
});
63+
}
64+
header.appendChild(signatureElem);
5965
header.appendChild(useRoutinesBlock);
6066

6167
useRoutinesToggle.checkbox.addEventListener("change", () => {
@@ -93,7 +99,7 @@ export function getCodeCaptionView ({ manifest, name, data, savePath }) {
9399
});
94100

95101
let hr = block(`hr`);
96-
hr.setAttribute(`title`, `Code Editor`);
102+
hr.setAttribute(`title`, `${ data["Name"] ? data["Name"] + " " : "" }Code Editor`);
97103
div.appendChild(hr);
98104
div.appendChild(header);
99105
// setTimeout(() => new AutoGrid(header), 1);

source/client/js/classEditor/class/formalSpecEditor.js

Lines changed: 240 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { getTypePickerView } from "./typePicker";
2-
import { block, autoSizeInput } from "../../domUtils";
1+
import { block, autoSizeInput, awaitInlineInput } from "../../domUtils";
2+
import { addChange } from "../changes";
33

44
/**
55
* Replaces the strings with spaces
@@ -16,7 +16,7 @@ function padSpace (string) {
1616
/**
1717
* A hero function that handles formalSpec string like 'test2:%String(VALUELIST="1,2,3",MAXLEN=1)={"test"+"best"+{"a":1}},noType,typeOnly:Cinema.TicketOrder={##class(TestPack.MyType).%New("AHA!, AHAHA, AA!",1)},defaultOnly="lol"'
1818
* @param {string} formalSpec =
19-
* @returns {string[]}
19+
* @returns {*[]}
2020
*/
2121
function splitArguments (formalSpec = "") {
2222

@@ -90,33 +90,246 @@ function hide (element) {
9090
element.style.display = "none";
9191
}
9292

93-
export function getFormalSpecEditor ({ formalSpec = "", savePath }) {
93+
function getParameterView (model, save, del, blur) {
94+
95+
let span = block(`span`, `parameter`),
96+
eq = block(`span`, `secondary`, ` = `),
97+
nameInput = autoSizeInput({
98+
placeholder: `Name`,
99+
value: model.name || ""
100+
}),
101+
valueInput = autoSizeInput({
102+
placeholder: `Value`,
103+
value: model.value || ""
104+
});
105+
106+
span.appendChild(nameInput);
107+
span.appendChild(eq);
108+
span.appendChild(valueInput);
109+
110+
nameInput.addEventListener(`input`, () => {
111+
model.name = nameInput.value;
112+
save();
113+
});
114+
valueInput.addEventListener(`input`, () => {
115+
model.value = valueInput.value;
116+
save();
117+
});
118+
nameInput.addEventListener(`blur`, () => {
119+
if (nameInput.value !== "")
120+
return;
121+
if (span.parentNode)
122+
span.parentNode.removeChild(span);
123+
del(model);
124+
});
125+
nameInput.addEventListener(`blur`, blur);
126+
valueInput.addEventListener(`blur`, blur);
127+
128+
return span;
129+
130+
}
94131

95-
let container = block(`div`, `formalSpecEdit`),
96-
args = splitArguments(formalSpec);
97-
98-
args.forEach((arg, i) => {
99-
100-
let span = block(`span`, `argument`),
101-
name = autoSizeInput({ placeholder: `Name`, value: arg.name, className: `nameInput` }),
102-
asSpan = block(`span`, `typeSpan`),
103-
defaultSpan = block(`span`);
104-
105-
if (i > 0)
106-
span.appendChild(block(`span`, ``, `, `));
107-
108-
span.appendChild(name);
109-
asSpan.appendChild(block(`span`, ``, ` As `));
110-
let typeInput = autoSizeInput({ placeholder: `Type`, value: arg.type || "" });
111-
if (!arg.type) {
112-
hide(asSpan);
132+
function getParamsSpan (parameters = [], save, blur) {
133+
134+
let paramsSpan = block(`span`, `parametersSpan`),
135+
paramPlus = block(`span`, `parameter`),
136+
plusEl = block(`span`, `interactive small add icon`);
137+
138+
function del (model) {
139+
for (let i = 0; i < parameters.length; i++) {
140+
if (parameters[i] === model) {
141+
parameters.splice(i, 1);
142+
break;
143+
}
113144
}
114-
asSpan.appendChild(typeInput);
115-
span.appendChild(asSpan);
116-
span.appendChild(defaultSpan);
117-
118-
container.appendChild(span);
145+
save();
146+
}
147+
148+
for (let p of parameters) {
149+
paramsSpan.appendChild(getParameterView(p, save, del, blur));
150+
}
151+
152+
paramPlus.appendChild(plusEl);
153+
paramsSpan.appendChild(paramPlus);
154+
plusEl.addEventListener(`click`, () => awaitInlineInput(plusEl, {
155+
placeholder: `New Parameter...`
156+
}, (cancelled, name) => {
157+
158+
if (cancelled)
159+
return;
160+
161+
let model = {
162+
name: name,
163+
value: ""
164+
};
165+
166+
paramsSpan.insertBefore(getParameterView(model, save, del, blur), paramPlus);
167+
parameters.push(model);
168+
169+
}));
170+
171+
return paramsSpan;
172+
173+
}
174+
175+
/**
176+
* @param {*} model
177+
* @param {function} save - Function that saves the model.
178+
* @param {function} remove - Function that removes the model part and saves the new model as well.
179+
* @returns {Element}
180+
*/
181+
function getArgumentView (model, save, remove) {
182+
183+
let argumentSpan = block(`span`, `argument`),
184+
nameInput = autoSizeInput({ placeholder: `Name`, value: model.name, className: `nameInput` }),
185+
asSpan = block(`span`, `typeSpan`),
186+
defaultSpan = block(`span`),
187+
paramsSpan = getParamsSpan(model.parameters, save, blur);
188+
189+
function paramsSpanIsActive () {
190+
return document.activeElement
191+
&& (document.activeElement.parentNode === paramsSpan
192+
|| document.activeElement.parentNode.parentNode === paramsSpan);
193+
}
194+
195+
function blur () {
196+
setTimeout(() => {
197+
if (
198+
document.activeElement !== defaultInput
199+
&& document.activeElement !== typeInput
200+
&& document.activeElement !== nameInput
201+
&& typeInput.value === ""
202+
) {
203+
hide(asSpan);
204+
}
205+
if (
206+
document.activeElement !== defaultInput
207+
&& document.activeElement !== typeInput
208+
&& document.activeElement !== nameInput
209+
&& defaultInput.value === ""
210+
) {
211+
hide(defaultSpan);
212+
}
213+
if (
214+
document.activeElement !== defaultInput
215+
&& document.activeElement !== typeInput
216+
&& document.activeElement !== nameInput
217+
&& !paramsSpanIsActive()
218+
&& (model.parameters || []).length === 0
219+
) {
220+
hide(paramsSpan);
221+
}
222+
if (nameInput.value === "") {
223+
if (argumentSpan.parentNode)
224+
argumentSpan.parentNode.removeChild(argumentSpan);
225+
remove(model);
226+
}
227+
}, 200);
228+
}
229+
230+
argumentSpan.appendChild(nameInput);
231+
232+
asSpan.appendChild(block(`span`, `asSpan`, ` As `));
233+
let typeInput = autoSizeInput({ placeholder: `Type`, value: model.type || "" });
234+
if (!model.type) {
235+
hide(asSpan);
236+
}
237+
asSpan.appendChild(typeInput);
238+
argumentSpan.appendChild(asSpan);
239+
240+
if (!model.parameters || !model.parameters.length) {
241+
hide(paramsSpan);
242+
}
243+
argumentSpan.appendChild(paramsSpan);
244+
245+
defaultSpan.appendChild(block(`span`, `defSpan`, ` = `));
246+
let defaultInput = autoSizeInput({ placeholder: `Default`, value: model.default || "" });
247+
if (!model.default) {
248+
hide(defaultSpan);
249+
}
250+
defaultSpan.appendChild(defaultInput);
251+
argumentSpan.appendChild(defaultSpan);
252+
253+
nameInput.addEventListener(`focus`, () => {
254+
if (typeInput.value === "")
255+
show(asSpan);
256+
if (defaultInput.value === "")
257+
show(defaultSpan);
258+
});
259+
nameInput.addEventListener(`blur`, () => blur());
260+
typeInput.addEventListener(`blur`, () => blur());
261+
defaultInput.addEventListener(`blur`, () => blur());
262+
nameInput.addEventListener(`input`, () => {
263+
if (!nameInput.value)
264+
return;
265+
model.name = nameInput.value;
266+
save();
267+
});
268+
typeInput.addEventListener(`input`, () => {
269+
model.type = typeInput.value;
270+
save();
119271
});
272+
defaultInput.addEventListener(`input`, () => {
273+
model.default = defaultInput.value;
274+
save();
275+
});
276+
typeInput.addEventListener(`focus`, () => {
277+
if (typeInput.value === "")
278+
return;
279+
show(paramsSpan);
280+
});
281+
282+
return argumentSpan;
283+
284+
}
285+
286+
export function getFormalSpecEditor ({ formalSpec = "", savePath }) {
287+
288+
let container = block(`span`, `formalSpecEdit`),
289+
args = splitArguments(formalSpec),
290+
plusArg = block(`span`, `argument`),
291+
plusEl = block(`div`, `interactive small add icon`);
292+
293+
function save () {
294+
let formalSpec = [];
295+
for (let fs of args) {
296+
formalSpec.push(`${ fs.name }${ fs.type ? ":" + fs.type : "" }${
297+
fs.type && fs.parameters && fs.parameters.length
298+
? "(" + fs.parameters.map(p => p.name + "=" + p.value).join(",") + ")"
299+
: ""
300+
}${ fs.default ? "=" + fs.default : "" }`);
301+
}
302+
addChange(savePath, formalSpec.join(`,`));
303+
}
304+
305+
function del (model) {
306+
for (let i = 0; i < args.length; i++) {
307+
if (args[i] === model) {
308+
args.splice(i, 1);
309+
break;
310+
}
311+
}
312+
save();
313+
}
314+
315+
args.forEach((arg) => container.appendChild(getArgumentView(arg, save, del)));
316+
plusArg.appendChild(plusEl);
317+
container.appendChild(plusArg);
318+
plusEl.addEventListener(`click`, () => awaitInlineInput(plusEl, {
319+
placeholder: `New Argument...`
320+
}, (cancelled, name) => {
321+
322+
if (cancelled)
323+
return;
324+
325+
let model = {
326+
name: name
327+
};
328+
329+
container.insertBefore(getArgumentView(model, save, del), plusArg);
330+
args.push(model);
331+
332+
}));
120333

121334
return container;
122335

source/client/scss/basic.scss

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ html, body {
55
margin: 0;
66
padding: 0;
77
background: $colorBack;
8+
color: $colorTextPrimary;
89
font-family: Verdana, Geneva, sans-serif;
910
font-size: 18px;
1011
}
@@ -19,6 +20,10 @@ html, body {
1920
color: $colorP2;
2021
}
2122

23+
span.secondary {
24+
color: $colorTextSecondary;
25+
}
26+
2227
.central {
2328

2429
position: absolute;
@@ -82,7 +87,7 @@ hr {
8287

8388
position: relative;
8489
border: none;
85-
border-top: 1px solid #ccc;
90+
border-top: 1px solid $colorTextSecondary;
8691
margin: .5em;
8792

8893
&:after {
@@ -93,7 +98,7 @@ hr {
9398
font-family: inherit;
9499
width: 100%;
95100
text-align: center;
96-
color: #ccc;
101+
color: $colorTextSecondary;
97102
text-shadow: 0 -1px 2px #fff, 0 1px 2px #fff;
98103
}
99104

0 commit comments

Comments
 (0)