├── src ├── File.ts ├── UI.ts ├── ColorViewerComponent.ts ├── ColorsComponent.ts ├── Startup.ts ├── RootComponent.ts └── SegmentedButtonComponent.ts ├── .gitignore ├── .editorconfig ├── package.json ├── tsconfig.json ├── Project.code-workspace └── readme.md /src/File.ts: -------------------------------------------------------------------------------- 1 | 2 | namespace App 3 | { 4 | const x = 3; 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | +* 3 | node_modules 4 | package-lock.json 5 | build/app.* -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = tab 3 | indent_size = 2 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raw-project", 3 | "author": "Paul Gordon", 4 | "description": "A sample project to demonstrate the simplicity of RawJS apps", 5 | "version": "1.0.4", 6 | "license": "MIT", 7 | "files": [ 8 | "LICENSE.txt", 9 | "app.js", 10 | "app.d.ts", 11 | "app.d.ts.map" 12 | ], 13 | "scripts": { 14 | "test": "exit 0", 15 | "serve": "cd build && npx vite --host --cors --port 4321" 16 | }, 17 | "dependencies": { 18 | "@squaresapp/rawjs": "^1.3.7", 19 | "rawter": "^1.0.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outFile": "build/app.js", 4 | "composite": true, 5 | "module": "system", 6 | "moduleResolution": "node", 7 | "target": "esnext", 8 | "inlineSourceMap": true, 9 | "inlineSources": true, 10 | "strict": true, 11 | "baseUrl": "./", 12 | "rootDir": ".", 13 | "declaration": true, 14 | "declarationMap": true, 15 | "stripInternal": true, 16 | "incremental": true, 17 | "preserveConstEnums": true, 18 | "lib": [ 19 | "dom", 20 | "esnext", 21 | "esnext.array", 22 | "esnext.asynciterable" 23 | ] 24 | }, 25 | "include": [ 26 | "node_modules/@squaresapp/rawjs/*.ts", 27 | "node_modules/rawter/*.ts", 28 | "src/**/*.ts" 29 | ] 30 | } -------------------------------------------------------------------------------- /src/UI.ts: -------------------------------------------------------------------------------- 1 | 2 | namespace App 3 | { 4 | /** 5 | * Most projects will have a namespace of utility functions 6 | * that reused across the entire project. One such function is 7 | * an "escape" function, in order to provide a quick way to make 8 | * a modal escapable. 9 | */ 10 | export namespace UI 11 | { 12 | /** */ 13 | export function escape(): Raw.Param 14 | { 15 | return [ 16 | // RawJS supports remote attachment targets. This means 17 | // that the element that receives the event is different from 18 | // the element where the event is attached. We want the 19 | // attachment of the keydown event to be contingent upon 20 | // the existence of the element receiving the UI.escape() 21 | // function, but we want the window to be the receiver 22 | // of the event, so that the keydown event can be captured 23 | // anywhere. 24 | e => raw.on(window, "keydown", () => 25 | { 26 | e.remove(); 27 | }) 28 | ] 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/ColorViewerComponent.ts: -------------------------------------------------------------------------------- 1 | 2 | namespace App 3 | { 4 | /** */ 5 | export class ColorViewerComponent 6 | { 7 | readonly head; 8 | 9 | /** */ 10 | constructor(hue: number, sat: number, light: number) 11 | { 12 | const cssColor = `hsl(${hue}, ${sat}%, ${light}%)`; 13 | 14 | this.head = raw.div( 15 | UI.escape(), 16 | "color-viewer-component", 17 | { 18 | position: "fixed", 19 | top: 0, 20 | left: 0, 21 | right: 0, 22 | bottom: 0, 23 | margin: "auto", 24 | padding: "10px", 25 | width: "fit-content", 26 | height: "fit-content", 27 | boxShadow: "0 0 100px 10000px rgba(0, 0, 0, 0.5)", 28 | backgroundColor: "white", 29 | color: "black", 30 | zIndex: 1 31 | }, 32 | raw.div( 33 | { 34 | width: "500px", 35 | height: "500px", 36 | backgroundColor: cssColor 37 | }, 38 | ), 39 | raw.div( 40 | { 41 | textAlign: "center", 42 | padding: "1em", 43 | }, 44 | raw.text("CSS color: " + cssColor) 45 | ) 46 | ); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Project.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": ".", 5 | }, 6 | ], 7 | "settings": { 8 | "files.exclude": { 9 | "**/.git": true, 10 | "**/.DS_Store": true, 11 | "**/node_modules": true, 12 | "**/package-lock.json": true, 13 | "build/app.*": true 14 | }, 15 | "search.exclude": { 16 | "**/.git": true, 17 | "**/.DS_Store": true, 18 | "**/build": true, 19 | "**/node_modules": true, 20 | "**/package-lock.json": true, 21 | "build/app.*": true 22 | }, 23 | "task.allowAutomaticTasks": "on", 24 | }, 25 | "launch": { 26 | "configurations": [ 27 | { 28 | "name": "Launch", 29 | "type": "chrome", 30 | "request": "launch", 31 | "port": 9222, 32 | "url": "http://localhost:4321/", 33 | "webRoot": "${workspaceFolder}/build", 34 | } 35 | ] 36 | }, 37 | "tasks": { 38 | "version": "2.0.0", 39 | "tasks": [ 40 | { 41 | "label": "Compile Library", 42 | "type": "shell", 43 | "command": "tsc", 44 | "args": [ 45 | "--build", 46 | "--watch" 47 | ], 48 | "options": { 49 | "cwd": "${workspaceRoot}" 50 | }, 51 | "problemMatcher": [ 52 | "$tsc" 53 | ], 54 | "runOptions": { 55 | "runOn": "folderOpen" 56 | }, 57 | "group": { 58 | "kind": "build", 59 | "isDefault": true 60 | }, 61 | "isBackground": true 62 | } 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ColorsComponent.ts: -------------------------------------------------------------------------------- 1 | 2 | namespace App 3 | { 4 | /** */ 5 | export class ColorsComponent 6 | { 7 | readonly head; 8 | readonly colorsContainer; 9 | 10 | /** */ 11 | constructor(routeName: string, roughHue: number) 12 | { 13 | this.head = raw.div( 14 | "colors-component", 15 | this.colorsContainer = raw.div( 16 | "colors-container", 17 | { 18 | textAlign: "center", 19 | maxWidth: "900px", 20 | margin: "auto", 21 | } 22 | ), 23 | ); 24 | 25 | for (let i = -1; ++i < 100;) 26 | { 27 | let hue = roughHue + Math.round(Math.random() * 20 - 10); 28 | if (hue >360) 29 | hue -= 360; 30 | 31 | if (hue < 0) 32 | hue += 360; 33 | 34 | const sat = Math.round(Math.random() * 100); 35 | const light = Math.max(20, Math.round(Math.random() * 100)); 36 | 37 | this.colorsContainer.append(raw.div( 38 | { 39 | float: "left", 40 | width: "300px", 41 | height: "300px", 42 | border: "1px solid black", 43 | borderWidth: "1px 1px 0 0 ", 44 | backgroundImage: `linear-gradient( 45 | -45deg, 46 | hsl(${hue}, ${sat}%, ${light}%), 47 | hsl(${hue}, ${sat}%, ${light - 20}%) 48 | )`, 49 | }, 50 | Rawter.on(`/${routeName}/${hue}-${sat}-${light}`, () => 51 | { 52 | this.head.append( 53 | new ColorViewerComponent(hue, sat, light).head 54 | ); 55 | }) 56 | )); 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Minimal RawJS Project 3 | 4 | This repository demonstrates a minimal RawJS project setup. You can clone this repository in order to get a good starting point for a front-end project using RawJS. 5 | 6 | The project demonstrates basic app development and project organization using RawJS, hierarchial routing with support for back and forward buttons (via [Rawter](https://github.com/paul-go/rawter)), `vite` as the build server, and a code-workspace configured for debugging. Be sure to open the code-workspace file into VS code in order to get step debugging to work. 7 | 8 | ## Online Demo 9 | 10 | The demo project is basic CSS color viewer. You can see an online demo of the project at 11 | [https://rawjssample.pages.dev](https://rawjssample.pages.dev) 12 | 13 | ## Trade-offs 14 | 15 | These are the limitations you need to accept with this project structure: 16 | 17 | - You have to use TypeScript (duh) 18 | - You have to be disciplined to only use dependencies that are published on jsdelivr (`npm install` programmers need to clean up their act) 19 | 20 | These are the advantages you gain: 21 | 22 | - Efficient one-class-per file project structure 23 | - No `import` dumpster at the top of each file. Forget imports even existed. Just access whatever you want, wherever you want. TypeScript knows where to find it. 24 | - TypeScript namespaces provide arguably better encapsulation than ES modules. 25 | - No bundler, no build system. Near-zero complexity. 26 | - TypeScript builds your app in milliseconds, even if your app gets huge. 27 | -------------------------------------------------------------------------------- /src/Startup.ts: -------------------------------------------------------------------------------- 1 | 2 | namespace App 3 | { 4 | /** */ 5 | function startup() 6 | { 7 | document.head.append( 8 | // The raw.style function creates a