Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor!: convert to TypeScript #1171

Merged
merged 20 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"sourceType": "module"
},
"rules": {
"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-expressions": "error"
},
"settings": {
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,6 @@ typings/

# Built TypeScript files
lib/*.js
webpack.config.js
lib/*.js.map
editor/**/*.js
editor/**/*.js.map
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ node_modules
# This can be removed after https://github.com/googleapis/release-please/issues/1802 is fixed
CHANGELOG.md
# TSC output:
webpack.config.js
lib/*.js
editor/js/**/*.js
65 changes: 39 additions & 26 deletions editor/js/editable-css.js → editor/js/editable-css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,29 @@ import "../css/editor-libs/common.css";
import "../css/editable-css.css";

(function () {
const exampleChoiceList = document.getElementById("example-choice-list");
const exampleChoices = exampleChoiceList.querySelectorAll(".example-choice");
const exampleChoiceList = document.getElementById(
"example-choice-list",
) as HTMLElement;

const exampleChoices = exampleChoiceList.querySelectorAll(
".example-choice",
) as NodeListOf<HTMLElement>;
const exampleDeclarations = Array.from(
exampleChoices,
(choice) => choice.querySelector("code").textContent,
(choice) => choice.querySelector("code")?.textContent,
);
const editorWrapper = document.getElementById("editor-wrapper");
const output = document.getElementById("output");
const warningNoSupport = document.getElementById("warning-no-support");

const originalChoices = [];
const editorWrapper = document.getElementById(
"editor-wrapper",
) as HTMLElement;
const output = document.getElementById("output") as HTMLElement;
const warningNoSupport = document.getElementById(
"warning-no-support",
) as HTMLElement;

const originalChoices: string[] = [];
let initialChoice = 0;

function applyCodeMirror(target, code) {
function applyCodeMirror(target: HTMLElement, code: string) {
return initCodeEditor(target, code, languageCSS(), {
lineNumbers: false,
});
Expand All @@ -42,14 +51,16 @@ import "../css/editable-css.css";
const exampleChoice = exampleChoices[i];
const choiceButton = document.createElement("button");
const choiceButtonText = document.createElement("span");
const choiceCode = exampleChoice.querySelector("code");
const copyButton = exampleChoice.getElementsByClassName("copy")[0];
const choiceCode = exampleChoice.querySelector("code") as HTMLElement;
const copyButton = exampleChoice.getElementsByClassName(
"copy",
)[0] as HTMLButtonElement;

originalChoices.push(choiceCode.textContent);
originalChoices.push(choiceCode.textContent || "");

const codeMirrorEditor = applyCodeMirror(
exampleChoice.querySelector("pre"),
choiceCode.textContent,
exampleChoice.querySelector("pre") as HTMLElement,
choiceCode.textContent || "",
);

choiceButton.setAttribute("type", "button");
Expand Down Expand Up @@ -82,11 +93,12 @@ import "../css/editable-css.css";
* reset all the CSS examples to their original state
*/
function handleResetEvents() {
const resetButton = document.getElementById("reset");
const resetButton = document.getElementById("reset") as HTMLElement;

resetButton.addEventListener("click", () => {
exampleChoices.forEach((e, i) => {
const preEl = e.querySelector("pre");
const preEl = e.querySelector("pre") as HTMLElement;

// Remove original codemirror
for (const e of preEl.children) {
e.remove();
Expand All @@ -100,16 +112,16 @@ import "../css/editable-css.css";

// if there is an initial choice set, set it as selected
if (initialChoice) {
mceEvents.onChoose(exampleChoices[initialChoice]);
clippy.toggleClippy(exampleChoices[initialChoice]);
mceEvents.onChoose(exampleChoices[initialChoice] as HTMLElement);
clippy.toggleClippy(exampleChoices[initialChoice] as HTMLElement);
} else {
mceEvents.onChoose(exampleChoices[0]);
clippy.toggleClippy(exampleChoices[0]);
mceEvents.onChoose(exampleChoices[0] as HTMLElement);
clippy.toggleClippy(exampleChoices[0] as HTMLElement);
}
});
}

function indexOf(exampleChoices, choice) {
function indexOf(exampleChoices: NodeListOf<Element>, choice: Element) {
for (let i = 0, l = exampleChoices.length; i < l; i++) {
if (exampleChoices[i] === choice) {
return i;
Expand All @@ -126,23 +138,24 @@ import "../css/editable-css.css";
function handleChoiceHover() {
for (let i = 0, l = exampleChoices.length; i < l; i++) {
const choice = exampleChoices[i];
const copyBtn = choice.querySelector(".copy");
const copyBtn = choice.querySelector(".copy") as HTMLElement;

copyBtn.setAttribute("aria-label", "Copy to clipboard");

choice.addEventListener("mouseover", () => {
copyBtn.setAttribute("aria-hidden", false);
copyBtn.setAttribute("aria-hidden", "false");
});
choice.addEventListener("mouseout", () => {
copyBtn.setAttribute("aria-hidden", true);
copyBtn.setAttribute("aria-hidden", "true");
});
}
}

/* only show the live code view if JS is enabled and the property is supported. */
if (cssEditorUtils.isAnyDeclarationSetSupported(exampleDeclarations)) {
enableLiveEditor();
mceEvents.onChoose(exampleChoices[initialChoice]);
clippy.toggleClippy(exampleChoices[initialChoice]);
mceEvents.onChoose(exampleChoices[initialChoice] as HTMLElement);
clippy.toggleClippy(exampleChoices[initialChoice] as HTMLElement);
} else {
warningNoSupport.classList.remove("hidden");
}
Expand Down
43 changes: 27 additions & 16 deletions editor/js/editable-js.js → editor/js/editable-js.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { EditorView } from "codemirror";
import * as featureDetector from "./editor-libs/feature-detector.js";
import mceConsole from "./editor-libs/console.js";
import * as mceEvents from "./editor-libs/events.js";
Expand All @@ -12,35 +13,40 @@ import {
} from "./editor-libs/codemirror-editor.js";

(function () {
const codeBlock = document.getElementById("static-js");
const codeBlock = document.getElementById("static-js") as HTMLElement;

const exampleFeature = codeBlock.dataset["feature"];
const execute = document.getElementById("execute");
const output = document.querySelector("#console code");
const reset = document.getElementById("reset");
const execute = document.getElementById("execute") as HTMLElement;
const output = document.querySelector("#console code") as HTMLElement;
const reset = document.getElementById("reset") as HTMLElement;

let codeMirror;
let codeMirror: EditorView | null;
let staticContainer;
let liveContainer = "";
let liveContainer;

/**
* Reads the textContent from the interactiveCodeBlock, sends the
* textContent to executeLiveExample, and logs the output to the
* output container
*/
function applyCode() {
const currentValue = getEditorContent(codeMirror);
if (!codeMirror) {
initCodeMirror();
// "as EditorView" on next line needed to trick TypeScript
}
const currentValue = getEditorContent(codeMirror as EditorView);
updateOutput(currentValue);
}

/**
* Initialize CodeMirror
*/
function initCodeMirror() {
const editorContainer = document.getElementById("editor");
const editorContainer = document.getElementById("editor") as HTMLElement;

codeMirror = initCodeEditor(
editorContainer,
codeBlock.textContent,
codeBlock.textContent || "",
languageJavaScript(),
);
}
Expand All @@ -51,15 +57,15 @@ import {
function initInteractiveEditor() {
/* If the `data-height` attribute is defined on the `codeBlock`, set
the value of this attribute as a class on the editor element. */
if (codeBlock.dataset["height"]) {
const editor = document.getElementById("editor");
if (codeBlock?.dataset["height"]) {
const editor = document.getElementById("editor") as HTMLElement;
editor.classList.add(codeBlock.dataset["height"]);
}

staticContainer = document.getElementById("static");
staticContainer = document.getElementById("static") as HTMLElement;
staticContainer.classList.add("hidden");

liveContainer = document.getElementById("live");
liveContainer = document.getElementById("live") as HTMLElement;
liveContainer.classList.remove("hidden");

mceConsole();
Expand All @@ -73,21 +79,22 @@ import {
* to the output container.
* @param {String} exampleCode - The code to execute
*/
function updateOutput(exampleCode) {
function updateOutput(exampleCode: string) {
output.classList.add("fade-in");

try {
// Create a new Function from the code, and immediately execute it.
new Function(exampleCode)();
} catch (event) {
output.textContent = "Error: " + event.message;
} catch (event: unknown) {
output.textContent = "Error: " + (event as Error)?.message;
}

output.addEventListener("animationend", () =>
output.classList.remove("fade-in"),
);
}

/* only execute code in supported browsers */
if (featureDetector.isDefined(exampleFeature)) {
document.documentElement.classList.add("js");

Expand All @@ -99,5 +106,9 @@ import {
});

reset.addEventListener("click", () => window.location.reload());
} else {
console.warn(
`Feature ${exampleFeature} is not supported; code editor disabled.`,
);
}
})();
Loading
Loading