Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Build and deploy Pages

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-slim
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- uses: taiki-e/install-action@just
- run: npm ci
- run: just style
- run: just build
- uses: actions/upload-pages-artifact@v3
if: github.event_name != 'pull_request'
with:
path: dist

deploy:
if: github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-slim
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v4
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# System files
.DS_Store
.idea

# Editor settings
.idea/

# Installed dependencies
node_modules/

# Generated site
dist/
10 changes: 10 additions & 0 deletions app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link href="./styles/style.css" rel="stylesheet" type="text/css" />
<link href="./styles/colors.css" rel="stylesheet" type="text/css" />
<script type="module" src="./src/pages/index.js" defer></script>
<title>StartTreeV2 - Home</title>
</head>
</html>
10 changes: 10 additions & 0 deletions app/pages/edit.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link href="../styles/style.css" rel="stylesheet" type="text/css" />
<link href="../styles/colors.css" rel="stylesheet" type="text/css" />
<title>StartTreeV3 - Edit</title>
<script type="module" src="../src/pages/edit.js" defer></script>
</head>
</html>
10 changes: 10 additions & 0 deletions app/pages/view.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<link href="../styles/style.css" rel="stylesheet" type="text/css" />
<link href="../styles/colors.css" rel="stylesheet" type="text/css" />
<script type="module" src="../src/pages/view.js" defer></script>
<title>StartTreeV3</title>
</head>
</html>
5 changes: 4 additions & 1 deletion js/helper/dragOptions.js → app/src/helper/dragOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export default class DragOptions {
constructor({ data, validDropzones }) {
data: string;
validDropzones: string[];

constructor({ data, validDropzones }: { data?: string; validDropzones?: string[] }) {
this.data = data ?? "";
this.validDropzones = validDropzones ?? []; // classes that this draggable can be dropped on
}
Expand Down
34 changes: 17 additions & 17 deletions js/helper/jsurl.js → app/src/helper/jsurl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@
* OTHER DEALINGS IN THE SOFTWARE.
*/
//
let stringify = function stringify(v) {
function encode(s) {
let stringify = function stringify(v: unknown): string | undefined {
function encode(s: string): string {
return !/[^\w-.]/.test(s)
? s
: s.replace(/[^\w-.]/g, function (ch) {
: s.replace(/[^\w-.]/g, function (ch: string) {
if (ch === "$") return "!";
ch = ch.charCodeAt(0);
const code = ch.charCodeAt(0);
// thanks to Douglas Crockford for the negative slice trick
return ch < 0x100
? "*" + ("00" + ch.toString(16)).slice(-2)
: "**" + ("0000" + ch.toString(16)).slice(-4);
return code < 0x100
? "*" + ("00" + code.toString(16)).slice(-2)
: "**" + ("0000" + code.toString(16)).slice(-4);
});
}

Expand All @@ -59,8 +59,8 @@ let stringify = function stringify(v) {
return "~(" + (tmpAry.join("") || "~") + ")";
} else {
for (var key in v) {
if (v.hasOwnProperty(key)) {
var val = stringify(v[key]);
if (Object.prototype.hasOwnProperty.call(v, key)) {
var val = stringify((v as Record<string, unknown>)[key]);

// skip undefined and functions
if (val) {
Expand All @@ -83,13 +83,13 @@ var reserved = {
null: null,
};

let parse = function (s) {
if (!s) return s;
s = s.replace(/%(25)*27/g, "'");
let parse = function (input: string | null): unknown {
if (!input) return input;
let s = input.replace(/%(25)*27/g, "'");
var i = 0,
len = s.length;

function eat(expected) {
function eat(expected: string) {
if (s.charAt(i) !== expected)
throw new Error(
"bad JSURL syntax: expected " + expected + ", got " + (s && s.charAt(i))
Expand Down Expand Up @@ -123,7 +123,7 @@ let parse = function (s) {
return r + s.substring(beg, i);
}

return (function parseOne() {
return (function parseOne(): unknown {
var result, ch, beg;
eat("~");
switch ((ch = s.charAt(i))) {
Expand All @@ -138,11 +138,11 @@ let parse = function (s) {
} while (s.charAt(i) === "~");
}
} else {
result = {};
result = {} as Record<string, unknown>;
if (s.charAt(i) !== ")") {
do {
var key = decode();
result[key] = parseOne();
(result as Record<string, unknown>)[key] = parseOne();
} while (s.charAt(i) === "~" && ++i);
}
}
Expand All @@ -159,7 +159,7 @@ let parse = function (s) {
if (/[\d\-]/.test(ch)) {
result = parseFloat(sub);
} else {
result = reserved[sub];
result = reserved[sub as keyof typeof reserved];
if (typeof result === "undefined")
throw new Error("bad value keyword: " + sub);
}
Expand Down
16 changes: 9 additions & 7 deletions js/helper/makeDraggable.js → app/src/helper/makeDraggable.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
import DragOptions from "./dragOptions.js";
export type DragAction = "dragstart" | "dragend" | "dragover" | "dragenter" | "dragleave" | "drop";
// ====================================================== //
// ==================== makeDraggable =================== //
// ====================================================== //

// helper method to make an element draggable, configured with DragOptions

let lastDropWasValid = false;
export default (element, dragOptions, callback) => {
export default (element: HTMLElement, dragOptions: DragOptions, callback: (type: DragAction, event: DragEvent) => void) => {
element.draggable = true;
element.classList.add("dropzone");

element.addEventListener("dragstart", (event) => {
event.stopPropagation();
event.dataTransfer.setData("text", dragOptions.data);
event.dataTransfer?.setData("text", dragOptions.data);
callback("dragstart", event);
});

element.addEventListener("dragend", (event) => {
event.preventDefault();
if (event.dataTransfer.dropEffect != "none" && lastDropWasValid) {
if (event.dataTransfer?.dropEffect != "none" && lastDropWasValid) {
callback("dragend", event);
event.stopPropagation();
}
Expand Down Expand Up @@ -45,22 +47,22 @@ export default (element, dragOptions, callback) => {
element.addEventListener("drop", (event) => {
event.preventDefault();
lastDropWasValid = _isValidDropzone(
JSON.parse(event.dataTransfer.getData("text"))
JSON.parse(event.dataTransfer?.getData("text") ?? "{}")
);
if (lastDropWasValid) {
event.stopPropagation();
callback("drop", event);
}
});

const _isValidDropzone = (dropZone) => {
const _isValidDropzone = (dropZone: { classList: string[] }) => {
// check if an element of dropzone.classlist is in the array dragOptions.classList
return _isValid(dropZone, dragOptions.validDropzones);
};

const _isValid = (element, classes) => {
const _isValid = (element: { classList: string[] }, classes: string[]) => {
for (let i = 0; i < classes.length; i++) {
if ([...element.classList].indexOf(classes[i]) > -1) {
if ([...element.classList].indexOf(classes[i]!) > -1) {
return true;
}
}
Expand Down
3 changes: 3 additions & 0 deletions app/src/helper/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function insertAfter(newNode: Node, existingNode: Node) {
existingNode.parentNode?.insertBefore(newNode, existingNode.nextSibling);
}
7 changes: 4 additions & 3 deletions js/html/edit.js → app/src/pages/edit.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
import type { TreeConfig } from "../views/tree/components/treeTypes.js";
import EditTree from "../views/tree/editTree/components/editTree.js";
import { parse, stringify } from "../helper/jsurl.js";
import Button from "../views/other/button.js";

const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const treeConfig = parse(urlParams.get("t")) || {};
const treeConfig = (parse(urlParams.get("t")) as TreeConfig | null) ?? { bmc: [], s: {}, t: {} };
const REPO_URL = "https://github.com/AlexW00/StartTreeV2";

const t = new EditTree(treeConfig, true);
const t = new EditTree(treeConfig);
document.body.appendChild(t.html());

const getExportUrl = () => {
const host = window.location.protocol + "//" + location.host,
path = location.pathname,
affix = "?t=",
data = stringify(t.export());
return host + path.replace("e.html", "v.html") + affix + data;
return host + path.replace("edit.html", "view.html") + affix + data;
};

const cancelButtonHtml = () => {
Expand Down
9 changes: 9 additions & 0 deletions app/src/pages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { stringify } from "../helper/jsurl.js";

const exampleData = await fetch("./src/views/tree/exampleConfig.json").then(
(response) => response.json()
);

const jsonStringified = stringify(exampleData);

document.location.replace(`./pages/edit.html?t=${jsonStringified}`);
7 changes: 4 additions & 3 deletions js/html/view.js → app/src/pages/view.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import type { TreeConfig } from "../views/tree/components/treeTypes.js";
import Tree from "../views/tree/components/tree.js";
import { parse } from "../helper/jsurl.js";
import Button from "../views/other/button.js";

const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const treeConfig = parse(urlParams.get("t")) || {};
const editModeHref = "./e.html";
const treeConfig = (parse(urlParams.get("t")) as TreeConfig | null) ?? { bmc: [], s: {}, t: {} };
const editModeHref = "./edit.html";

const t = new Tree(treeConfig, false);
const t = new Tree(treeConfig);
document.body.appendChild(t.html());

const editModeButtonHtml = () => {
Expand Down
5 changes: 4 additions & 1 deletion js/views/other/button.js → app/src/views/other/button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@
// ====================================================== //

export default class Button {
svg: string;
type: string;

// possible values for type: "save", "cancel", "add" or "delete"
constructor(type, width, height) {
constructor(type: string, width?: number, height?: number) {
this.type = type;
if (type === "add") {
this.svg = `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@
// ====================================================== //;

export default class SearchBar {
constructor(config) {
form!: HTMLFormElement;
formInput!: HTMLInputElement;
h1!: HTMLHeadingElement;
root!: HTMLDivElement;
searchEngineNameShort: string;
searchEngineUrl: string;
sectionName!: HTMLDivElement;

constructor(config: { n?: string; u?: string }) {
this.searchEngineUrl = config.u ?? "https://duckduckgo.com/?q=";
this.searchEngineNameShort = config.n ?? "ddg";
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { TreeConfig, CategoryConfig } from "./treeTypes.js";
import ThemeChanger from "../themechanger/theme-changer.js";
import SearchBar from "./searchBar.js";
import TreeColumn from "./treeColumn.js";
Expand All @@ -7,7 +8,15 @@ import TreeColumn from "./treeColumn.js";
// ====================================================== //

export default class Tree {
constructor(config) {
bookmarkColumns: TreeColumn[];
bookmarkRow!: HTMLDivElement;
root!: HTMLDivElement;
searchBar: SearchBar;
themeChanger: ThemeChanger;
titlePrompt!: HTMLDivElement;
version: string;

constructor(config: TreeConfig) {
this.version = config.v || "0.0";

this.bookmarkColumns = this.initBookmarkColumns(config.bmc);
Expand All @@ -17,15 +26,15 @@ export default class Tree {

// ~~~~~~~~ initialization methods ~~~~~~~ //

initBookmarkColumns(config) {
initBookmarkColumns(config: CategoryConfig[][]) {
return config.map((column) => new TreeColumn(column));
}

initSearchBar(config) {
initSearchBar(config: TreeConfig["s"]) {
return new SearchBar(config);
}

initThemeChanger(config) {
initThemeChanger(config: TreeConfig["t"]) {
return new ThemeChanger(config);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ import TreeColumnCategory from "./treeColumnCategory.js";
// ====================================================== //

export default class TreeColumn {
bookmarkCategories: TreeColumnCategory[];
categoryList!: HTMLUListElement;
columnTitle!: HTMLHeadingElement;
id: string;
root!: HTMLDivElement;
tree!: HTMLDivElement;

static count = 0;
constructor(bookmarkColumn) {
constructor(bookmarkColumn: { cn: string; b: { n: string; u: string }[] }[]) {
TreeColumn.count++;
this.id = `column-${TreeColumn.count}`;
this.bookmarkCategories = this.initBookmarkCategories(bookmarkColumn);
Expand Down Expand Up @@ -67,7 +74,7 @@ export default class TreeColumn {

// ~~~~~~~~~~~~ class functionality ~~~~~~~~~~~~ //

initBookmarkCategories(categoryConfig) {
initBookmarkCategories(categoryConfig: { cn: string; b: { n: string; u: string }[] }[]) {
return categoryConfig.map((bookmarkCategory) => {
return new TreeColumnCategory(bookmarkCategory);
});
Expand Down
Loading