init mast-react
This commit is contained in:
parent
417a16cc74
commit
d04497f24d
|
@ -22,6 +22,3 @@ dist-ssr
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
dbs/
|
|
||||||
|
|
||||||
public/assets/crsqlite-*.wasm
|
|
50
mast-react-vite/README.md
Normal file
50
mast-react-vite/README.md
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
|
If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
|
||||||
|
|
||||||
|
- Configure the top-level `parserOptions` property like this:
|
||||||
|
|
||||||
|
```js
|
||||||
|
export default tseslint.config({
|
||||||
|
languageOptions: {
|
||||||
|
// other options...
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
|
||||||
|
- Optionally add `...tseslint.configs.stylisticTypeChecked`
|
||||||
|
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:
|
||||||
|
|
||||||
|
```js
|
||||||
|
// eslint.config.js
|
||||||
|
import react from 'eslint-plugin-react'
|
||||||
|
|
||||||
|
export default tseslint.config({
|
||||||
|
// Set the react version
|
||||||
|
settings: { react: { version: '18.3' } },
|
||||||
|
plugins: {
|
||||||
|
// Add the react plugin
|
||||||
|
react,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
// other rules...
|
||||||
|
// Enable its recommended rules
|
||||||
|
...react.configs.recommended.rules,
|
||||||
|
...react.configs['jsx-runtime'].rules,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
```
|
20
mast-react-vite/components.json
Normal file
20
mast-react-vite/components.json
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.js",
|
||||||
|
"css": "src/index.css",
|
||||||
|
"baseColor": "zinc",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
}
|
||||||
|
}
|
28
mast-react-vite/eslint.config.js
Normal file
28
mast-react-vite/eslint.config.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
|
||||||
|
export default tseslint.config(
|
||||||
|
{ ignores: ['dist'] },
|
||||||
|
{
|
||||||
|
extends: [js.configs.recommended, ...tseslint.configs.recommended],
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
'react-hooks': reactHooks,
|
||||||
|
'react-refresh': reactRefresh,
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
...reactHooks.configs.recommended.rules,
|
||||||
|
'react-refresh/only-export-components': [
|
||||||
|
'warn',
|
||||||
|
{ allowConstantExport: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
|
@ -1,4 +1,4 @@
|
||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
3872
mast-react-vite/package-lock.json
generated
Normal file
3872
mast-react-vite/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
42
mast-react-vite/package.json
Normal file
42
mast-react-vite/package.json
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"name": "mast-react",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
|
"@tanstack/react-table": "^8.20.5",
|
||||||
|
"@vlcn.io/react": "^3.1.0",
|
||||||
|
"@vlcn.io/rx-tbl": "^0.15.0",
|
||||||
|
"class-variance-authority": "^0.7.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.454.0",
|
||||||
|
"react": "^18.3.1",
|
||||||
|
"react-dom": "^18.3.1",
|
||||||
|
"tailwind-merge": "^2.5.4",
|
||||||
|
"tailwindcss-animate": "^1.0.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.13.0",
|
||||||
|
"@types/node": "^22.8.2",
|
||||||
|
"@types/react": "^18.3.11",
|
||||||
|
"@types/react-dom": "^18.3.1",
|
||||||
|
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||||
|
"autoprefixer": "^10.4.20",
|
||||||
|
"eslint": "^9.13.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.0.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.13",
|
||||||
|
"globals": "^15.11.0",
|
||||||
|
"postcss": "^8.4.47",
|
||||||
|
"tailwindcss": "^3.4.14",
|
||||||
|
"typescript": "~5.6.2",
|
||||||
|
"typescript-eslint": "^8.10.0",
|
||||||
|
"vite": "^5.4.9"
|
||||||
|
}
|
||||||
|
}
|
6
mast-react-vite/postcss.config.js
Normal file
6
mast-react-vite/postcss.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
@ -7,19 +7,30 @@
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
height: 6em;
|
height: 6em;
|
||||||
padding: 2em;
|
padding: 1.5em;
|
||||||
will-change: filter;
|
will-change: filter;
|
||||||
transition: filter 300ms;
|
transition: filter 300ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo:hover {
|
.logo:hover {
|
||||||
filter: drop-shadow(0 0 2em #646cffaa);
|
filter: drop-shadow(0 0 2em #646cffaa);
|
||||||
}
|
}
|
||||||
.logo.react:hover {
|
.logo.react:hover {
|
||||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||||
}
|
}
|
||||||
.logo.vlcn:hover {
|
|
||||||
filter: drop-shadow(0 0 2em #e3eaef);
|
@keyframes logo-spin {
|
||||||
|
from {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
a:nth-of-type(2) .logo {
|
||||||
|
animation: logo-spin infinite 20s linear;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
88
mast-react-vite/src/App.tsx
Normal file
88
mast-react-vite/src/App.tsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { useCachedState, useQuery } from "@vlcn.io/react";
|
||||||
|
import './App.css'
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
import { DataTable } from "@/components/ui/data-table"
|
||||||
|
import * as addParser from '../../parser/add_parser.js';
|
||||||
|
|
||||||
|
function Marshall(parsed) {
|
||||||
|
let project = "";
|
||||||
|
const tags = [];
|
||||||
|
console.log(parsed);
|
||||||
|
for (const attr of parsed.attributes) {
|
||||||
|
console.log(attr);
|
||||||
|
switch (attr.type) {
|
||||||
|
case 'project':
|
||||||
|
project = attr.value;
|
||||||
|
break;
|
||||||
|
case 'tag':
|
||||||
|
tags.push(attr.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { description: parsed.description, tags: JSON.stringify(tags), project: project };
|
||||||
|
}
|
||||||
|
|
||||||
|
type Todo = {
|
||||||
|
id: string
|
||||||
|
description: string
|
||||||
|
status: 0 | 1
|
||||||
|
tags: string[]
|
||||||
|
project: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const columns: ColumnDef<Todo>[] = [
|
||||||
|
{
|
||||||
|
accessorKey: "description",
|
||||||
|
header: "Description",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "project",
|
||||||
|
header: "Project",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "tags",
|
||||||
|
header: "tags",
|
||||||
|
cell: ({ row }) => {
|
||||||
|
console.log("Tags Parsing: " + row.getValue("tags"))
|
||||||
|
const formatted = JSON.parse(row.getValue("tags"))
|
||||||
|
return <div className="text-right font-medium">{formatted.join(", ")}</div>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function App({ ctx }) {
|
||||||
|
const todos = useQuery(ctx, "SELECT * FROM todos").data;
|
||||||
|
const [newText, setNewText] = useState("");
|
||||||
|
|
||||||
|
const addTodo = (e) => {
|
||||||
|
if (e.key === "Enter" && e.target.value.trim() !== "") {
|
||||||
|
const parsed = Marshall(addParser.parse(e.target.value));
|
||||||
|
ctx.db.exec(`INSERT INTO todos (id, description, tags, project, completed)
|
||||||
|
VALUES (lower(hex(randomblob(16))), ?, ?, ?, 0)`,
|
||||||
|
[
|
||||||
|
parsed.description,
|
||||||
|
parsed.tags,
|
||||||
|
parsed.project
|
||||||
|
]);
|
||||||
|
setNewText("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<input type="text"
|
||||||
|
onKeyUp={addTodo}
|
||||||
|
onChange={(e) => setNewText(e.target.value)}
|
||||||
|
/>
|
||||||
|
<DataTable columns={columns} data={todos} />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4 KiB |
81
mast-react-vite/src/components/ui/data-table.tsx
Normal file
81
mast-react-vite/src/components/ui/data-table.tsx
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ColumnDef,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table"
|
||||||
|
|
||||||
|
interface DataTableProps<TData, TValue> {
|
||||||
|
columns: ColumnDef<TData, TValue>[]
|
||||||
|
data: TData[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTable<TData, TValue>({
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
}: DataTableProps<TData, TValue>) {
|
||||||
|
const table = useReactTable({
|
||||||
|
data,
|
||||||
|
columns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="bg-zinc-950 dark:bg-white">
|
||||||
|
<Table >
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => {
|
||||||
|
return (
|
||||||
|
<TableHead key={header.id}>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={row.getIsSelected() && "selected"}
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id}>
|
||||||
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
120
mast-react-vite/src/components/ui/table.tsx
Normal file
120
mast-react-vite/src/components/ui/table.tsx
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Table = React.forwardRef<
|
||||||
|
HTMLTableElement,
|
||||||
|
React.HTMLAttributes<HTMLTableElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div className="relative w-full overflow-auto">
|
||||||
|
<table
|
||||||
|
ref={ref}
|
||||||
|
className={cn("w-full caption-bottom text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
Table.displayName = "Table"
|
||||||
|
|
||||||
|
const TableHeader = React.forwardRef<
|
||||||
|
HTMLTableSectionElement,
|
||||||
|
React.HTMLAttributes<HTMLTableSectionElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
||||||
|
))
|
||||||
|
TableHeader.displayName = "TableHeader"
|
||||||
|
|
||||||
|
const TableBody = React.forwardRef<
|
||||||
|
HTMLTableSectionElement,
|
||||||
|
React.HTMLAttributes<HTMLTableSectionElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<tbody
|
||||||
|
ref={ref}
|
||||||
|
className={cn("[&_tr:last-child]:border-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TableBody.displayName = "TableBody"
|
||||||
|
|
||||||
|
const TableFooter = React.forwardRef<
|
||||||
|
HTMLTableSectionElement,
|
||||||
|
React.HTMLAttributes<HTMLTableSectionElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<tfoot
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TableFooter.displayName = "TableFooter"
|
||||||
|
|
||||||
|
const TableRow = React.forwardRef<
|
||||||
|
HTMLTableRowElement,
|
||||||
|
React.HTMLAttributes<HTMLTableRowElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<tr
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TableRow.displayName = "TableRow"
|
||||||
|
|
||||||
|
const TableHead = React.forwardRef<
|
||||||
|
HTMLTableCellElement,
|
||||||
|
React.ThHTMLAttributes<HTMLTableCellElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<th
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TableHead.displayName = "TableHead"
|
||||||
|
|
||||||
|
const TableCell = React.forwardRef<
|
||||||
|
HTMLTableCellElement,
|
||||||
|
React.TdHTMLAttributes<HTMLTableCellElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<td
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TableCell.displayName = "TableCell"
|
||||||
|
|
||||||
|
const TableCaption = React.forwardRef<
|
||||||
|
HTMLTableCaptionElement,
|
||||||
|
React.HTMLAttributes<HTMLTableCaptionElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<caption
|
||||||
|
ref={ref}
|
||||||
|
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TableCaption.displayName = "TableCaption"
|
||||||
|
|
||||||
|
export {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableBody,
|
||||||
|
TableFooter,
|
||||||
|
TableHead,
|
||||||
|
TableRow,
|
||||||
|
TableCell,
|
||||||
|
TableCaption,
|
||||||
|
}
|
|
@ -1,3 +1,7 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
@ -11,7 +15,6 @@
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
-webkit-text-size-adjust: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -55,34 +58,6 @@ button:focus-visible {
|
||||||
outline: 4px auto -webkit-focus-ring-color;
|
outline: 4px auto -webkit-focus-ring-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-bottom: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table input {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
text-align: center;
|
|
||||||
padding: 8px;
|
|
||||||
font-size: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
table input:focus {
|
|
||||||
background: grey;
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
:root {
|
:root {
|
||||||
color: #213547;
|
color: #213547;
|
6
mast-react-vite/src/lib/utils.ts
Normal file
6
mast-react-vite/src/lib/utils.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { clsx, type ClassValue } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
33
mast-react-vite/src/main.tsx
Normal file
33
mast-react-vite/src/main.tsx
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import { StrictMode } from 'react'
|
||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import './index.css'
|
||||||
|
import App from "./App.tsx";
|
||||||
|
|
||||||
|
import initWasm from '@vlcn.io/crsqlite-wasm';
|
||||||
|
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
|
||||||
|
import tblrx from '@vlcn.io/rx-tbl';
|
||||||
|
|
||||||
|
const sqlite = await initWasm(() => wasmUrl);
|
||||||
|
|
||||||
|
const db = await sqlite.open("todo.db");
|
||||||
|
await db.exec(`
|
||||||
|
CREATE TABLE IF NOT EXISTS todos (
|
||||||
|
id BLOB PRIMARY KEY NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
project text,
|
||||||
|
tags text,
|
||||||
|
due text,
|
||||||
|
wait text,
|
||||||
|
priority text,
|
||||||
|
urgency real,
|
||||||
|
completed INTEGER NOT NULL DEFAULT 0
|
||||||
|
);
|
||||||
|
SELECT crsql_as_crr('todos');
|
||||||
|
`);
|
||||||
|
|
||||||
|
const rx = tblrx(db);
|
||||||
|
const ctx = { db, rx };
|
||||||
|
|
||||||
|
createRoot(document.getElementById("root") as HTMLElement).render(
|
||||||
|
<App ctx={ctx} />
|
||||||
|
);
|
60
mast-react-vite/tailwind.config.js
Normal file
60
mast-react-vite/tailwind.config.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
export default {
|
||||||
|
darkMode: ["class"],
|
||||||
|
content: [
|
||||||
|
"./index.html",
|
||||||
|
"./src/**/.{js,ts,jsx,tsx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {
|
||||||
|
borderRadius: {
|
||||||
|
lg: 'var(--radius)',
|
||||||
|
md: 'calc(var(--radius) - 2px)',
|
||||||
|
sm: 'calc(var(--radius) - 4px)'
|
||||||
|
},
|
||||||
|
colors: {
|
||||||
|
background: 'hsl(var(--background))',
|
||||||
|
foreground: 'hsl(var(--foreground))',
|
||||||
|
card: {
|
||||||
|
DEFAULT: 'hsl(var(--card))',
|
||||||
|
foreground: 'hsl(var(--card-foreground))'
|
||||||
|
},
|
||||||
|
popover: {
|
||||||
|
DEFAULT: 'hsl(var(--popover))',
|
||||||
|
foreground: 'hsl(var(--popover-foreground))'
|
||||||
|
},
|
||||||
|
primary: {
|
||||||
|
DEFAULT: 'hsl(var(--primary))',
|
||||||
|
foreground: 'hsl(var(--primary-foreground))'
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
DEFAULT: 'hsl(var(--secondary))',
|
||||||
|
foreground: 'hsl(var(--secondary-foreground))'
|
||||||
|
},
|
||||||
|
muted: {
|
||||||
|
DEFAULT: 'hsl(var(--muted))',
|
||||||
|
foreground: 'hsl(var(--muted-foreground))'
|
||||||
|
},
|
||||||
|
accent: {
|
||||||
|
DEFAULT: 'hsl(var(--accent))',
|
||||||
|
foreground: 'hsl(var(--accent-foreground))'
|
||||||
|
},
|
||||||
|
destructive: {
|
||||||
|
DEFAULT: 'hsl(var(--destructive))',
|
||||||
|
foreground: 'hsl(var(--destructive-foreground))'
|
||||||
|
},
|
||||||
|
border: 'hsl(var(--border))',
|
||||||
|
input: 'hsl(var(--input))',
|
||||||
|
ring: 'hsl(var(--ring))',
|
||||||
|
chart: {
|
||||||
|
'1': 'hsl(var(--chart-1))',
|
||||||
|
'2': 'hsl(var(--chart-2))',
|
||||||
|
'3': 'hsl(var(--chart-3))',
|
||||||
|
'4': 'hsl(var(--chart-4))',
|
||||||
|
'5': 'hsl(var(--chart-5))'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [require("tailwindcss-animate")],
|
||||||
|
}
|
36
mast-react-vite/tsconfig.app.json
Normal file
36
mast-react-vite/tsconfig.app.json
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"lib": [
|
||||||
|
"ES2020",
|
||||||
|
"DOM",
|
||||||
|
"DOM.Iterable"
|
||||||
|
],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
/* shadcn */
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src"
|
||||||
|
]
|
||||||
|
}
|
19
mast-react-vite/tsconfig.json
Normal file
19
mast-react-vite/tsconfig.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
mast-react-vite/tsconfig.node.json
Normal file
23
mast-react-vite/tsconfig.node.json
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2022",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "Bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedSideEffectImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
13
mast-react-vite/vite.config.ts
Normal file
13
mast-react-vite/vite.config.ts
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react-swc'
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,6 +0,0 @@
|
||||||
fly.toml
|
|
||||||
Dockerfile
|
|
||||||
.dockerignore
|
|
||||||
node_modules
|
|
||||||
.git
|
|
||||||
dbs
|
|
|
@ -1,14 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
env: { browser: true, es2020: true },
|
|
||||||
extends: [
|
|
||||||
'eslint:recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended',
|
|
||||||
'plugin:react-hooks/recommended',
|
|
||||||
],
|
|
||||||
parser: '@typescript-eslint/parser',
|
|
||||||
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
|
|
||||||
plugins: ['react-refresh'],
|
|
||||||
rules: {
|
|
||||||
'react-refresh/only-export-components': 'warn',
|
|
||||||
},
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
{
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": false
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
nodeLinker: node-modules
|
|
|
@ -1,32 +0,0 @@
|
||||||
FROM debian:bullseye as builder
|
|
||||||
|
|
||||||
ENV PATH=/usr/local/node/bin:$PATH
|
|
||||||
ARG NODE_VERSION=19.0.1
|
|
||||||
|
|
||||||
RUN apt-get update; apt install -y curl python-is-python3 pkg-config build-essential && \
|
|
||||||
curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
|
|
||||||
/tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
|
|
||||||
rm -rf /tmp/node-build-master
|
|
||||||
|
|
||||||
RUN mkdir /app
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN npm install -g pnpm
|
|
||||||
RUN pnpm install
|
|
||||||
RUN pnpm run build
|
|
||||||
|
|
||||||
|
|
||||||
FROM debian:bullseye-slim
|
|
||||||
|
|
||||||
LABEL fly_launch_runtime="nodejs"
|
|
||||||
|
|
||||||
COPY --from=builder /usr/local/node /usr/local/node
|
|
||||||
COPY --from=builder /app /app
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
ENV NODE_ENV production
|
|
||||||
ENV PATH /usr/local/node/bin:$PATH
|
|
||||||
|
|
||||||
CMD [ "pnpm", "run", "start" ]
|
|
|
@ -1,30 +0,0 @@
|
||||||
# vite-starter
|
|
||||||
|
|
||||||
Getting started with pnpm/npm/yarn create:
|
|
||||||
|
|
||||||
```
|
|
||||||
# npm
|
|
||||||
npm create @vlcn.io@latest your-app-name
|
|
||||||
# pnpm
|
|
||||||
pnpm create @vlcn.io your-app-name
|
|
||||||
# yarn
|
|
||||||
yarn create @vlcn.io your-app-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Or, just cloning directly:
|
|
||||||
|
|
||||||
```
|
|
||||||
git clone git@github.com:vlcn-io/vite-starter.git
|
|
||||||
npm install
|
|
||||||
npm dev
|
|
||||||
```
|
|
||||||
|
|
||||||
What you get:
|
|
||||||
- A client ([App.tsx](https://github.com/vlcn-io/vite-starter/blob/main/src/App.tsx)) that runs a SQLite DB
|
|
||||||
- A server ([server.js](https://github.com/vlcn-io/vite-starter/blob/main/server.js)) that the client (or many clients) can sync to when online
|
|
||||||
- A database schema file ([schemas/main.mjs](https://github.com/vlcn-io/vite-starter/blob/main/src/schemas/main.mjs) that is automatically migrated to (auto migration is still in beta! You may find yourself needing to wipe the DB (clear indexeddb or change dbid) when using auto-migrate) on server and client restart.
|
|
||||||
|
|
||||||
|
|
||||||
Demo Video: [![example scaffolding result](https://img.youtube.com/vi/QJBQLYmXReI/0.jpg)](https://www.youtube.com/watch?v=QJBQLYmXReI)
|
|
||||||
|
|
||||||
Deployed Scaffolding: https://vite-starter2.fly.dev/ Try it out! Collaborate between all your devices.
|
|
|
@ -1,25 +0,0 @@
|
||||||
# This directory is where your application will access the database.
|
|
||||||
fuse:
|
|
||||||
dir: "/app/dbs"
|
|
||||||
|
|
||||||
# This directory is where LiteFS will store internal data.
|
|
||||||
# You must place this directory on a persistent volume.
|
|
||||||
data:
|
|
||||||
dir: "/var/lib/litefs"
|
|
||||||
|
|
||||||
exec:
|
|
||||||
- cmd: "pnpm run start"
|
|
||||||
|
|
||||||
# The lease section specifies how the cluster will be managed. We're using the
|
|
||||||
# "consul" lease type so that our application can dynamically change the primary.
|
|
||||||
#
|
|
||||||
# These environment variables will be available in your Fly.io application.
|
|
||||||
lease:
|
|
||||||
type: "consul"
|
|
||||||
advertise-url: "http://${HOSTNAME}.vm.${FLY_APP_NAME}.internal:20202"
|
|
||||||
candidate: ${FLY_REGION == PRIMARY_REGION}
|
|
||||||
promote: true
|
|
||||||
|
|
||||||
consul:
|
|
||||||
url: "${FLY_CONSUL_URL}"
|
|
||||||
key: "litefs/${FLY_APP_NAME}"
|
|
|
@ -1,45 +0,0 @@
|
||||||
# fly.toml app configuration file generated for mast-react on 2023-06-01T10:35:47-04:00
|
|
||||||
#
|
|
||||||
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
|
||||||
#
|
|
||||||
|
|
||||||
app = "vite-starter2"
|
|
||||||
primary_region = "iad"
|
|
||||||
kill_signal = "SIGINT"
|
|
||||||
kill_timeout = "5s"
|
|
||||||
|
|
||||||
[experimental]
|
|
||||||
auto_rollback = true
|
|
||||||
|
|
||||||
[env]
|
|
||||||
PORT = "8080"
|
|
||||||
PRIMARY_REGION = "iad"
|
|
||||||
|
|
||||||
[[mounts]]
|
|
||||||
source = "dbs_machines"
|
|
||||||
destination = "/app/dbs"
|
|
||||||
processes = ["app"]
|
|
||||||
|
|
||||||
[[services]]
|
|
||||||
protocol = "tcp"
|
|
||||||
internal_port = 8080
|
|
||||||
processes = ["app"]
|
|
||||||
|
|
||||||
[[services.ports]]
|
|
||||||
port = 80
|
|
||||||
handlers = ["http"]
|
|
||||||
force_https = true
|
|
||||||
|
|
||||||
[[services.ports]]
|
|
||||||
port = 443
|
|
||||||
handlers = ["tls", "http"]
|
|
||||||
[services.concurrency]
|
|
||||||
type = "connections"
|
|
||||||
hard_limit = 250
|
|
||||||
soft_limit = 200
|
|
||||||
|
|
||||||
[[services.tcp_checks]]
|
|
||||||
interval = "15s"
|
|
||||||
timeout = "2s"
|
|
||||||
grace_period = "1s"
|
|
||||||
restart_limit = 0
|
|
|
@ -1,12 +0,0 @@
|
||||||
more regions:
|
|
||||||
https://fly.io/docs/litefs/speedrun/#5-add-some-replicas-in-other-regions
|
|
||||||
|
|
||||||
```
|
|
||||||
fly m clone --select --region lhr
|
|
||||||
```
|
|
||||||
|
|
||||||
consul:
|
|
||||||
|
|
||||||
```
|
|
||||||
fly consul attach
|
|
||||||
```
|
|
7718
mast-react/package-lock.json
generated
7718
mast-react/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,38 +0,0 @@
|
||||||
{
|
|
||||||
"name": "mast-react",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.0",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "node ./server.js",
|
|
||||||
"start": "NODE_ENV=production node ./server.js",
|
|
||||||
"build": "tsc && vite build",
|
|
||||||
"lint": "eslint src --ext ts,tsx --report-unused-disable-directives --max-warnings 0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@vlcn.io/crsqlite-wasm": "0.16.0",
|
|
||||||
"@vlcn.io/react": "3.1.0",
|
|
||||||
"@vlcn.io/ws-browserdb": "0.2.0",
|
|
||||||
"@vlcn.io/ws-client": "0.2.0",
|
|
||||||
"@vlcn.io/ws-server": "0.2.3",
|
|
||||||
"cors": "^2.8.5",
|
|
||||||
"express": "^4.18.2",
|
|
||||||
"react": "^18.2.0",
|
|
||||||
"react-dom": "^18.2.0",
|
|
||||||
"uuid4": "^2.0.3",
|
|
||||||
"vite-express": "^0.9.2"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/react": "^18.0.28",
|
|
||||||
"@types/react-dom": "^18.0.11",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^5.57.1",
|
|
||||||
"@typescript-eslint/parser": "^5.57.1",
|
|
||||||
"@vitejs/plugin-react": "^4.0.0",
|
|
||||||
"eslint": "^8.38.0",
|
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
|
||||||
"eslint-plugin-react-refresh": "^0.3.4",
|
|
||||||
"typescript": "^5.0.2",
|
|
||||||
"vite": "^4.3.9",
|
|
||||||
"vite-plugin-pwa": "^0.16.4"
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,23 +0,0 @@
|
||||||
import express from "express";
|
|
||||||
import ViteExpress from "vite-express";
|
|
||||||
import { attachWebsocketServer } from "@vlcn.io/ws-server";
|
|
||||||
import * as http from "http";
|
|
||||||
|
|
||||||
const PORT = parseInt(process.env.PORT || "8080");
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
const server = http.createServer(app);
|
|
||||||
|
|
||||||
const wsConfig = {
|
|
||||||
dbFolder: "./dbs",
|
|
||||||
schemaFolder: "./src/schemas",
|
|
||||||
pathPattern: /\/sync/,
|
|
||||||
};
|
|
||||||
|
|
||||||
attachWebsocketServer(server, wsConfig);
|
|
||||||
|
|
||||||
server.listen(PORT, () =>
|
|
||||||
console.log("info", `listening on http://localhost:${PORT}!`)
|
|
||||||
);
|
|
||||||
|
|
||||||
ViteExpress.bind(app, server);
|
|
|
@ -1,148 +0,0 @@
|
||||||
import { CtxAsync, useCachedState, useQuery, useSync } from "@vlcn.io/react";
|
|
||||||
import reactLogo from "./assets/react.svg";
|
|
||||||
import viteLogo from "/vite.svg";
|
|
||||||
import vlcnLogo from "./assets/vlcn.png";
|
|
||||||
import "./App.css";
|
|
||||||
import randomWords from "./support/randomWords.js";
|
|
||||||
import { useDB } from "@vlcn.io/react";
|
|
||||||
import SyncWorker from "./sync-worker.js?worker";
|
|
||||||
|
|
||||||
type TestRecord = { id: string; name: string };
|
|
||||||
const wordOptions = { exactly: 3, join: " " };
|
|
||||||
|
|
||||||
function getEndpoint() {
|
|
||||||
let proto = "ws:";
|
|
||||||
const host = window.location.host;
|
|
||||||
if (window.location.protocol === "https:") {
|
|
||||||
proto = "wss:";
|
|
||||||
}
|
|
||||||
|
|
||||||
return `${proto}//${host}/sync`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const worker = new SyncWorker();
|
|
||||||
function App({ dbname }: { dbname: string }) {
|
|
||||||
const ctx = useDB(dbname);
|
|
||||||
useSync({
|
|
||||||
dbname,
|
|
||||||
endpoint: getEndpoint(),
|
|
||||||
room: dbname,
|
|
||||||
worker,
|
|
||||||
});
|
|
||||||
const data = useQuery<TestRecord>(
|
|
||||||
ctx,
|
|
||||||
"SELECT * FROM test ORDER BY id DESC"
|
|
||||||
).data;
|
|
||||||
|
|
||||||
const addData = () => {
|
|
||||||
ctx.db.exec("INSERT INTO test (id, name) VALUES (?, ?);", [
|
|
||||||
nanoid(10),
|
|
||||||
randomWords(wordOptions) as string,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dropData = () => {
|
|
||||||
ctx.db.exec("DELETE FROM test;");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div>
|
|
||||||
<a href="https://vitejs.dev" target="_blank">
|
|
||||||
<img src={viteLogo} className="logo" alt="Vite logo" />
|
|
||||||
</a>
|
|
||||||
<a href="https://react.dev" target="_blank">
|
|
||||||
<img src={reactLogo} className="logo react" alt="React logo" />
|
|
||||||
</a>
|
|
||||||
<a href="https://vlcn.io" target="_blank">
|
|
||||||
<img src={vlcnLogo} className="logo vlcn" alt="Vulcan logo" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h1>Vite + React + Vulcan</h1>
|
|
||||||
<div className="card">
|
|
||||||
<button onClick={addData} style={{ marginRight: "1em" }}>
|
|
||||||
Add Data
|
|
||||||
</button>
|
|
||||||
<button onClick={dropData}>Drop Data</button>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>Name</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{data.map((row) => (
|
|
||||||
<tr key={row.id}>
|
|
||||||
<td>{row.id}</td>
|
|
||||||
<td>
|
|
||||||
<EditableItem ctx={ctx} id={row.id} value={row.name} />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p>
|
|
||||||
Edit <code>src/App.tsx</code> and save to test HMR
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Open another browser and navigate to{" "}
|
|
||||||
<a href={window.location.href} target="_blank">
|
|
||||||
this window's url
|
|
||||||
</a>{" "}
|
|
||||||
to test sync.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<p className="read-the-docs">
|
|
||||||
Click on the Vite, React and Vulcan logos to learn more
|
|
||||||
</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function EditableItem({
|
|
||||||
ctx,
|
|
||||||
id,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
ctx: CtxAsync;
|
|
||||||
id: string;
|
|
||||||
value: string;
|
|
||||||
}) {
|
|
||||||
// Generally you will not need to use `useCachedState`. It is only required for highly interactive components
|
|
||||||
// that write to the database on every interaction (e.g., keystroke or drag) or in cases where you want
|
|
||||||
// to de-bounce your writes to the DB.
|
|
||||||
//
|
|
||||||
// `useCachedState` will never be required once when one of the following is true:
|
|
||||||
// a. We complete the synchronous Reactive SQL layer (SQLiteRX)
|
|
||||||
// b. We figure out how to get SQLite-WASM to do a write + read round-trip in a single event loop tick
|
|
||||||
const [cachedValue, setCachedValue] = useCachedState(value);
|
|
||||||
const onChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
setCachedValue(e.target.value);
|
|
||||||
// You could de-bounce your write to the DB here if so desired.
|
|
||||||
return ctx.db.exec("UPDATE test SET name = ? WHERE id = ?;", [
|
|
||||||
e.target.value,
|
|
||||||
id,
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
return <input type="text" value={cachedValue} onChange={onChange} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
|
|
||||||
const nanoid = (t = 21) =>
|
|
||||||
crypto
|
|
||||||
.getRandomValues(new Uint8Array(t))
|
|
||||||
.reduce(
|
|
||||||
(t, e) =>
|
|
||||||
(t +=
|
|
||||||
(e &= 63) < 36
|
|
||||||
? e.toString(36)
|
|
||||||
: e < 62
|
|
||||||
? (e - 26).toString(36).toUpperCase()
|
|
||||||
: e > 62
|
|
||||||
? "-"
|
|
||||||
: "_"),
|
|
||||||
""
|
|
||||||
);
|
|
|
@ -1,82 +0,0 @@
|
||||||
import App from "./App.tsx";
|
|
||||||
import schemaContent from "./schemas/main2.sql?raw";
|
|
||||||
import { DBProvider } from "@vlcn.io/react";
|
|
||||||
import { useEffect, useState } from "react";
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a random room name to sync with or pulls one from local storage.
|
|
||||||
*/
|
|
||||||
function getRoom(hash: HashBag): string {
|
|
||||||
return hash.room || localStorage.getItem("room") || newRoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
function hashChanged() {
|
|
||||||
const hash = parseHash();
|
|
||||||
const room = getRoom(hash);
|
|
||||||
if (room != hash.room) {
|
|
||||||
hash.room = room;
|
|
||||||
window.location.hash = writeHash(hash);
|
|
||||||
}
|
|
||||||
localStorage.setItem("room", room);
|
|
||||||
return room;
|
|
||||||
}
|
|
||||||
const room = hashChanged();
|
|
||||||
|
|
||||||
export default function Root() {
|
|
||||||
const [theRoom, setTheRoom] = useState(room);
|
|
||||||
useEffect(() => {
|
|
||||||
const cb = () => {
|
|
||||||
const room = hashChanged();
|
|
||||||
if (room != theRoom) {
|
|
||||||
setTheRoom(room);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
addEventListener("hashchange", cb);
|
|
||||||
return () => {
|
|
||||||
removeEventListener("hashchange", cb);
|
|
||||||
};
|
|
||||||
}, []); // ignore -- theRoom is managed by the effect
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DBProvider
|
|
||||||
dbname={theRoom}
|
|
||||||
schema={{
|
|
||||||
name: "main2.sql",
|
|
||||||
content: schemaContent,
|
|
||||||
}}
|
|
||||||
Render={() => <App dbname={theRoom} />}
|
|
||||||
></DBProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
type HashBag = { [key: string]: string };
|
|
||||||
function parseHash(): HashBag {
|
|
||||||
const hash = window.location.hash;
|
|
||||||
const ret: { [key: string]: string } = {};
|
|
||||||
if (hash.length > 1) {
|
|
||||||
const substr = hash.substring(1);
|
|
||||||
const parts = substr.split(",");
|
|
||||||
for (const part of parts) {
|
|
||||||
const [key, value] = part.split("=");
|
|
||||||
ret[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeHash(hash: HashBag) {
|
|
||||||
const parts = [];
|
|
||||||
for (const key in hash) {
|
|
||||||
parts.push(`${key}=${hash[key]}`);
|
|
||||||
}
|
|
||||||
return parts.join(",");
|
|
||||||
}
|
|
||||||
|
|
||||||
function newRoom() {
|
|
||||||
let uuid = uuidv4();
|
|
||||||
return uuid.replaceAll("-", "");
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 70 KiB |
Binary file not shown.
Before Width: | Height: | Size: 26 KiB |
|
@ -1,13 +0,0 @@
|
||||||
import ReactDOM from "react-dom/client";
|
|
||||||
|
|
||||||
import "./index.css";
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import Root from "./Root.tsx";
|
|
||||||
|
|
||||||
// Launch our app.
|
|
||||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
|
||||||
<React.StrictMode>
|
|
||||||
<Root />
|
|
||||||
</React.StrictMode>
|
|
||||||
);
|
|
|
@ -1,2 +0,0 @@
|
||||||
CREATE TABLE IF NOT EXISTS test (id PRIMARY KEY NOT NULL, name TEXT);
|
|
||||||
SELECT crsql_as_crr('test');
|
|
|
@ -1,338 +0,0 @@
|
||||||
var wordList = [
|
|
||||||
// Borrowed from xkcd password generator which borrowed it from wherever
|
|
||||||
"ability","able","aboard","about","above","accept","accident","according",
|
|
||||||
"account","accurate","acres","across","act","action","active","activity",
|
|
||||||
"actual","actually","add","addition","additional","adjective","adult","adventure",
|
|
||||||
"advice","affect","afraid","after","afternoon","again","against","age",
|
|
||||||
"ago","agree","ahead","aid","air","airplane","alike","alive",
|
|
||||||
"all","allow","almost","alone","along","aloud","alphabet","already",
|
|
||||||
"also","although","am","among","amount","ancient","angle","angry",
|
|
||||||
"animal","announced","another","answer","ants","any","anybody","anyone",
|
|
||||||
"anything","anyway","anywhere","apart","apartment","appearance","apple","applied",
|
|
||||||
"appropriate","are","area","arm","army","around","arrange","arrangement",
|
|
||||||
"arrive","arrow","art","article","as","aside","ask","asleep",
|
|
||||||
"at","ate","atmosphere","atom","atomic","attached","attack","attempt",
|
|
||||||
"attention","audience","author","automobile","available","average","avoid","aware",
|
|
||||||
"away","baby","back","bad","badly","bag","balance","ball",
|
|
||||||
"balloon","band","bank","bar","bare","bark","barn","base",
|
|
||||||
"baseball","basic","basis","basket","bat","battle","be","bean",
|
|
||||||
"bear","beat","beautiful","beauty","became","because","become","becoming",
|
|
||||||
"bee","been","before","began","beginning","begun","behavior","behind",
|
|
||||||
"being","believed","bell","belong","below","belt","bend","beneath",
|
|
||||||
"bent","beside","best","bet","better","between","beyond","bicycle",
|
|
||||||
"bigger","biggest","bill","birds","birth","birthday","bit","bite",
|
|
||||||
"black","blank","blanket","blew","blind","block","blood","blow",
|
|
||||||
"blue","board","boat","body","bone","book","border","born",
|
|
||||||
"both","bottle","bottom","bound","bow","bowl","box","boy",
|
|
||||||
"brain","branch","brass","brave","bread","break","breakfast","breath",
|
|
||||||
"breathe","breathing","breeze","brick","bridge","brief","bright","bring",
|
|
||||||
"broad","broke","broken","brother","brought","brown","brush","buffalo",
|
|
||||||
"build","building","built","buried","burn","burst","bus","bush",
|
|
||||||
"business","busy","but","butter","buy","by","cabin","cage",
|
|
||||||
"cake","call","calm","came","camera","camp","can","canal",
|
|
||||||
"cannot","cap","capital","captain","captured","car","carbon","card",
|
|
||||||
"care","careful","carefully","carried","carry","case","cast","castle",
|
|
||||||
"cat","catch","cattle","caught","cause","cave","cell","cent",
|
|
||||||
"center","central","century","certain","certainly","chain","chair","chamber",
|
|
||||||
"chance","change","changing","chapter","character","characteristic","charge","chart",
|
|
||||||
"check","cheese","chemical","chest","chicken","chief","child","children",
|
|
||||||
"choice","choose","chose","chosen","church","circle","circus","citizen",
|
|
||||||
"city","class","classroom","claws","clay","clean","clear","clearly",
|
|
||||||
"climate","climb","clock","close","closely","closer","cloth","clothes",
|
|
||||||
"clothing","cloud","club","coach","coal","coast","coat","coffee",
|
|
||||||
"cold","collect","college","colony","color","column","combination","combine",
|
|
||||||
"come","comfortable","coming","command","common","community","company","compare",
|
|
||||||
"compass","complete","completely","complex","composed","composition","compound","concerned",
|
|
||||||
"condition","congress","connected","consider","consist","consonant","constantly","construction",
|
|
||||||
"contain","continent","continued","contrast","control","conversation","cook","cookies",
|
|
||||||
"cool","copper","copy","corn","corner","correct","correctly","cost",
|
|
||||||
"cotton","could","count","country","couple","courage","course","court",
|
|
||||||
"cover","cow","cowboy","crack","cream","create","creature","crew",
|
|
||||||
"crop","cross","crowd","cry","cup","curious","current","curve",
|
|
||||||
"customs","cut","cutting","daily","damage","dance","danger","dangerous",
|
|
||||||
"dark","darkness","date","daughter","dawn","day","dead","deal",
|
|
||||||
"dear","death","decide","declared","deep","deeply","deer","definition",
|
|
||||||
"degree","depend","depth","describe","desert","design","desk","detail",
|
|
||||||
"determine","develop","development","diagram","diameter","did","die","differ",
|
|
||||||
"difference","different","difficult","difficulty","dig","dinner","direct","direction",
|
|
||||||
"directly","dirt","dirty","disappear","discover","discovery","discuss","discussion",
|
|
||||||
"disease","dish","distance","distant","divide","division","do","doctor",
|
|
||||||
"does","dog","doing","doll","dollar","done","donkey","door",
|
|
||||||
"dot","double","doubt","down","dozen","draw","drawn","dream",
|
|
||||||
"dress","drew","dried","drink","drive","driven","driver","driving",
|
|
||||||
"drop","dropped","drove","dry","duck","due","dug","dull",
|
|
||||||
"during","dust","duty","each","eager","ear","earlier","early",
|
|
||||||
"earn","earth","easier","easily","east","easy","eat","eaten",
|
|
||||||
"edge","education","effect","effort","egg","eight","either","electric",
|
|
||||||
"electricity","element","elephant","eleven","else","empty","end","enemy",
|
|
||||||
"energy","engine","engineer","enjoy","enough","enter","entire","entirely",
|
|
||||||
"environment","equal","equally","equator","equipment","escape","especially","essential",
|
|
||||||
"establish","even","evening","event","eventually","ever","every","everybody",
|
|
||||||
"everyone","everything","everywhere","evidence","exact","exactly","examine","example",
|
|
||||||
"excellent","except","exchange","excited","excitement","exciting","exclaimed","exercise",
|
|
||||||
"exist","expect","experience","experiment","explain","explanation","explore","express",
|
|
||||||
"expression","extra","eye","face","facing","fact","factor","factory",
|
|
||||||
"failed","fair","fairly","fall","fallen","familiar","family","famous",
|
|
||||||
"far","farm","farmer","farther","fast","fastened","faster","fat",
|
|
||||||
"father","favorite","fear","feathers","feature","fed","feed","feel",
|
|
||||||
"feet","fell","fellow","felt","fence","few","fewer","field",
|
|
||||||
"fierce","fifteen","fifth","fifty","fight","fighting","figure","fill",
|
|
||||||
"film","final","finally","find","fine","finest","finger","finish",
|
|
||||||
"fire","fireplace","firm","first","fish","five","fix","flag",
|
|
||||||
"flame","flat","flew","flies","flight","floating","floor","flow",
|
|
||||||
"flower","fly","fog","folks","follow","food","foot","football",
|
|
||||||
"for","force","foreign","forest","forget","forgot","forgotten","form",
|
|
||||||
"former","fort","forth","forty","forward","fought","found","four",
|
|
||||||
"fourth","fox","frame","free","freedom","frequently","fresh","friend",
|
|
||||||
"friendly","frighten","frog","from","front","frozen","fruit","fuel",
|
|
||||||
"full","fully","fun","function","funny","fur","furniture","further",
|
|
||||||
"future","gain","game","garage","garden","gas","gasoline","gate",
|
|
||||||
"gather","gave","general","generally","gentle","gently","get","getting",
|
|
||||||
"giant","gift","girl","give","given","giving","glad","glass",
|
|
||||||
"globe","go","goes","gold","golden","gone","good","goose",
|
|
||||||
"got","government","grabbed","grade","gradually","grain","grandfather","grandmother",
|
|
||||||
"graph","grass","gravity","gray","great","greater","greatest","greatly",
|
|
||||||
"green","grew","ground","group","grow","grown","growth","guard",
|
|
||||||
"guess","guide","gulf","gun","habit","had","hair","half",
|
|
||||||
"halfway","hall","hand","handle","handsome","hang","happen","happened",
|
|
||||||
"happily","happy","harbor","hard","harder","hardly","has","hat",
|
|
||||||
"have","having","hay","he","headed","heading","health","heard",
|
|
||||||
"hearing","heart","heat","heavy","height","held","hello","help",
|
|
||||||
"helpful","her","herd","here","herself","hidden","hide","high",
|
|
||||||
"higher","highest","highway","hill","him","himself","his","history",
|
|
||||||
"hit","hold","hole","hollow","home","honor","hope","horn",
|
|
||||||
"horse","hospital","hot","hour","house","how","however","huge",
|
|
||||||
"human","hundred","hung","hungry","hunt","hunter","hurried","hurry",
|
|
||||||
"hurt","husband","ice","idea","identity","if","ill","image",
|
|
||||||
"imagine","immediately","importance","important","impossible","improve","in","inch",
|
|
||||||
"include","including","income","increase","indeed","independent","indicate","individual",
|
|
||||||
"industrial","industry","influence","information","inside","instance","instant","instead",
|
|
||||||
"instrument","interest","interior","into","introduced","invented","involved","iron",
|
|
||||||
"is","island","it","its","itself","jack","jar","jet",
|
|
||||||
"job","join","joined","journey","joy","judge","jump","jungle",
|
|
||||||
"just","keep","kept","key","kids","kill","kind","kitchen",
|
|
||||||
"knew","knife","know","knowledge","known","label","labor","lack",
|
|
||||||
"lady","laid","lake","lamp","land","language","large","larger",
|
|
||||||
"largest","last","late","later","laugh","law","lay","layers",
|
|
||||||
"lead","leader","leaf","learn","least","leather","leave","leaving",
|
|
||||||
"led","left","leg","length","lesson","let","letter","level",
|
|
||||||
"library","lie","life","lift","light","like","likely","limited",
|
|
||||||
"line","lion","lips","liquid","list","listen","little","live",
|
|
||||||
"living","load","local","locate","location","log","lonely","long",
|
|
||||||
"longer","look","loose","lose","loss","lost","lot","loud",
|
|
||||||
"love","lovely","low","lower","luck","lucky","lunch","lungs",
|
|
||||||
"lying","machine","machinery","mad","made","magic","magnet","mail",
|
|
||||||
"main","mainly","major","make","making","man","managed","manner",
|
|
||||||
"manufacturing","many","map","mark","market","married","mass","massage",
|
|
||||||
"master","material","mathematics","matter","may","maybe","me","meal",
|
|
||||||
"mean","means","meant","measure","meat","medicine","meet","melted",
|
|
||||||
"member","memory","men","mental","merely","met","metal","method",
|
|
||||||
"mice","middle","might","mighty","mile","military","milk","mill",
|
|
||||||
"mind","mine","minerals","minute","mirror","missing","mission","mistake",
|
|
||||||
"mix","mixture","model","modern","molecular","moment","money","monkey",
|
|
||||||
"month","mood","moon","more","morning","most","mostly","mother",
|
|
||||||
"motion","motor","mountain","mouse","mouth","move","movement","movie",
|
|
||||||
"moving","mud","muscle","music","musical","must","my","myself",
|
|
||||||
"mysterious","nails","name","nation","national","native","natural","naturally",
|
|
||||||
"nature","near","nearby","nearer","nearest","nearly","necessary","neck",
|
|
||||||
"needed","needle","needs","negative","neighbor","neighborhood","nervous","nest",
|
|
||||||
"never","new","news","newspaper","next","nice","night","nine",
|
|
||||||
"no","nobody","nodded","noise","none","noon","nor","north",
|
|
||||||
"nose","not","note","noted","nothing","notice","noun","now",
|
|
||||||
"number","numeral","nuts","object","observe","obtain","occasionally","occur",
|
|
||||||
"ocean","of","off","offer","office","officer","official","oil",
|
|
||||||
"old","older","oldest","on","once","one","only","onto",
|
|
||||||
"open","operation","opinion","opportunity","opposite","or","orange","orbit",
|
|
||||||
"order","ordinary","organization","organized","origin","original","other","ought",
|
|
||||||
"our","ourselves","out","outer","outline","outside","over","own",
|
|
||||||
"owner","oxygen","pack","package","page","paid","pain","paint",
|
|
||||||
"pair","palace","pale","pan","paper","paragraph","parallel","parent",
|
|
||||||
"park","part","particles","particular","particularly","partly","parts","party",
|
|
||||||
"pass","passage","past","path","pattern","pay","peace","pen",
|
|
||||||
"pencil","people","per","percent","perfect","perfectly","perhaps","period",
|
|
||||||
"person","personal","pet","phrase","physical","piano","pick","picture",
|
|
||||||
"pictured","pie","piece","pig","pile","pilot","pine","pink",
|
|
||||||
"pipe","pitch","place","plain","plan","plane","planet","planned",
|
|
||||||
"planning","plant","plastic","plate","plates","play","pleasant","please",
|
|
||||||
"pleasure","plenty","plural","plus","pocket","poem","poet","poetry",
|
|
||||||
"point","pole","police","policeman","political","pond","pony","pool",
|
|
||||||
"poor","popular","population","porch","port","position","positive","possible",
|
|
||||||
"possibly","post","pot","potatoes","pound","pour","powder","power",
|
|
||||||
"powerful","practical","practice","prepare","present","president","press","pressure",
|
|
||||||
"pretty","prevent","previous","price","pride","primitive","principal","principle",
|
|
||||||
"printed","private","prize","probably","problem","process","produce","product",
|
|
||||||
"production","program","progress","promised","proper","properly","property","protection",
|
|
||||||
"proud","prove","provide","public","pull","pupil","pure","purple",
|
|
||||||
"purpose","push","put","putting","quarter","queen","question","quick",
|
|
||||||
"quickly","quiet","quietly","quite","rabbit","race","radio","railroad",
|
|
||||||
"rain","raise","ran","ranch","range","rapidly","rate","rather",
|
|
||||||
"raw","rays","reach","read","reader","ready","real","realize",
|
|
||||||
"rear","reason","recall","receive","recent","recently","recognize","record",
|
|
||||||
"red","refer","refused","region","regular","related","relationship","religious",
|
|
||||||
"remain","remarkable","remember","remove","repeat","replace","replied","report",
|
|
||||||
"represent","require","research","respect","rest","result","return","review",
|
|
||||||
"rhyme","rhythm","rice","rich","ride","riding","right","ring",
|
|
||||||
"rise","rising","river","road","roar","rock","rocket","rocky",
|
|
||||||
"rod","roll","roof","room","root","rope","rose","rough",
|
|
||||||
"round","route","row","rubbed","rubber","rule","ruler","run",
|
|
||||||
"running","rush","sad","saddle","safe","safety","said","sail",
|
|
||||||
"sale","salmon","salt","same","sand","sang","sat","satellites",
|
|
||||||
"satisfied","save","saved","saw","say","scale","scared","scene",
|
|
||||||
"school","science","scientific","scientist","score","screen","sea","search",
|
|
||||||
"season","seat","second","secret","section","see","seed","seeing",
|
|
||||||
"seems","seen","seldom","select","selection","sell","send","sense",
|
|
||||||
"sent","sentence","separate","series","serious","serve","service","sets",
|
|
||||||
"setting","settle","settlers","seven","several","shade","shadow","shake",
|
|
||||||
"shaking","shall","shallow","shape","share","sharp","she","sheep",
|
|
||||||
"sheet","shelf","shells","shelter","shine","shinning","ship","shirt",
|
|
||||||
"shoe","shoot","shop","shore","short","shorter","shot","should",
|
|
||||||
"shoulder","shout","show","shown","shut","sick","sides","sight",
|
|
||||||
"sign","signal","silence","silent","silk","silly","silver","similar",
|
|
||||||
"simple","simplest","simply","since","sing","single","sink","sister",
|
|
||||||
"sit","sitting","situation","six","size","skill","skin","sky",
|
|
||||||
"slabs","slave","sleep","slept","slide","slight","slightly","slip",
|
|
||||||
"slipped","slope","slow","slowly","small","smaller","smallest","smell",
|
|
||||||
"smile","smoke","smooth","snake","snow","so","soap","social",
|
|
||||||
"society","soft","softly","soil","solar","sold","soldier","solid",
|
|
||||||
"solution","solve","some","somebody","somehow","someone","something","sometime",
|
|
||||||
"somewhere","son","song","soon","sort","sound","source","south",
|
|
||||||
"southern","space","speak","special","species","specific","speech","speed",
|
|
||||||
"spell","spend","spent","spider","spin","spirit","spite","split",
|
|
||||||
"spoken","sport","spread","spring","square","stage","stairs","stand",
|
|
||||||
"standard","star","stared","start","state","statement","station","stay",
|
|
||||||
"steady","steam","steel","steep","stems","step","stepped","stick",
|
|
||||||
"stiff","still","stock","stomach","stone","stood","stop","stopped",
|
|
||||||
"store","storm","story","stove","straight","strange","stranger","straw",
|
|
||||||
"stream","street","strength","stretch","strike","string","strip","strong",
|
|
||||||
"stronger","struck","structure","struggle","stuck","student","studied","studying",
|
|
||||||
"subject","substance","success","successful","such","sudden","suddenly","sugar",
|
|
||||||
"suggest","suit","sum","summer","sun","sunlight","supper","supply",
|
|
||||||
"support","suppose","sure","surface","surprise","surrounded","swam","sweet",
|
|
||||||
"swept","swim","swimming","swing","swung","syllable","symbol","system",
|
|
||||||
"table","tail","take","taken","tales","talk","tall","tank",
|
|
||||||
"tape","task","taste","taught","tax","tea","teach","teacher",
|
|
||||||
"team","tears","teeth","telephone","television","tell","temperature","ten",
|
|
||||||
"tent","term","terrible","test","than","thank","that","thee",
|
|
||||||
"them","themselves","then","theory","there","therefore","these","they",
|
|
||||||
"thick","thin","thing","think","third","thirty","this","those",
|
|
||||||
"thou","though","thought","thousand","thread","three","threw","throat",
|
|
||||||
"through","throughout","throw","thrown","thumb","thus","thy","tide",
|
|
||||||
"tie","tight","tightly","till","time","tin","tiny","tip",
|
|
||||||
"tired","title","to","tobacco","today","together","told","tomorrow",
|
|
||||||
"tone","tongue","tonight","too","took","tool","top","topic",
|
|
||||||
"torn","total","touch","toward","tower","town","toy","trace",
|
|
||||||
"track","trade","traffic","trail","train","transportation","trap","travel",
|
|
||||||
"treated","tree","triangle","tribe","trick","tried","trip","troops",
|
|
||||||
"tropical","trouble","truck","trunk","truth","try","tube","tune",
|
|
||||||
"turn","twelve","twenty","twice","two","type","typical","uncle",
|
|
||||||
"under","underline","understanding","unhappy","union","unit","universe","unknown",
|
|
||||||
"unless","until","unusual","up","upon","upper","upward","us",
|
|
||||||
"use","useful","using","usual","usually","valley","valuable","value",
|
|
||||||
"vapor","variety","various","vast","vegetable","verb","vertical","very",
|
|
||||||
"vessels","victory","view","village","visit","visitor","voice","volume",
|
|
||||||
"vote","vowel","voyage","wagon","wait","walk","wall","want",
|
|
||||||
"war","warm","warn","was","wash","waste","watch","water",
|
|
||||||
"wave","way","we","weak","wealth","wear","weather","week",
|
|
||||||
"weigh","weight","welcome","well","went","were","west","western",
|
|
||||||
"wet","whale","what","whatever","wheat","wheel","when","whenever",
|
|
||||||
"where","wherever","whether","which","while","whispered","whistle","white",
|
|
||||||
"who","whole","whom","whose","why","wide","widely","wife",
|
|
||||||
"wild","will","willing","win","wind","window","wing","winter",
|
|
||||||
"wire","wise","wish","with","within","without","wolf","women",
|
|
||||||
"won","wonder","wonderful","wood","wooden","wool","word","wore",
|
|
||||||
"work","worker","world","worried","worry","worse","worth","would",
|
|
||||||
"wrapped","write","writer","writing","written","wrong","wrote","yard",
|
|
||||||
"year","yellow","yes","yesterday","yet","you","young","younger",
|
|
||||||
"your","yourself","youth","zero","zebra","zipper","zoo","zulu"
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function words(
|
|
||||||
options: any
|
|
||||||
) {
|
|
||||||
|
|
||||||
function word() {
|
|
||||||
if (options && options.maxLength > 1) {
|
|
||||||
return generateWordWithMaxLength();
|
|
||||||
} else {
|
|
||||||
return generateRandomWord();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateWordWithMaxLength() {
|
|
||||||
var rightSize = false;
|
|
||||||
var wordUsed;
|
|
||||||
while (!rightSize) {
|
|
||||||
wordUsed = generateRandomWord();
|
|
||||||
if(wordUsed.length <= (options.maxLength)) {
|
|
||||||
rightSize = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return wordUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateRandomWord() {
|
|
||||||
return wordList[randInt(wordList.length)];
|
|
||||||
}
|
|
||||||
|
|
||||||
function randInt(lessThan: number) {
|
|
||||||
return Math.floor(Math.random() * lessThan);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No arguments = generate one word
|
|
||||||
if (typeof(options) === 'undefined') {
|
|
||||||
return word();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just a number = return that many words
|
|
||||||
if (typeof(options) === 'number') {
|
|
||||||
options = { exactly: options };
|
|
||||||
}
|
|
||||||
|
|
||||||
// options supported: exactly, min, max, join
|
|
||||||
if (options.exactly) {
|
|
||||||
options.min = options.exactly;
|
|
||||||
options.max = options.exactly;
|
|
||||||
}
|
|
||||||
|
|
||||||
// not a number = one word par string
|
|
||||||
if (typeof(options.wordsPerString) !== 'number') {
|
|
||||||
options.wordsPerString = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//not a function = returns the raw word
|
|
||||||
if (typeof(options.formatter) !== 'function') {
|
|
||||||
options.formatter = (word: string) => word;
|
|
||||||
}
|
|
||||||
|
|
||||||
//not a string = separator is a space
|
|
||||||
if (typeof(options.separator) !== 'string') {
|
|
||||||
options.separator = ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
var total = options.min + randInt(options.max + 1 - options.min);
|
|
||||||
var results: any = [];
|
|
||||||
var token = '';
|
|
||||||
var relativeIndex = 0;
|
|
||||||
|
|
||||||
for (var i = 0; (i < total * options.wordsPerString); i++) {
|
|
||||||
if (relativeIndex === options.wordsPerString - 1) {
|
|
||||||
token += options.formatter(word(), relativeIndex);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
token += options.formatter(word(), relativeIndex) + options.separator;
|
|
||||||
}
|
|
||||||
relativeIndex++;
|
|
||||||
if ((i + 1) % options.wordsPerString === 0) {
|
|
||||||
results.push(token);
|
|
||||||
token = '';
|
|
||||||
relativeIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
if (typeof options.join === 'string') {
|
|
||||||
results = results.join(options.join);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
import { Config, defaultConfig } from "@vlcn.io/ws-client";
|
|
||||||
import { start } from "@vlcn.io/ws-client/worker.js";
|
|
||||||
import { createDbProvider } from "@vlcn.io/ws-browserdb";
|
|
||||||
|
|
||||||
export const config: Config = {
|
|
||||||
dbProvider: createDbProvider(),
|
|
||||||
transportProvider: defaultConfig.transportProvider,
|
|
||||||
};
|
|
||||||
|
|
||||||
start(config);
|
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ESNext",
|
|
||||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
||||||
"module": "ESNext",
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"allowJs": true,
|
|
||||||
|
|
||||||
/* Bundler mode */
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowImportingTsExtensions": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"noEmit": true,
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
|
|
||||||
/* Linting */
|
|
||||||
"strict": true,
|
|
||||||
"noUnusedLocals": true,
|
|
||||||
"noUnusedParameters": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": ["src"],
|
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"composite": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "bundler",
|
|
||||||
"allowSyntheticDefaultImports": true
|
|
||||||
},
|
|
||||||
"include": ["vite.config.ts"]
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { defineConfig } from "vite";
|
|
||||||
import react from "@vitejs/plugin-react";
|
|
||||||
import { VitePWA } from "vite-plugin-pwa";
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
build: {
|
|
||||||
target: "esnext",
|
|
||||||
},
|
|
||||||
optimizeDeps: {
|
|
||||||
exclude: ["@vite/client", "@vite/env", "@vlcn.io/crsqlite-wasm"],
|
|
||||||
esbuildOptions: {
|
|
||||||
target: "esnext",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
react(),
|
|
||||||
// VitePWA({
|
|
||||||
// workbox: {
|
|
||||||
// globPatterns: [
|
|
||||||
// "**/*.js",
|
|
||||||
// "**/*.css",
|
|
||||||
// "**/*.svg",
|
|
||||||
// "**/*.html",
|
|
||||||
// "**/*.png",
|
|
||||||
// "**/*.wasm",
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// }),
|
|
||||||
],
|
|
||||||
server: {
|
|
||||||
fs: {
|
|
||||||
strict: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
Reference in a new issue