Compare commits
10 Commits
48529ae3e2
...
ed6d885557
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed6d885557 | ||
|
|
6e2b4dcdbd | ||
|
|
71b08bd184 | ||
|
|
1060797170 | ||
|
|
cfdf67998d | ||
|
|
9432c65fa4 | ||
|
|
65f79d1631 | ||
|
|
41ec6b7dd3 | ||
|
|
8b2f9e2b77 | ||
|
|
07725994b9 |
1
.env
Normal file
1
.env
Normal file
@@ -0,0 +1 @@
|
||||
VSCE_PAT=9wucEHBNFD5RTS403Oyoflyi984cAwAfa42FHN7tmqmzfbXsSKP9JQQJ99BKACAAAAAAAAAAAAAGAZDO25Ro
|
||||
39
README.md
39
README.md
@@ -5,25 +5,42 @@
|
||||
</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:
|
||||
Go to https://github.com/capturedsun/template-quill and clone this repo.
|
||||
|
||||
### App:
|
||||
src/app.js is the application entry point. Any Shadows rendered inside will be rendered in the body.
|
||||
|
||||
### Rendering Elements:
|
||||
|
||||
The basis of rendering is the Shadow, which extends HTMLElement. The Shadow is the equivalent of a React Component.
|
||||
|
||||
To create a Shadow, simply:
|
||||
- Create a class which extends Shadow
|
||||
- Include a render() function
|
||||
- Register the shadow as an element
|
||||
|
||||
...
|
||||
```
|
||||
document.body.append(
|
||||
p("Hi")
|
||||
.padding("top", 12),
|
||||
class File extends Shadow {
|
||||
render = () => {
|
||||
p(this.name)
|
||||
p("asd")
|
||||
}
|
||||
}
|
||||
|
||||
Link({href: "google.com", name: "hey"})
|
||||
.background("green")
|
||||
|
||||
// NavigationBar()
|
||||
// .onClick()
|
||||
)
|
||||
register(File, "file-el")
|
||||
```
|
||||
|
||||
## Needs Support:
|
||||
Ternaries within render()
|
||||
Other statements within render()
|
||||
Multiple-argument attributes in render()
|
||||
|
||||
## Limitations:
|
||||
While rendering is underway, an element's state can only be accessed from within that element
|
||||
|
||||
## Boilerplate:
|
||||
- ```*html```: Type in an HTML file and select the suggestion to create HTML boilerplate.
|
||||
|
||||
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,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)
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
}, randomName("file"))
|
||||
let obj = WindowState.create()
|
||||
console.log(obj)
|
||||
|
||||
|
||||
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 {
|
||||
@@ -155,5 +125,38 @@ window.testSuites.push( class testRender {
|
||||
return "No reactivity for adding children"
|
||||
}
|
||||
}
|
||||
|
||||
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!"
|
||||
// }
|
||||
// }
|
||||
|
||||
})
|
||||
12
Test/test.js
12
Test/test.js
@@ -1,11 +1,13 @@
|
||||
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 ("./Skeleton/parse.test.js")
|
||||
await import ("./Skeleton/init.test.js")
|
||||
await import ("./Skeleton/observedobject.test.js")
|
||||
await import ("./Skeleton/parserender.test.js")
|
||||
await import ("./Skeleton/state.test.js")
|
||||
await import ("./Element/stacks.test.js")
|
||||
await import ("./Element/Group.test.js")
|
||||
|
||||
window.randomName = function randomName(prefix) {
|
||||
const sanitizedPrefix = prefix.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
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```
|
||||
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