Compare commits
21 Commits
48529ae3e2
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 49b153b0ff | |||
| b801ac9da6 | |||
| 656a673ca9 | |||
| 61af976a7a | |||
|
|
f1e8be1cd8 | ||
|
|
cee4d4fa8a | ||
|
|
188837d873 | ||
|
|
12bb5346e8 | ||
|
|
eb6975c7de | ||
|
|
c4560aba37 | ||
|
|
b08e2767f6 | ||
|
|
ed6d885557 | ||
|
|
6e2b4dcdbd | ||
|
|
71b08bd184 | ||
|
|
1060797170 | ||
|
|
cfdf67998d | ||
|
|
9432c65fa4 | ||
|
|
65f79d1631 | ||
|
|
41ec6b7dd3 | ||
|
|
8b2f9e2b77 | ||
|
|
07725994b9 |
1
.env
Normal file
1
.env
Normal file
@@ -0,0 +1 @@
|
||||
VSCE_PAT=9wucEHBNFD5RTS403Oyoflyi984cAwAfa42FHN7tmqmzfbXsSKP9JQQJ99BKACAAAAAAAAAAAAAGAZDO25Ro
|
||||
171
README.md
171
README.md
@@ -1,38 +1,163 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="VSCode/docs/Quill.png" alt="drawing" width="100"/>
|
||||
<h1 align="center">Quill</h1>
|
||||
</p>
|
||||
<br>
|
||||
|
||||
Usage: Install the VSCode extension "Quill".
|
||||
Quill is a SwiftUI-style JavaScript framework. It makes use of components called Shadows, which are HTML Custom Elements.
|
||||
|
||||
## Rendering Elements:
|
||||
### Getting Started:
|
||||
Take index.js and put it in your app. Typically as quill.js. Then import it in the head of the HTML.
|
||||
|
||||
### Basic Overview:
|
||||
|
||||
Quill uses components called Shadows. Each Shadow is a Custom HTML Element (https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements)
|
||||
```
|
||||
document.body.append(
|
||||
p("Hi")
|
||||
.padding("top", 12),
|
||||
class Home extends Shadow {
|
||||
render() {
|
||||
}
|
||||
}
|
||||
|
||||
Link({href: "google.com", name: "hey"})
|
||||
.background("green")
|
||||
|
||||
// NavigationBar()
|
||||
// .onClick()
|
||||
)
|
||||
register(Home)
|
||||
```
|
||||
|
||||
## Needs Support:
|
||||
Ternaries within render()
|
||||
Other statements within render()
|
||||
Once created, it can be imported like
|
||||
```
|
||||
import "Home.js"
|
||||
```
|
||||
(Not how we are NOT importing the actual class object. If that happens, it will fail.)
|
||||
|
||||
## Boilerplate:
|
||||
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
||||
- ```*element```: Type in a JS file and select the suggestion to create JS Custom Element boilerplate.
|
||||
Here is an example of Hello World:
|
||||
|
||||
## Functions:
|
||||
Clone this repository into the top level of the project you are working on, so the HTML can find the "quill.js" functions.
|
||||
Use backticks with both to get HTML and CSS syntax highlighting.
|
||||
- ```css() or addStyle()```: Adds a style to a Quill style tag in the head.
|
||||
- ```html()```: Creates a parsed HTML element (which is not yet in the DOM)
|
||||
```
|
||||
class Home extends Shadow {
|
||||
render() {
|
||||
p("Hello World")
|
||||
.x(50, vw)
|
||||
.y(50, vh)
|
||||
}
|
||||
}
|
||||
|
||||
register(Home)
|
||||
```
|
||||
|
||||
This will render a paragraph tag in the middle of the screen.
|
||||
|
||||
Here's what it will look like in HTML:
|
||||
|
||||
```
|
||||
<body>
|
||||
<home->
|
||||
<p style="position: absolute; top: 50vh; left: 50vw;">Hello World</p>
|
||||
</home->
|
||||
</body>
|
||||
```
|
||||
|
||||
Note: .x() and .y() are quill-specific functions that were created simply for nice syntax. However, this would also be valid:
|
||||
```
|
||||
p("Hello World")
|
||||
.top(50, vh)
|
||||
.left(50, vw)
|
||||
```
|
||||
|
||||
There are quill functions for every HTML style attribute. If they have units, they will follow the pattern directly above, where the first parameter is the amount and the second parameter is the unit.
|
||||
|
||||
### Real Basic Example:
|
||||
|
||||
First, you need your index.html. Here is one:
|
||||
|
||||
```
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="public">
|
||||
<head>
|
||||
<title>Parchment</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="/_/icons/quill.svg">
|
||||
<link rel="stylesheet" href="/_/code/shared.css">
|
||||
<script src="/_/code/quill.js"></script>
|
||||
<script type="module" src="75820185/index.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
When starting, it is typical to make a "Home" shadow and import it in index.js. Here is an example:
|
||||
|
||||
index.js:
|
||||
```
|
||||
import "./Home.js"
|
||||
Home()
|
||||
```
|
||||
|
||||
Home.js:
|
||||
```
|
||||
import "../components/NavBar.js"
|
||||
import "./HomeContent.js"
|
||||
import "./Why.js"
|
||||
import "./Events.js"
|
||||
import "./Join.js"
|
||||
import "./SignIn.js"
|
||||
import "./Success.js"
|
||||
|
||||
class Home extends Shadow {
|
||||
render() {
|
||||
|
||||
ZStack(() => {
|
||||
|
||||
NavBar()
|
||||
|
||||
img("/_/icons/logo.svg", "2.5em")
|
||||
.onClick((done) => {
|
||||
if(!done) return
|
||||
window.navigateTo("/")
|
||||
})
|
||||
.position("absolute")
|
||||
.left(50, vw).top(4, em)
|
||||
.center()
|
||||
.transform(`translate(${window.isMobile() ? "-50%" : "-2em"}, -50%)`)
|
||||
|
||||
switch(window.location.pathname) {
|
||||
case "/":
|
||||
HomeContent()
|
||||
break;
|
||||
case "/why":
|
||||
Why()
|
||||
break;
|
||||
case "/events":
|
||||
Events()
|
||||
break;
|
||||
case "/join":
|
||||
Join()
|
||||
break;
|
||||
case "/success":
|
||||
Success()
|
||||
break;
|
||||
}
|
||||
|
||||
})
|
||||
.onNavigate(() => {
|
||||
this.rerender()
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
register(Home)
|
||||
|
||||
```
|
||||
|
||||
|
||||
Success.js:
|
||||
|
||||
```
|
||||
class Success extends Shadow {
|
||||
render() {
|
||||
p("Thanks for your purchase! You will receive a confirmation email shortly. <br><br> <b>Keep that email; it will be checked at the door.</b>")
|
||||
.x(50, vw).y(50, vh)
|
||||
.center()
|
||||
}
|
||||
}
|
||||
|
||||
register(Success)
|
||||
```
|
||||
2
Sample/.gitignore
vendored
Normal file
2
Sample/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
package-lock.json
|
||||
0
Sample/db/db.json
Normal file
0
Sample/db/db.json
Normal file
18
Sample/package.json
Normal file
18
Sample/package.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Quill Starter",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node server/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"chalk": "^4.1.2",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.18.2",
|
||||
"moment": "^2.30.1"
|
||||
}
|
||||
}
|
||||
12
Sample/server/db/db.js
Normal file
12
Sample/server/db/db.js
Normal file
@@ -0,0 +1,12 @@
|
||||
const fs = require('fs/promises');
|
||||
|
||||
export default class Database {
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
async saveData() {
|
||||
|
||||
}
|
||||
}
|
||||
100
Sample/server/index.js
Normal file
100
Sample/server/index.js
Normal file
@@ -0,0 +1,100 @@
|
||||
const express = require('express');
|
||||
const cors = require('cors');
|
||||
const http = require('http');
|
||||
const chalk = require('chalk');
|
||||
const moment = require('moment');
|
||||
const path = require('path');
|
||||
|
||||
class Server {
|
||||
db;
|
||||
UIPath = path.join(__dirname, '../ui')
|
||||
|
||||
registerRoutes(router) {
|
||||
router.post('/join', this.join)
|
||||
router.post('/contact', this.contact)
|
||||
router.get('/*', this.get)
|
||||
return router
|
||||
}
|
||||
|
||||
join = (req, res) => {
|
||||
|
||||
}
|
||||
|
||||
contact = (req, res) => {
|
||||
|
||||
}
|
||||
|
||||
get = async (req, res) => {
|
||||
console.log(this.UIPath)
|
||||
let url = req.url
|
||||
let filePath;
|
||||
if(url.startsWith("/_")) {
|
||||
filePath = path.join(this.UIPath, url);
|
||||
} else if(url.includes("75820185")) {
|
||||
filePath = path.join(this.UIPath, url.split("75820185")[1]);
|
||||
} else {
|
||||
filePath = path.join(this.UIPath, "index.html");
|
||||
}
|
||||
|
||||
res.sendFile(filePath);
|
||||
}
|
||||
|
||||
logRequest(req, res, next) {
|
||||
const formattedDate = moment().format('M.D');
|
||||
const formattedTime = moment().format('h:mma');
|
||||
if(req.url.includes("/api/")) {
|
||||
console.log(chalk.blue(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
||||
} else {
|
||||
if(req.url === "/")
|
||||
console.log(chalk.gray(` ${req.method} ${req.url} | ${formattedDate} ${formattedTime}`));
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
logResponse(req, res, next) {
|
||||
const originalSend = res.send;
|
||||
res.send = function (body) {
|
||||
if(res.statusCode >= 400) {
|
||||
console.log(chalk.blue( `<-${chalk.red(res.statusCode)}- ${req.method} ${req.url} | ${chalk.red(body)}`));
|
||||
} else {
|
||||
console.log(chalk.blue(`<-${res.statusCode}- ${req.method} ${req.url}`));
|
||||
}
|
||||
originalSend.call(this, body);
|
||||
};
|
||||
next();
|
||||
}
|
||||
|
||||
constructor() {
|
||||
const app = express();
|
||||
app.use(cors({ origin: '*' }));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
app.use(this.logRequest);
|
||||
app.use(this.logResponse);
|
||||
|
||||
let router = express.Router();
|
||||
this.registerRoutes(router)
|
||||
app.use('/', router);
|
||||
|
||||
const server = http.createServer(app);
|
||||
const PORT = 3004;
|
||||
server.listen(PORT, () => {
|
||||
console.log("\n")
|
||||
console.log(chalk.yellow("*************** Comal YR ***************"))
|
||||
console.log(chalk.yellowBright(`Server is running on port ${PORT}: http://localhost`));
|
||||
console.log(chalk.yellow("***************************************"))
|
||||
console.log("\n")
|
||||
});
|
||||
|
||||
process.on('SIGINT', async () => {
|
||||
console.log(chalk.red('Closing server...'));
|
||||
console.log(chalk.green('Database connection closed.'));
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
Object.preventExtensions(this);
|
||||
}
|
||||
}
|
||||
|
||||
const server = new Server()
|
||||
1047
Sample/ui/_/code/quill.js
Normal file
1047
Sample/ui/_/code/quill.js
Normal file
File diff suppressed because one or more lines are too long
0
Sample/ui/_/code/shared.css
Normal file
0
Sample/ui/_/code/shared.css
Normal file
7
Sample/ui/components/Home.js
Normal file
7
Sample/ui/components/Home.js
Normal file
@@ -0,0 +1,7 @@
|
||||
class Home extends Shadow {
|
||||
render() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
register(Home)
|
||||
13
Sample/ui/index.html
Normal file
13
Sample/ui/index.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Quill Starter</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="/_/icons/logo.svg">
|
||||
<link rel="stylesheet" href="/_/code/shared.css">
|
||||
<script src="/_/code/quill.js"></script>
|
||||
<script type="module" src="75820185/index.js"></script>
|
||||
</head>
|
||||
<body style="margin: 0px">
|
||||
</body>
|
||||
</html>
|
||||
2
Sample/ui/index.js
Normal file
2
Sample/ui/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
import "./components/Home.js"
|
||||
Home()
|
||||
38
Test/Element/Group.test.js
Normal file
38
Test/Element/Group.test.js
Normal file
@@ -0,0 +1,38 @@
|
||||
window.testSuites.push( class testGroup {
|
||||
|
||||
BasicDivs() {
|
||||
let divs = Group(() => {
|
||||
div()
|
||||
div()
|
||||
div()
|
||||
})
|
||||
|
||||
if(!(divs instanceof HTMLElement)) {
|
||||
return `Did not receive an element!`
|
||||
}
|
||||
if(!(divs.children.length === 3)) {
|
||||
return `Incorrect amount of children!`
|
||||
}
|
||||
}
|
||||
|
||||
VStack() {
|
||||
let divs = Group(() => {
|
||||
VStack(() => {
|
||||
div()
|
||||
div()
|
||||
div()
|
||||
})
|
||||
})
|
||||
|
||||
if(!(divs instanceof HTMLElement)) {
|
||||
return `Did not receive an element!`
|
||||
}
|
||||
if(!(divs.style.display === "flex")) {
|
||||
return `Did not receive a flex container!`
|
||||
}
|
||||
if(!(divs.children.length === 3)) {
|
||||
return `Incorrect amount of children!`
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
79
Test/Element/stacks.test.js
Normal file
79
Test/Element/stacks.test.js
Normal file
@@ -0,0 +1,79 @@
|
||||
window.testSuites.push( class testStacks {
|
||||
|
||||
NestedChildren() {
|
||||
register(class File extends Shadow {
|
||||
$name
|
||||
|
||||
render = () => {
|
||||
p(this.name)
|
||||
|
||||
VStack(() => {
|
||||
p("Learn to code inside of a startup")
|
||||
|
||||
p("➵")
|
||||
.position("absolute")
|
||||
.fontSize(30)
|
||||
.left("50%")
|
||||
.top("50%")
|
||||
.transformOrigin("center")
|
||||
.transform("translate(-50%, -50%) rotate(90deg)")
|
||||
.transition("all 1s")
|
||||
.userSelect("none")
|
||||
.onAppear((self) => {
|
||||
setTimeout(() => {
|
||||
self.style.top = "88%"
|
||||
}, 100)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
const file = File("asdf")
|
||||
if(file.querySelector(".VStack")?.children.length !== 2) {
|
||||
return "Incorrect amount of children inside vstack!"
|
||||
}
|
||||
}
|
||||
|
||||
NestedStacks() {
|
||||
register(class File extends Shadow {
|
||||
$name
|
||||
|
||||
render = () => {
|
||||
VStack(() => {
|
||||
HStack(() => {
|
||||
p("hi")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
}, randomName("file"))
|
||||
|
||||
const file = File("asdf")
|
||||
console.log(file)
|
||||
let fileStyle = Registry.styles.children[Registry.styles.children.length-1].sheet.cssRules
|
||||
if(fileStyle[0].cssText.includes("row")) {
|
||||
return "Should not be horizontal"
|
||||
}
|
||||
|
||||
console.log(file.innerHTML)
|
||||
if(!file.children[0].matches("div.HStack")) {
|
||||
return "The child is not an HStack"
|
||||
}
|
||||
|
||||
file.rerender()
|
||||
|
||||
if(!file.children[0].matches("div.HStack")) {
|
||||
return "The child is not an HStack"
|
||||
}
|
||||
|
||||
let fileStyle2 = Registry.styles.children[Registry.styles.children.length-1].sheet.cssRules
|
||||
if(fileStyle2[0].cssText.includes("row")) {
|
||||
return "Should not be horizontal"
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
@@ -1,7 +0,0 @@
|
||||
class Home extends Page {
|
||||
|
||||
render = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export default Home
|
||||
@@ -1,75 +1,45 @@
|
||||
window.testSuites.push( class testRender {
|
||||
window.testSuites.push( class testObservedObject {
|
||||
|
||||
SimpleParagraphWithState() {
|
||||
class File extends Shadow {
|
||||
$form
|
||||
|
||||
render = () => {
|
||||
p(this.form.data)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
FromJSONFailsWithoutAllFields() {
|
||||
class Form extends ObservedObject {
|
||||
id
|
||||
path
|
||||
$canvasPosition
|
||||
}
|
||||
|
||||
window.register(File, randomName("file"))
|
||||
let form = {data: "asdf"}
|
||||
const el = window.File(form)
|
||||
try {
|
||||
let obj = Form.create({id: "123"})
|
||||
return "Not implemented"
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if(!(el.firstChild?.matches("p"))) {
|
||||
return `Child paragraph not rendered`
|
||||
FromJSONInitsAllFields() {
|
||||
class Form extends ObservedObject {
|
||||
id
|
||||
path
|
||||
$canvasPosition
|
||||
}
|
||||
if(!(el.firstChild.innerText === "asdf")) {
|
||||
return "Child para does not have inner text"
|
||||
|
||||
let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||
if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
||||
return "Not all fields initialized!"
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphConstructorChangeState() {
|
||||
register(class File extends Shadow {
|
||||
$name
|
||||
DefaultValueWorks() {
|
||||
class WindowState extends ObservedObject {
|
||||
$sidebarOut = false
|
||||
}
|
||||
|
||||
render = () => {
|
||||
p(this.name)
|
||||
}
|
||||
let obj = WindowState.create()
|
||||
console.log(obj)
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
|
||||
let name = "asdf"
|
||||
const file = File(name)
|
||||
file.name = "hey123"
|
||||
|
||||
if(file.firstChild.innerText !== "hey123") {
|
||||
return "Paragraph did not react to change!"
|
||||
if(obj.sidebarOut !== false) {
|
||||
return "Default field not set"
|
||||
}
|
||||
}
|
||||
|
||||
LiteralDoesNotCreateFalseReactivity() {
|
||||
register(class File extends Shadow {
|
||||
$name = "asd"
|
||||
|
||||
render = () => {
|
||||
p(this.name)
|
||||
p("asd")
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
|
||||
const file = File()
|
||||
file.name = "hey123"
|
||||
|
||||
if(file.children[1].innerText === "hey123") {
|
||||
return "Paragraph innertext falsely changed"
|
||||
}
|
||||
}
|
||||
// throw some sort of warning if a global OO is accessed without "this"
|
||||
|
||||
DefaultObservedObject() {
|
||||
window.Form = class Form extends ObservedObject {
|
||||
@@ -156,4 +126,37 @@ window.testSuites.push( class testRender {
|
||||
}
|
||||
}
|
||||
|
||||
NotExtensible() {
|
||||
return "not done"
|
||||
}
|
||||
|
||||
|
||||
// MustInitAllFields() {
|
||||
// class Form extends ObservedObject {
|
||||
// id
|
||||
// path
|
||||
// $canvasPosition
|
||||
// }
|
||||
|
||||
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||
// if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
||||
// return "Not all fields initialized!"
|
||||
// }
|
||||
// }
|
||||
|
||||
// ChangingObjChangesInstance() {
|
||||
// class Form extends ObservedObject {
|
||||
// id
|
||||
// path
|
||||
// $canvasPosition
|
||||
// }
|
||||
|
||||
// let json = {id: "123", path: "/", canvasPosition: "25|25"}
|
||||
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||
// json.id = "456"
|
||||
// if(!(obj["id"] === "456")) {
|
||||
// return "Change to original object was not reflected!"
|
||||
// }
|
||||
// }
|
||||
|
||||
})
|
||||
@@ -1,13 +1,19 @@
|
||||
|
||||
/*
|
||||
|
||||
"(" is preceding character: el, el.attr, if, switch
|
||||
el: the el is window.rendering
|
||||
el.attr:
|
||||
find the function and attr in the string
|
||||
if there are multiple instances of being used with this el, add to a list (and if no list then make it and we are first)
|
||||
if: the el is window.rendering. rerender el
|
||||
switch: the el is window.rendering. rerender el
|
||||
*/
|
||||
|
||||
window.testSuites.push(
|
||||
|
||||
|
||||
class ParseRender {
|
||||
// CopyTo() {
|
||||
// let str = "render=()=>{VStack(()=>{"
|
||||
// let ret = str.copyTo("{")
|
||||
|
||||
// if(ret !== "render=()=>") return "Copy 1 failed!"
|
||||
// }
|
||||
|
||||
ParseRender() {
|
||||
class Sidebar extends Shadow {
|
||||
126
Test/Skeleton/state.test.js
Normal file
126
Test/Skeleton/state.test.js
Normal file
@@ -0,0 +1,126 @@
|
||||
window.testSuites.push( class testState {
|
||||
|
||||
SimpleParagraphWithState() {
|
||||
class File extends Shadow {
|
||||
$form
|
||||
|
||||
render = () => {
|
||||
p(this.form.data)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}
|
||||
|
||||
window.register(File, randomName("file"))
|
||||
let form = {data: "asdf"}
|
||||
const el = window.File(form)
|
||||
|
||||
if(!(el.firstChild?.matches("p"))) {
|
||||
return `Child paragraph not rendered`
|
||||
}
|
||||
if(!(el.firstChild.innerText === "asdf")) {
|
||||
return "Child para does not have inner text"
|
||||
}
|
||||
}
|
||||
|
||||
ParagraphConstructorChangeState() {
|
||||
register(class File extends Shadow {
|
||||
$name
|
||||
|
||||
render = () => {
|
||||
p(this.name)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
|
||||
let name = "asdf"
|
||||
const file = File(name)
|
||||
file.name = "hey123"
|
||||
|
||||
if(file.firstChild.innerText !== "hey123") {
|
||||
return "Paragraph did not react to change!"
|
||||
}
|
||||
}
|
||||
|
||||
LiteralDoesNotCreateFalseReactivity() {
|
||||
register(class File extends Shadow {
|
||||
$name = "asd"
|
||||
|
||||
render = () => {
|
||||
p(this.name)
|
||||
p("asd")
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
|
||||
const file = File()
|
||||
file.name = "hey123"
|
||||
|
||||
if(file.children[1].innerText === "hey123") {
|
||||
return "Paragraph innertext falsely changed"
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
State itself should check if the reactivity is based on an element or a standalone expression
|
||||
If standalone, handle it
|
||||
If element, push the info for initReactivity to handle it
|
||||
*/
|
||||
|
||||
TernaryInState() {
|
||||
register(class File extends Shadow {
|
||||
$name
|
||||
|
||||
render = () => {
|
||||
p(this.name)
|
||||
.fontSize(this.name === "asdf" ? 16 : 32)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
let name = "asdf"
|
||||
const file = File(name)
|
||||
|
||||
if(file.style.fontSize !== "16px") {
|
||||
return "fail"
|
||||
}
|
||||
}
|
||||
|
||||
StateWorksWithCustomStyleFunctions() {
|
||||
// reactive setting needs to use the actual style functions
|
||||
|
||||
register(class File extends Shadow {
|
||||
$paraWidth = 16
|
||||
|
||||
render = () => {
|
||||
p("guppy")
|
||||
.width(this.paraWidth)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
|
||||
const file = File()
|
||||
file.paraWidth = 18
|
||||
|
||||
if(file.firstChild.style.width !== "18px") {
|
||||
return "Width did not reactively change!"
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
@@ -1,77 +0,0 @@
|
||||
window.testSuites.push( class testObservedObject {
|
||||
|
||||
FromJSONFailsWithoutAllFields() {
|
||||
class Form extends ObservedObject {
|
||||
id
|
||||
path
|
||||
$canvasPosition
|
||||
}
|
||||
|
||||
try {
|
||||
let obj = Form.create({id: "123"})
|
||||
return "Not implemented"
|
||||
} catch {}
|
||||
}
|
||||
|
||||
FromJSONInitsAllFields() {
|
||||
class Form extends ObservedObject {
|
||||
id
|
||||
path
|
||||
$canvasPosition
|
||||
}
|
||||
|
||||
let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||
if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
||||
return "Not all fields initialized!"
|
||||
}
|
||||
}
|
||||
|
||||
DefaultValueWorks() {
|
||||
class WindowState extends ObservedObject {
|
||||
$sidebarOut = false
|
||||
}
|
||||
|
||||
let obj = WindowState.create()
|
||||
console.log(obj)
|
||||
|
||||
if(obj.sidebarOut !== false) {
|
||||
return "Default field not set"
|
||||
}
|
||||
}
|
||||
|
||||
// throw some sort of warning if a global OO is accessed without "this"
|
||||
|
||||
NotExtensible() {
|
||||
return "not done"
|
||||
}
|
||||
|
||||
|
||||
// MustInitAllFields() {
|
||||
// class Form extends ObservedObject {
|
||||
// id
|
||||
// path
|
||||
// $canvasPosition
|
||||
// }
|
||||
|
||||
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||
// if(!(obj && obj["id"] === "123" && obj["path"] === "/" && obj["canvasPosition"] === "25|25")) {
|
||||
// return "Not all fields initialized!"
|
||||
// }
|
||||
// }
|
||||
|
||||
// ChangingObjChangesInstance() {
|
||||
// class Form extends ObservedObject {
|
||||
// id
|
||||
// path
|
||||
// $canvasPosition
|
||||
// }
|
||||
|
||||
// let json = {id: "123", path: "/", canvasPosition: "25|25"}
|
||||
// let obj = Form.create({id: "123", path: "/", canvasPosition: "25|25"})
|
||||
// json.id = "456"
|
||||
// if(!(obj["id"] === "456")) {
|
||||
// return "Change to original object was not reflected!"
|
||||
// }
|
||||
// }
|
||||
|
||||
})
|
||||
73
Test/state/state.test.js
Normal file
73
Test/state/state.test.js
Normal file
@@ -0,0 +1,73 @@
|
||||
window.testSuites.push( class testState {
|
||||
SimpleState() {
|
||||
class Home extends Shadow {
|
||||
state = {
|
||||
pathname: "/"
|
||||
}
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
p("hi")
|
||||
.top(() => {return (this.state.pathname === "/" ? [11, vw] : [7, vw])})
|
||||
})
|
||||
.onAppear(() => {
|
||||
this.state.pathname = "/asd"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
register(Home, randomName("home"))
|
||||
window.Home()
|
||||
if(!($("p").style.top === "7vw")) return "state was not respeccted"
|
||||
}
|
||||
|
||||
StateArrayPush() {
|
||||
class Home extends Shadow {
|
||||
state = {
|
||||
logs: []
|
||||
}
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
p("hi")
|
||||
.fontSize(() => {return this.state.logs.length > 0 ? [2, em] : [1, em]})
|
||||
})
|
||||
.onAppear(() => {
|
||||
this.state.logs.push("one")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
register(Home, randomName("home"))
|
||||
window.Home()
|
||||
|
||||
if(!($("p").style.fontSize === "2em")) return "state did not update!"
|
||||
}
|
||||
|
||||
SimpleStack() {
|
||||
class Home extends Shadow {
|
||||
state = {
|
||||
logs: []
|
||||
}
|
||||
|
||||
render() {
|
||||
VStack(() => {
|
||||
this.state.logs.forEach((log) => {
|
||||
p(log)
|
||||
})
|
||||
})
|
||||
.onAppear(() => {
|
||||
this.state.logs.push("one")
|
||||
this.state.logs.push("two")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
register(Home, randomName("home"))
|
||||
window.Home()
|
||||
|
||||
if(!$("p")) return "no p's rendered"
|
||||
if($$("p")[0].innerText !== "one") return "state did not update!"
|
||||
if($$("p")[1].innerText !== "two") return "state did not update!"
|
||||
}
|
||||
})
|
||||
@@ -2,17 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Quill</title>
|
||||
<link rel="icon" href="">
|
||||
<link rel="icon" href="../_/Quill.png">
|
||||
<link rel="stylesheet" href="">
|
||||
<script src="../index.js"></script>
|
||||
<script src="test.js" type="module"></script>
|
||||
<script type="module">
|
||||
import Home from "./Pages/home.js";
|
||||
|
||||
window.routes = {
|
||||
"/Test": Home
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body style="background: rgb(242, 194, 147)">
|
||||
|
||||
|
||||
15
Test/test.js
15
Test/test.js
@@ -1,11 +1,7 @@
|
||||
console.log("Tests initializing.")
|
||||
window.testSuites = [];
|
||||
|
||||
await import ("./parse.test.js")
|
||||
await import ("./init.test.js")
|
||||
await import ("./render.test.js")
|
||||
await import ("./observedobject.test.js")
|
||||
await import ("./parserender.test.js")
|
||||
await import ("./state/state.test.js")
|
||||
|
||||
window.randomName = function randomName(prefix) {
|
||||
const sanitizedPrefix = prefix.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
@@ -41,6 +37,7 @@ window.test = async function() {
|
||||
let test = suiteContents[i];
|
||||
if(typeof suite[test] === 'function' && test !== "constructor") {
|
||||
testNum++;
|
||||
document.body.innerHTML = ""
|
||||
console.log(`%c${testNum}. ${test}`, "margin-top: 10px; border-top: 2px solid #e9c9a0; color: #e9c9a0; border-radius: 10px; padding: 10px;");
|
||||
|
||||
let fail;
|
||||
@@ -76,12 +73,12 @@ window.test = async function() {
|
||||
console.log("")
|
||||
let elapsed = new Date() - start;
|
||||
if(failed === 0) {
|
||||
console.log(`%cRan ${failed+success} tests in ${elapsed}ms`, 'color: #00FF00');
|
||||
console.log(`%c ${success} passed`, 'color: #00FF00');
|
||||
console.log(`%c ${failed} failed`);
|
||||
console.log(`%cRan ${failed+success} tests in ${elapsed}ms`, 'color: #9cd499ff');
|
||||
console.log(`%c ${success} passed`, 'color: #9cd499ff');
|
||||
console.log(` ${failed} failed`);
|
||||
} else {
|
||||
console.log(`%cRan ${failed+success} tests in ${elapsed}ms`, 'color: rgb(254, 62, 43)');
|
||||
console.log(`%c ${success} `, 'color: #00FF00', "passed");
|
||||
console.log(`%c ${success} `, 'color: #9cd499ff', "passed");
|
||||
console.log(`%c ${failed} failed`, 'color: rgb(254, 62, 43)');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ Forked from this repository:
|
||||
|
||||
## Boilerplate:
|
||||
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
||||
- ```*element```: Type in a JS file and select the suggestion to create JS Custom Element boilerplate.
|
||||
- ```*shadow```: Type in a JS file and select the suggestion to create JS Custom Element boilerplate.
|
||||
|
||||
## Functions:
|
||||
Clone this repository into the top level of the project you are working on, so the HTML can find the "quill.js" functions.
|
||||
|
||||
15
VSCode/deploy-howto.md
Normal file
15
VSCode/deploy-howto.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# To Deploy Extension
|
||||
|
||||
```vsce package```
|
||||
|
||||
```vsce publish```
|
||||
|
||||
# To Get PAT
|
||||
|
||||
Login to Azure
|
||||
Search "Devops"
|
||||
Login to devops (samuel@sun.museum)
|
||||
Click "User Settings" in the top right > "Personal Access Tokens"
|
||||
Create one which has "Allow all Organizations" and "Marketplace" > "Manage" selected
|
||||
Put it in the .env file here
|
||||
set it in the terminal like `export VSCE_PAT="<token>"`
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "quill",
|
||||
"displayName": "Quill",
|
||||
"description": "HTML/CSS Syntax highlighting, best used with the Quill framework",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.6",
|
||||
"publisher": "capturedsun",
|
||||
"icon": "docs/Quill.png",
|
||||
"engines": {
|
||||
|
||||
BIN
VSCode/quill-1.0.5.vsix
Normal file
BIN
VSCode/quill-1.0.5.vsix
Normal file
Binary file not shown.
@@ -2,46 +2,17 @@
|
||||
"*page": {
|
||||
"prefix": "*html",
|
||||
"body": [
|
||||
"<!DOCTYPE html>",
|
||||
"<!DOCTYPE html>",
|
||||
"<html lang=\"en\">",
|
||||
" <head>",
|
||||
" <title>Quill</title>",
|
||||
" <link rel=\"icon\" href=\"\">",
|
||||
" <link rel=\"stylesheet\" href=\"\">",
|
||||
" <script type=\"module\">",
|
||||
" window.addStyle = function addStyle(cssString) {",
|
||||
" let container = document.querySelector(\"style#quillStyles\");",
|
||||
" if(!container) {",
|
||||
" container = document.createElement('style');",
|
||||
" container.id = \"quillStyles\";",
|
||||
" document.head.appendChild(container);",
|
||||
" }",
|
||||
"",
|
||||
" let primarySelector = cssString.substring(0, cssString.indexOf(\"{\"));",
|
||||
" primarySelector = primarySelector.replace(/\\*/g, \"all\");",
|
||||
" primarySelector = primarySelector.replace(/#/g, \"id-\");",
|
||||
" primarySelector = primarySelector.replace(/,/g, \"\");",
|
||||
" let stylesheet = container.querySelector(`:scope > style[id='${primarySelector}']`)",
|
||||
" if(!stylesheet) {",
|
||||
" stylesheet = document.createElement('style');",
|
||||
" stylesheet.id = primarySelector;",
|
||||
" stylesheet.appendChild(document.createTextNode(cssString));",
|
||||
" container.appendChild(stylesheet);",
|
||||
" } else {",
|
||||
" stylesheet.innerText = cssString",
|
||||
" }",
|
||||
" }",
|
||||
"",
|
||||
" window.html = function html(elementString) {",
|
||||
" let parser = new DOMParser();",
|
||||
" let doc = parser.parseFromString(elementString, 'text/html');",
|
||||
" return doc.body.firstChild;",
|
||||
" }",
|
||||
" </script>",
|
||||
" <script type=\"module\" src=\"\"></script>",
|
||||
" <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">",
|
||||
" <link rel=\"icon\" href=\"/_/icons/logo.svg\">",
|
||||
" <link rel=\"stylesheet\" href=\"/_/code/shared.css\">",
|
||||
" <script src=\"/_/code/quill.js\"></script>",
|
||||
" <script type=\"module\" src=\"75820185/index.js\"></script>",
|
||||
" </head>",
|
||||
" <body>",
|
||||
"",
|
||||
" <body style=\"margin: 0px\">",
|
||||
" </body>",
|
||||
"</html>"
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"html": {
|
||||
"prefix": "html",
|
||||
"body": [
|
||||
"html(`$1`);"
|
||||
"html(`$`);"
|
||||
],
|
||||
"description": "Create a DOM node"
|
||||
},
|
||||
@@ -15,35 +15,17 @@
|
||||
"description": "Use the DOM collection of functions"
|
||||
},
|
||||
|
||||
"*element": {
|
||||
"prefix": "*element",
|
||||
"*shadow": {
|
||||
"prefix": "*shadow",
|
||||
"body": [
|
||||
"class Index extends HTMLElement {",
|
||||
"",
|
||||
" css = /*css*/ `",
|
||||
" index-el {",
|
||||
" ",
|
||||
" }",
|
||||
" `",
|
||||
"",
|
||||
" html = /*html*/ `",
|
||||
" `",
|
||||
"",
|
||||
" constructor() {",
|
||||
" super();",
|
||||
" addStyle(this.css);",
|
||||
" this.innerHTML = this.html;",
|
||||
" }",
|
||||
"",
|
||||
" connectedCallback() {",
|
||||
"class Index extends Shadow {",
|
||||
" render() {",
|
||||
" ",
|
||||
" }",
|
||||
"",
|
||||
"}",
|
||||
"",
|
||||
"customElements.define('index-el', Index);",
|
||||
"export default Index;"
|
||||
" ",
|
||||
"register(Index)"
|
||||
],
|
||||
"description": "Custom Element Template"
|
||||
"description": "Custom Shadow Template"
|
||||
}
|
||||
}
|
||||
BIN
_/Quill.png
Normal file
BIN
_/Quill.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
26
about.md
26
about.md
@@ -1,26 +0,0 @@
|
||||
Attribute/State Cases:
|
||||
|
||||
Dual Instantiation
|
||||
- HTML-first instantiation from attributes (when first loaded and parsed)
|
||||
observedAttributes will pick this up
|
||||
- JS-first instantiation where attributes are set from constructor (or) from init function (or)
|
||||
press puts attributes on from state before saving
|
||||
init function can set attributes and variables - perhaps state is always required to be passed in
|
||||
|
||||
Usage Flexibility
|
||||
- attributes can have default values
|
||||
$url = "hey"
|
||||
- attributes can be named or unnamed when passed in to constructor functions
|
||||
|
||||
Attribute / State Reflexivity
|
||||
- when attribute is changed, state value is changed
|
||||
modify prototype at runtime? Add overrides for setattr and remove? ||
|
||||
use observedAttributes + attributeChanged (not good) - Forms parent element?
|
||||
- when state is changed, attribute value is changed
|
||||
modify prototype at runtime to add a setter for the state such that when it is set it sets the attribute
|
||||
|
||||
Bindings
|
||||
- should be able to have a child variable be bound to that of a parent
|
||||
|
||||
Binding is denoted by prior to variable
|
||||
State is denoted by "$" prior to variable
|
||||
13
app.js
13
app.js
@@ -1,13 +0,0 @@
|
||||
/*
|
||||
Captured Sun
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
class Home extends Page {
|
||||
render = () => {
|
||||
("hello world")
|
||||
}
|
||||
}
|
||||
|
||||
export default Home
|
||||
@@ -1,5 +0,0 @@
|
||||
# To Deploy Extension
|
||||
|
||||
```vsce package```
|
||||
|
||||
```vsce publish```
|
||||
229
experiment.html
Normal file
229
experiment.html
Normal file
@@ -0,0 +1,229 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Canvas Text Editor</title>
|
||||
<style>
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
background: #d8cbb0;
|
||||
overflow: hidden;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
background: #e7dcc6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<canvas id="c"></canvas>
|
||||
|
||||
<script>
|
||||
const canvas = document.getElementById("c");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
|
||||
function resize() {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
|
||||
canvas.style.width = w + "px";
|
||||
canvas.style.height = h + "px";
|
||||
|
||||
canvas.width = Math.floor(w * dpr);
|
||||
canvas.height = Math.floor(h * dpr);
|
||||
|
||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
||||
}
|
||||
window.addEventListener("resize", resize);
|
||||
resize();
|
||||
|
||||
/* =====================
|
||||
Editor State
|
||||
===================== */
|
||||
|
||||
const fontSize = 13;
|
||||
const lineHeight = 20;
|
||||
const padding = 20;
|
||||
const font = `${fontSize}px monospace`;
|
||||
|
||||
let lines = [""];
|
||||
let cursor = { line: 0, col: 0 };
|
||||
|
||||
let blink = true;
|
||||
let blinkEnabled = true;
|
||||
let typingTimeout = null;
|
||||
|
||||
/* =====================
|
||||
Rendering
|
||||
===================== */
|
||||
|
||||
function draw() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.fillStyle = "#631414";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
ctx.font = font;
|
||||
ctx.textBaseline = "top";
|
||||
ctx.fillStyle = "#d2531a";
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
ctx.fillText(
|
||||
lines[i],
|
||||
padding,
|
||||
padding + i * lineHeight
|
||||
);
|
||||
}
|
||||
|
||||
if (blink) {
|
||||
const before = lines[cursor.line].slice(0, cursor.col);
|
||||
const x = padding + ctx.measureText(before).width;
|
||||
const y = padding - 2 + cursor.line * lineHeight;
|
||||
|
||||
ctx.fillRect(x, y, 2, fontSize + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Cursor Blink Logic
|
||||
===================== */
|
||||
|
||||
setInterval(() => {
|
||||
if (!blinkEnabled) return;
|
||||
blink = !blink;
|
||||
draw();
|
||||
}, 500);
|
||||
|
||||
function stopBlinkWhileTyping() {
|
||||
blinkEnabled = false;
|
||||
blink = true;
|
||||
draw();
|
||||
|
||||
clearTimeout(typingTimeout);
|
||||
typingTimeout = setTimeout(() => {
|
||||
blinkEnabled = true;
|
||||
blink = true;
|
||||
draw();
|
||||
}, 600);
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Helpers
|
||||
===================== */
|
||||
|
||||
function clampCursor() {
|
||||
cursor.line = Math.max(0, Math.min(cursor.line, lines.length - 1));
|
||||
cursor.col = Math.max(0, Math.min(cursor.col, lines[cursor.line].length));
|
||||
}
|
||||
|
||||
function insertText(text) {
|
||||
const line = lines[cursor.line];
|
||||
lines[cursor.line] =
|
||||
line.slice(0, cursor.col) +
|
||||
text +
|
||||
line.slice(cursor.col);
|
||||
cursor.col += text.length;
|
||||
}
|
||||
|
||||
function newline() {
|
||||
const line = lines[cursor.line];
|
||||
lines[cursor.line] = line.slice(0, cursor.col);
|
||||
lines.splice(cursor.line + 1, 0, line.slice(cursor.col));
|
||||
cursor.line++;
|
||||
cursor.col = 0;
|
||||
}
|
||||
|
||||
function backspace() {
|
||||
if (cursor.col > 0) {
|
||||
const line = lines[cursor.line];
|
||||
lines[cursor.line] =
|
||||
line.slice(0, cursor.col - 1) +
|
||||
line.slice(cursor.col);
|
||||
cursor.col--;
|
||||
} else if (cursor.line > 0) {
|
||||
const prevLen = lines[cursor.line - 1].length;
|
||||
lines[cursor.line - 1] += lines[cursor.line];
|
||||
lines.splice(cursor.line, 1);
|
||||
cursor.line--;
|
||||
cursor.col = prevLen;
|
||||
}
|
||||
}
|
||||
|
||||
/* =====================
|
||||
Keyboard Input
|
||||
===================== */
|
||||
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.metaKey || e.ctrlKey) return;
|
||||
|
||||
let changed = true;
|
||||
|
||||
switch (e.key) {
|
||||
case "ArrowLeft": cursor.col--; break;
|
||||
case "ArrowRight": cursor.col++; break;
|
||||
case "ArrowUp": cursor.line--; break;
|
||||
case "ArrowDown": cursor.line++; break;
|
||||
case "Backspace": backspace(); e.preventDefault(); break;
|
||||
case "Enter": newline(); e.preventDefault(); break;
|
||||
case "Tab": insertText(" "); e.preventDefault(); break;
|
||||
default:
|
||||
if (e.key.length === 1) {
|
||||
insertText(e.key);
|
||||
e.preventDefault();
|
||||
} else {
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
clampCursor();
|
||||
|
||||
if (changed) {
|
||||
stopBlinkWhileTyping();
|
||||
}
|
||||
|
||||
draw();
|
||||
});
|
||||
|
||||
/* =====================
|
||||
Mouse → Cursor
|
||||
===================== */
|
||||
|
||||
canvas.addEventListener("mousedown", (e) => {
|
||||
ctx.font = font;
|
||||
|
||||
const mx = e.offsetX - padding;
|
||||
const my = e.offsetY - padding;
|
||||
|
||||
cursor.line = Math.max(
|
||||
0,
|
||||
Math.min(Math.floor(my / lineHeight), lines.length - 1)
|
||||
);
|
||||
|
||||
let x = 0;
|
||||
cursor.col = 0;
|
||||
for (let i = 0; i < lines[cursor.line].length; i++) {
|
||||
const w = ctx.measureText(lines[cursor.line][i]).width;
|
||||
if (x + w / 2 >= mx) break;
|
||||
x += w;
|
||||
cursor.col++;
|
||||
}
|
||||
|
||||
blink = true;
|
||||
blinkEnabled = true;
|
||||
draw();
|
||||
});
|
||||
|
||||
/* =====================
|
||||
Initial Draw
|
||||
===================== */
|
||||
|
||||
draw();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
20
index.html
20
index.html
@@ -1,20 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Quill</title>
|
||||
<link rel="icon" href="">
|
||||
<link rel="stylesheet" href="">
|
||||
<script src=""></script>
|
||||
<script src="Quill/index.js"></script>
|
||||
<script type="module">
|
||||
import Home from "./app.js"
|
||||
|
||||
window.routes = {
|
||||
"/": Home
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
151
notes.txt
151
notes.txt
@@ -1,151 +0,0 @@
|
||||
|
||||
|
||||
|
||||
|
||||
Shadow.File = (param) => new class {
|
||||
trippy = "asd"
|
||||
|
||||
constructor() {
|
||||
console.log(param)
|
||||
}
|
||||
}
|
||||
|
||||
console.log(Shadow.File("as"))
|
||||
|
||||
|
||||
ObservedObject.WindowState = () => ({
|
||||
sidebarOut: false,
|
||||
children: null
|
||||
})
|
||||
let windowState = ObservedObject.WindowState()
|
||||
console.log(windowState)
|
||||
|
||||
|
||||
Shadow(class File {
|
||||
|
||||
}, "file-")
|
||||
|
||||
|
||||
|
||||
3/16
|
||||
const TestClass = () => new class {
|
||||
constructor() {
|
||||
console.log("hey")
|
||||
}
|
||||
}()
|
||||
|
||||
This works. Could extend shadow. This way the function and the actual code are not separate.
|
||||
|
||||
|
||||
3/10
|
||||
|
||||
Ran into a problem where I can't call the same element within itself.
|
||||
|
||||
There are two problems:
|
||||
|
||||
1. Javascript scoping means that it tries to call the class it is inside of.
|
||||
2. Quill instantiates the object when registered, to track its state
|
||||
|
||||
|
||||
This is what I ended up going with - simply not using the Space() recursively and instead making a child space.
|
||||
|
||||
class Space extends HTMLElement {
|
||||
form = Forms.observe(window.location.pathname, this)
|
||||
|
||||
contents = [
|
||||
...this.form.children.map(form => {
|
||||
switch(form.type) {
|
||||
case "file":
|
||||
return File()
|
||||
case "space":
|
||||
return ChildSpace()
|
||||
case "link":
|
||||
return Link()
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
console.log(this.form.path)
|
||||
console.log(this.contents)
|
||||
this.render(...this.contents)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
This was my attempt to see if an anonymous class can be used. The class functions and extends HTMLElement - so problem #2 is solved.
|
||||
Problem #1 however, scoping, is not solved unless putting "window.Space()" instead of Space() here, so that it does not
|
||||
attempt to access the named function value. It seems both functions and classes have this problem. Perhaps there is a
|
||||
different way it could be done in Quill, like so -
|
||||
|
||||
|
||||
const el = () => class extends HTMLElement {
|
||||
...
|
||||
}
|
||||
quill.register(el, "Space", "parchment-space")
|
||||
|
||||
|
||||
However, not naming it at the top is less than desirable.
|
||||
|
||||
quill("Space", class extends HTMLElement {
|
||||
form = Forms.observe(window.location.pathname, this)
|
||||
|
||||
contents = [
|
||||
...this.form.children.map(form => {
|
||||
switch(form.type) {
|
||||
case "file":
|
||||
return File()
|
||||
case "space":
|
||||
return ChildSpace()
|
||||
case "link":
|
||||
return Link()
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
console.log(this.form.path)
|
||||
console.log(this.contents)
|
||||
this.render(...this.contents)
|
||||
}
|
||||
})
|
||||
|
||||
this would probably work. Still less than ideal but maybe if we used syntax highlighting it could be alright.
|
||||
How to name something without the class having access? This is the problem. I didn't try it without being an arrow function so
|
||||
perhaps this would have a chance.
|
||||
|
||||
|
||||
|
||||
// const Space = () => class extends HTMLElement {
|
||||
// form = Forms.observe(window.location.pathname, this)
|
||||
|
||||
// contents = [
|
||||
// ...this.form.children.map(form => {
|
||||
// switch(form.type) {
|
||||
// case "file":
|
||||
// return File()
|
||||
// case "space":
|
||||
// return Space()
|
||||
// case "link":
|
||||
// return Link()
|
||||
// }
|
||||
// })
|
||||
// ]
|
||||
|
||||
// constructor() {
|
||||
// super()
|
||||
// console.log(this.form.path)
|
||||
// console.log(this.contents)
|
||||
// this.render(...this.contents)
|
||||
// }
|
||||
// }
|
||||
|
||||
// let instan = Space()
|
||||
// customElements.define("space-", instan)
|
||||
// window.Space = () => "boi"
|
||||
// console.log(new instan())
|
||||
|
||||
window.register(Space, "parchment-space")
|
||||
151
path.js
151
path.js
@@ -1,151 +0,0 @@
|
||||
class PathProcessor {
|
||||
path;
|
||||
hasTrailingSlash = false;
|
||||
node = (typeof module !== 'undefined' && module.exports) ? true : false;
|
||||
|
||||
constructor(path) {
|
||||
this.path = path;
|
||||
if(path === undefined) {
|
||||
this.path = ""
|
||||
}
|
||||
}
|
||||
|
||||
#removeTrailingSlash(path) {
|
||||
if(path === "/") {
|
||||
return path;
|
||||
}
|
||||
return path.endsWith("/") ? path.slice(0, -1) : path
|
||||
}
|
||||
|
||||
full() {
|
||||
if(!this.node) return;
|
||||
let path = this.path;
|
||||
this.path = ""
|
||||
this.join(Path.homedir(), path)
|
||||
return this;
|
||||
}
|
||||
|
||||
web() {
|
||||
if(!this.node) return;
|
||||
const os = require('os')
|
||||
this.path = this.path.replace(this.#removeTrailingSlash(os.homedir()), "")
|
||||
if(this.path === "") {
|
||||
this.path = "/"
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
encode() { // Uses the built in encodeURI and also encodes certain characters that are't covered by it
|
||||
this.path = encodeURI(this.path).replace(/[!'()*]/g, function(c) {
|
||||
return '%' + c.charCodeAt(0).toString(16);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
decoded() {
|
||||
this.path = decodeURIComponent(this.path);
|
||||
return this;
|
||||
}
|
||||
|
||||
parent() {
|
||||
const parts = this.path.split('/').filter(Boolean);
|
||||
parts.pop();
|
||||
this.path = '/' + parts.join('/');
|
||||
return this;
|
||||
}
|
||||
|
||||
end() {
|
||||
const parts = this.path.split('/').filter(Boolean);
|
||||
this.path = parts.pop() || '';
|
||||
return this;
|
||||
}
|
||||
|
||||
trailingSlash() {
|
||||
this.path = this.path.endsWith("/") ? this.path : this.path+"/";
|
||||
this.hasTrailingSlash = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
noTrailingSlash() {
|
||||
this.path = this.#removeTrailingSlash(this.path)
|
||||
return this;
|
||||
}
|
||||
|
||||
join(...segments) {
|
||||
if (this.path) {
|
||||
segments.unshift(this.path);
|
||||
}
|
||||
|
||||
this.path = segments
|
||||
.map((part, index) => {
|
||||
if (index === 0) {
|
||||
return part.trim().replace(/[/]*$/g, '');
|
||||
} else {
|
||||
return part.trim().replace(/(^[/]*|[/]*$)/g, '');
|
||||
}
|
||||
})
|
||||
.filter(Boolean) // Remove empty segments
|
||||
.join('/');
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
components() {
|
||||
return this.path.split('/').filter(Boolean);
|
||||
}
|
||||
|
||||
build() {
|
||||
return this.hasTrailingSlash ? this.path : this.#removeTrailingSlash(this.path)
|
||||
}
|
||||
}
|
||||
|
||||
export default class Path {
|
||||
static full(path) {
|
||||
return new PathProcessor(path).full();
|
||||
}
|
||||
|
||||
static web(path) {
|
||||
return new PathProcessor(path).web();
|
||||
}
|
||||
|
||||
static decoded(path) {
|
||||
return new PathProcessor(path).decoded()
|
||||
}
|
||||
|
||||
static encode(path) {
|
||||
return new PathProcessor(path).encode()
|
||||
}
|
||||
|
||||
static parent(path) {
|
||||
return new PathProcessor(path).parent();
|
||||
}
|
||||
|
||||
static end(path) {
|
||||
return new PathProcessor(path).end();
|
||||
}
|
||||
|
||||
static trailingSlash(path) {
|
||||
return new PathProcessor(path).trailingSlash();
|
||||
}
|
||||
|
||||
static noTrailingSlash(path) {
|
||||
return new PathProcessor(path).noTrailingSlash();
|
||||
}
|
||||
|
||||
static components(path) {
|
||||
return new PathProcessor(path).components();
|
||||
}
|
||||
|
||||
static join(...segments) {
|
||||
return new PathProcessor(null).join(...segments);
|
||||
}
|
||||
|
||||
static homedir() {
|
||||
if(typeof module === 'undefined' || !module.exports) return;
|
||||
const os = require('os')
|
||||
let ret = os.homedir().replace(/\\/g, '/').replace(/^[a-zA-Z]:/, '');
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
window.Path = Path;
|
||||
Reference in New Issue
Block a user