diff --git a/.editorconfig b/.editorconfig
index 3920760..193bac6 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -4,4 +4,4 @@ insert_final_newline = true
end_of_line = lf
indent_style = space
indent_size = 2
-max_line_length = 80
+max_line_length = 120
diff --git a/src/d.ts b/src/d.ts
new file mode 100644
index 0000000..c39c2cf
--- /dev/null
+++ b/src/d.ts
@@ -0,0 +1,5 @@
+declare namespace JSX {
+ interface IntrinsicElements {
+ [elemName: string]: any;
+ }
+}
diff --git a/src/main.tsx b/src/main.tsx
index 7830e88..2ef906f 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,4 +1,138 @@
-const root = document.getElementById('root');
+import { createElement } from "./utils/jsx";
+
+interface Ticket {
+ id: number;
+ title: string;
+ description: string;
+ status: "open" | "closed";
+ toggle(): void;
+}
+
+function Header() {
+ return (
+ Tickets
+
Hello, world!
'; + const tickets: Ticket[] = []; + + const update = () => { + render({ root, tickets, addTicket }); + }; + + const addTicket = ({ title, description }: { title: string; description: string }) => { + const id = Math.max(...tickets.map((ticket) => ticket.id), 0) + 1; + const ticket: Ticket = { + id, + title, + description, + status: "open", + toggle() { + this.status = this.status === "open" ? "closed" : "open"; + update(); + }, + }; + + tickets.push(ticket); + + update(); + }; + + update(); } diff --git a/src/utils/jsx.ts b/src/utils/jsx.ts new file mode 100644 index 0000000..f492e25 --- /dev/null +++ b/src/utils/jsx.ts @@ -0,0 +1,27 @@ +export function createElement(type: string | Function, props: any, ...children: any[]) { + if (typeof type === "function") { + return type({ ...props, children }); + } + + const element = document.createElement(type); + Object.assign(element, props); + + if (props) { + Object.entries(props).forEach(([key, value]) => { + if (key.startsWith("on") && typeof value === "function") { + const event = key.slice(2).toLocaleLowerCase(); + element.addEventListener(event, value as EventListener); + } + }); + } + + children.forEach((child) => { + if (Array.isArray(child)) { + child.forEach((childItem) => element.append(childItem)); + return; + } + element.append(child); + }); + + return element; +} diff --git a/styles.css b/styles.css index 303c8b6..478c865 100644 --- a/styles.css +++ b/styles.css @@ -15,3 +15,53 @@ html { body { font-size: 1.6rem; } + +#ticket-list { + margin: 0; + padding: 0; + list-style: none; + + li { + margin-block: 1rem; + padding: 1rem; + border: 1px solid #ccc; + + .title { + font-size: 2rem; + font-weight: 700; + } + + .description { + margin-block: 0.5rem; + } + } +} + +form { + margin-block: 2rem; + max-width: 40rem; + + div { + margin-block: 1rem; + } + + label { + display: block; + margin-bottom: 0.4rem; + } + + input, + textarea { + width: 100%; + padding: 0.8rem 1rem; + } + + textarea { + height: 10rem; + } + + button { + width: 100%; + padding: 0.8rem 1rem; + } +} diff --git a/tsconfig.json b/tsconfig.json index 0760e96..ee047e5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,9 +11,9 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "ES2017" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - "jsx": "preserve", /* Specify what JSX code is generated. */ + "jsx": "preserve" /* Specify what JSX code is generated. */, // "libReplacement": true, /* Enable lib replacement. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ @@ -26,7 +26,7 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "commonjs" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ @@ -80,12 +80,12 @@ // "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */ // "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ + "strict": true /* Enable all strict type-checking options. */, // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ @@ -108,6 +108,6 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ } } diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..a60779c --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vite"; + +export default defineConfig({ + esbuild: { + jsxFactory: "createElement", + }, +});