🦄 Refactored app to src main
This commit is contained in:
parent
59c04276ac
commit
cf67d463b3
9
.gitignore
vendored
9
.gitignore
vendored
@ -5,10 +5,17 @@
|
||||
app/node_modules
|
||||
app-next/node_modules
|
||||
api/node_modules
|
||||
/node_modules
|
||||
node_modules
|
||||
|
||||
localhost-key.pem
|
||||
localhost.pem
|
||||
|
||||
yarn-error.log
|
||||
pnpm-lock.yaml
|
||||
|
||||
build/*
|
||||
public/build/*
|
||||
api/index.js
|
||||
api/index.js.map
|
||||
|
||||
pnpm-lock.yaml
|
12
.prettierrc
Normal file
12
.prettierrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 150,
|
||||
"singleAttributePerLine": false,
|
||||
"arrowParens": "always",
|
||||
"plugins": []
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: "module",
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
"@typescript-eslint",
|
||||
"react",
|
||||
// "unused-imports",
|
||||
"prettier",
|
||||
"simple-import-sort",
|
||||
"chakra-ui",
|
||||
],
|
||||
extends: [
|
||||
"@remix-run/eslint-config",
|
||||
"eslint:recommended",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
],
|
||||
rules: {
|
||||
"react/jsx-curly-brace-presence": [
|
||||
"error",
|
||||
{ props: "never", children: "never" },
|
||||
],
|
||||
"direct-eval/direct-eval": "off",
|
||||
"no-eval": "off",
|
||||
"no-async-promise-executor": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/display-name": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
// "unused-imports/no-unused-imports": "error",
|
||||
// "unused-imports/no-unused-vars": "error",
|
||||
"react/react-in-jsx-scope": "off",
|
||||
"chakra-ui/props-order": "error",
|
||||
"chakra-ui/props-shorthand": [
|
||||
"error",
|
||||
{
|
||||
noShorthand: true,
|
||||
applyToAllComponents: true,
|
||||
},
|
||||
],
|
||||
"chakra-ui/require-specific-component": "error",
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
semi: false,
|
||||
singleQuote: false,
|
||||
parser: "typescript",
|
||||
bracketSpacing: true,
|
||||
},
|
||||
],
|
||||
"simple-import-sort/imports": [
|
||||
"error",
|
||||
{
|
||||
groups: [
|
||||
// Packages `react` related packages come first.
|
||||
["^react", "^@?\\w"],
|
||||
// Internal packages.
|
||||
["^(@root)(/.*|$)"],
|
||||
["^(@app)(/.*|$)"],
|
||||
// Side effect imports.
|
||||
["^\\u0000"],
|
||||
// Other relative imports. Put same-folder imports and `.` last.
|
||||
["^\\./(?=.*/)(?!/?$)", "^\\.(?!/?$)", "^\\./?$"],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.js"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
10
app/.gitignore
vendored
10
app/.gitignore
vendored
@ -5,7 +5,9 @@ node_modules
|
||||
.vercel
|
||||
.output
|
||||
|
||||
/build/
|
||||
/public/build
|
||||
/api/index.js
|
||||
/api/index.js.map
|
||||
build/*
|
||||
public/build/*
|
||||
api/index.js
|
||||
api/index.js.map
|
||||
|
||||
pnpm-lock.yaml
|
12
app/.prettierrc
Normal file
12
app/.prettierrc
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 150,
|
||||
"singleAttributePerLine": false,
|
||||
"arrowParens": "always",
|
||||
"plugins": []
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
import { ChakraProvider } from '@chakra-ui/react'
|
||||
import { theme } from './theme'
|
||||
export const ChakraWrapper = props => {
|
||||
return <ChakraProvider theme={theme}>{props.children}</ChakraProvider>
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Flex } from '@chakra-ui/react'
|
||||
|
||||
export const Attention = props => {
|
||||
return (
|
||||
<Flex
|
||||
{...props}
|
||||
flexDirection={'column'}
|
||||
justifyContent={'center'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
>
|
||||
<Flex
|
||||
w={props.w || '40px'}
|
||||
mb='10px'
|
||||
h='2px'
|
||||
bg='linear-gradient(to right, #f34a4a, #ffbc48, #58ca70, #47b5e6, #a555e8, #f34a4a);'
|
||||
backgroundSize='200%'
|
||||
borderBottomRadius={'20px'}
|
||||
transition='all 0.5s ease-in-out'
|
||||
sx={{
|
||||
'@keyframes moving-rainbow': {
|
||||
'0%': { backgroundPosition: '0 0' },
|
||||
'100%': { backgroundPosition: '200% 0' }
|
||||
},
|
||||
// add delay
|
||||
animation: `moving-rainbow 3s infinite linear`
|
||||
}}
|
||||
></Flex>
|
||||
</Flex>
|
||||
)
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Flex } from '@chakra-ui/react'
|
||||
|
||||
export const Hamburger = props => {
|
||||
const lineCount = [1, 2, 3]
|
||||
|
||||
return (
|
||||
<Flex
|
||||
{...props}
|
||||
flexDirection={'column'}
|
||||
justifyContent={'center'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
>
|
||||
{lineCount.map((line, idx) => {
|
||||
return (
|
||||
<Flex
|
||||
key={idx}
|
||||
w='40px'
|
||||
mb='10px'
|
||||
h='3px'
|
||||
bg='linear-gradient(to right, #f34a4a, #ffbc48, #58ca70, #47b5e6, #a555e8, #f34a4a);'
|
||||
backgroundSize='200%'
|
||||
borderRadius={'9px'}
|
||||
sx={{
|
||||
'@keyframes moving-rainbow': {
|
||||
'0%': { backgroundPosition: '0 0' },
|
||||
'100%': { backgroundPosition: '200% 0' }
|
||||
},
|
||||
// add delay
|
||||
animation: `moving-rainbow 3s infinite linear -${idx * 0.3}s}`
|
||||
}}
|
||||
></Flex>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
)
|
||||
}
|
@ -1,225 +0,0 @@
|
||||
import React from "react"
|
||||
import { Center } from "@chakra-ui/react"
|
||||
import emojis from "emojis-list"
|
||||
|
||||
export const Icon = (props) => {
|
||||
const name = props?.name
|
||||
|
||||
const icon = React.useMemo(() => {
|
||||
// nothing
|
||||
if (["gear", "cog"]?.includes(name)) {
|
||||
return "⚙️"
|
||||
}
|
||||
if (["crystal"]?.includes(name)) {
|
||||
return "🔮"
|
||||
}
|
||||
if (["flower", "hibiscus"]?.includes(name)) {
|
||||
return "🌺"
|
||||
}
|
||||
if (["sparke", "magic"]?.includes(name)) {
|
||||
return "✨"
|
||||
}
|
||||
if (["wizard", "gandalf"]?.includes(name)) {
|
||||
return "🧙♂️"
|
||||
}
|
||||
if (["two eyes"]?.includes(name)) {
|
||||
return "👀"
|
||||
}
|
||||
if (["box", "thing", "object"]?.includes(name)) {
|
||||
return "📦"
|
||||
}
|
||||
if (["pencil"]?.includes(name)) {
|
||||
return "✏️"
|
||||
}
|
||||
if (["edit", "paint", "create"]?.includes(name)) {
|
||||
return "🎨"
|
||||
}
|
||||
if (["book", "books"]?.includes(name)) {
|
||||
return "📚"
|
||||
}
|
||||
if (["any", "magic wand"]?.includes(name)) {
|
||||
return "🪄"
|
||||
}
|
||||
if (["book-open", "books-open"]?.includes(name)) {
|
||||
return "📖"
|
||||
}
|
||||
if (["book-reader", "books-reader"]?.includes(name)) {
|
||||
return "👩🏫"
|
||||
}
|
||||
if (["number", "hundred"]?.includes(name)) {
|
||||
return "💯"
|
||||
}
|
||||
if (["puzzle", "types"]?.includes(name)) {
|
||||
return "🧩"
|
||||
}
|
||||
if (["heart"]?.includes(name)) {
|
||||
return "❤️"
|
||||
}
|
||||
if (["heart-broken"]?.includes(name)) {
|
||||
return "💔"
|
||||
}
|
||||
if (["heart-pulse"]?.includes(name)) {
|
||||
return "💗"
|
||||
}
|
||||
if (["string", "text"]?.includes(name)) {
|
||||
return "💬"
|
||||
}
|
||||
if (["array", "list"]?.includes(name)) {
|
||||
return "📚"
|
||||
}
|
||||
if (["boolean", "bool"]?.includes(name)) {
|
||||
return "🌗"
|
||||
// return "⚖️"
|
||||
}
|
||||
if (["rainbow"]?.includes(name)) {
|
||||
return "🌈"
|
||||
}
|
||||
if (["sun"]?.includes(name)) {
|
||||
return "☀️"
|
||||
}
|
||||
if (["moon"]?.includes(name)) {
|
||||
return "🌙"
|
||||
}
|
||||
if (["unicorn"]?.includes(name)) {
|
||||
return "🦄"
|
||||
}
|
||||
if (["user", "person"]?.includes(name)) {
|
||||
return "👤"
|
||||
}
|
||||
if (["group", "team"]?.includes(name)) {
|
||||
return "👥"
|
||||
}
|
||||
if (["success", "check"]?.includes(name)) {
|
||||
return "✅"
|
||||
}
|
||||
if (["error", "stop"]?.includes(name)) {
|
||||
return "❌"
|
||||
}
|
||||
if (["warning", "alert"]?.includes(name)) {
|
||||
return "⚠️"
|
||||
}
|
||||
if (["time", "clock"]?.includes(name)) {
|
||||
return "⏰"
|
||||
}
|
||||
if (["star", "favorite"]?.includes(name)) {
|
||||
return "⭐"
|
||||
}
|
||||
if (["glowing star", "glowing favorite"]?.includes(name)) {
|
||||
return "🌟"
|
||||
}
|
||||
if (["question", "help"]?.includes(name)) {
|
||||
return "❓"
|
||||
}
|
||||
if (["video", "media"]?.includes(name)) {
|
||||
return "🎥"
|
||||
}
|
||||
if (["music", "audio"]?.includes(name)) {
|
||||
return "🎵"
|
||||
}
|
||||
if (["image", "picture"]?.includes(name)) {
|
||||
return "🖼️"
|
||||
}
|
||||
if (["email", "mail"]?.includes(name)) {
|
||||
return "✉️"
|
||||
}
|
||||
if (["computer", "laptop"]?.includes(name)) {
|
||||
return "💻"
|
||||
}
|
||||
if (["mobile", "phone"]?.includes(name)) {
|
||||
return "📱"
|
||||
}
|
||||
if (["world", "globe"]?.includes(name)) {
|
||||
return "🌍"
|
||||
}
|
||||
if (["rocket", "launch"]?.includes(name)) {
|
||||
return "🚀"
|
||||
}
|
||||
if (["pencil", "edit"]?.includes(name)) {
|
||||
return "✏️"
|
||||
}
|
||||
if (["search", "magnify"]?.includes(name)) {
|
||||
return "🔍"
|
||||
}
|
||||
if (["lock", "secure"]?.includes(name)) {
|
||||
return "🔒"
|
||||
}
|
||||
if (["unlock", "access"]?.includes(name)) {
|
||||
return "🔓"
|
||||
}
|
||||
if (["thumb-up", "like"]?.includes(name)) {
|
||||
return "👍"
|
||||
}
|
||||
if (["thumb-down", "dislike"]?.includes(name)) {
|
||||
return "👎"
|
||||
}
|
||||
if (["plus", "add"]?.includes(name)) {
|
||||
return "➕"
|
||||
}
|
||||
if (["seedling", "seed"]?.includes(name)) {
|
||||
return "🌱"
|
||||
}
|
||||
if (["undefined", "null", "question", "confused"]?.includes(name)) {
|
||||
return "❓"
|
||||
// return "🌫️"
|
||||
}
|
||||
if (["codex", "robot", "ai", "chatgpt"]?.includes(name)) {
|
||||
return "🤖"
|
||||
}
|
||||
if (["trash", "bin", "delete", "remove"]?.includes(name)) {
|
||||
return "🗑️"
|
||||
}
|
||||
if (["cash", "money"]?.includes(name)) {
|
||||
// return "💰"
|
||||
return "💵"
|
||||
}
|
||||
if (["money bag"]?.includes(name)) {
|
||||
return "💰"
|
||||
}
|
||||
if (["cyclone", "tornado"]?.includes(name)) {
|
||||
return "🌀"
|
||||
}
|
||||
if (["thingtime"]?.includes(name)) {
|
||||
if (Math.random() > 0.5) {
|
||||
return "🌳"
|
||||
}
|
||||
return "🌀"
|
||||
}
|
||||
if (["function", "lambda"]?.includes(name)) {
|
||||
return "📐"
|
||||
}
|
||||
if (["pin", "pinned", "located"]?.includes(name)) {
|
||||
return "📌"
|
||||
}
|
||||
|
||||
if (["wrap", "wrapped"]?.includes(name)) {
|
||||
return "🎁"
|
||||
// return "🎀"
|
||||
}
|
||||
|
||||
if (["dinosaur", "dino"]?.includes(name)) {
|
||||
return "🦕"
|
||||
}
|
||||
|
||||
if (emojis?.includes(name)) {
|
||||
return name
|
||||
}
|
||||
|
||||
if (["random"]?.includes(name)) {
|
||||
return emojis[Math.floor(Math.random() * emojis.length)]
|
||||
}
|
||||
|
||||
// question mark
|
||||
return "❓"
|
||||
}, [name])
|
||||
|
||||
return (
|
||||
<Center
|
||||
transition="all 0.2s ease-out"
|
||||
{...props}
|
||||
{...props?.chakras}
|
||||
fontSize={props?.size}
|
||||
>
|
||||
{icon}
|
||||
</Center>
|
||||
)
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
import React, { useState } from "react"
|
||||
import { Flex, FormControl, Input } from "@chakra-ui/react"
|
||||
|
||||
export const Login = (props) => {
|
||||
const [email, setEmail] = useState("")
|
||||
const [password, setPassword] = useState("")
|
||||
|
||||
const handleLogin = () => {
|
||||
// handle login
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex flexDirection="column" gap={4} width="auto">
|
||||
<FormControl>
|
||||
<Input
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
value={email}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<Flex
|
||||
width="100%"
|
||||
color="white"
|
||||
background="chakras.violet"
|
||||
borderRadius={6}
|
||||
cursor="pointer"
|
||||
paddingX={5}
|
||||
paddingY={2}
|
||||
>
|
||||
Login
|
||||
</Flex>
|
||||
</Flex>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Flex } from '@chakra-ui/react'
|
||||
import { Hamburger } from '../Buttons/Hamburger'
|
||||
import { Attention } from '../Buttons/Attention'
|
||||
import { RightNav } from './ReactiveRightNav'
|
||||
|
||||
export const ReactiveNav = props => {
|
||||
const [navItems] = React.useState([{}])
|
||||
|
||||
const setNav = React.useCallback(active => {
|
||||
setMt(active ? '100%' : '0%')
|
||||
setNavActive(active)
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
// react to mouse move event
|
||||
const listener = event => {
|
||||
if (event?.clientY <= 20) {
|
||||
setNav(true)
|
||||
} else if (event?.clientY >= 100) {
|
||||
setNav(false)
|
||||
}
|
||||
}
|
||||
window.addEventListener('mousemove', listener)
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', listener)
|
||||
}
|
||||
}, [setNav])
|
||||
|
||||
const [navActive, setNavActive] = React.useState(false)
|
||||
|
||||
const [mt, setMt] = React.useState('0%')
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
as='nav'
|
||||
w='100%'
|
||||
alignItems={'center'}
|
||||
justifyContent='center'
|
||||
flexDir={'row'}
|
||||
position='fixed'
|
||||
py={'1%'}
|
||||
px={'1%'}
|
||||
bottom={'100%'}
|
||||
transform={`translateY(${mt})`}
|
||||
transition={'all 0.3s ease-in-out'}
|
||||
// top={'100%'}
|
||||
// top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
>
|
||||
<Hamburger ml='auto'></Hamburger>
|
||||
<Flex
|
||||
opacity={navActive ? 0 : 1}
|
||||
position='absolute'
|
||||
transition={'all 0.2s ease-in-out'}
|
||||
top='100%'
|
||||
left={'50%'}
|
||||
translateX={'-50%'}
|
||||
>
|
||||
{/* w={navActive ? '0px' : '40px'} */}
|
||||
<Attention></Attention>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<RightNav></RightNav>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
import React from 'react'
|
||||
import { Flex } from '@chakra-ui/react'
|
||||
|
||||
export const RainbowSkeleton = props => {
|
||||
const [rainbowColours] = React.useState([
|
||||
'#f34a4a',
|
||||
'#ffbc48',
|
||||
'#58ca70',
|
||||
'#47b5e6',
|
||||
'#a555e8'
|
||||
])
|
||||
|
||||
const keyframes = React.useMemo(() => {
|
||||
let keyframes = {}
|
||||
rainbowColours.forEach((colour, idx) => {
|
||||
keyframes[Math.round((idx * 100) / rainbowColours.length) + '%'] = {
|
||||
backgroundColor: colour
|
||||
}
|
||||
})
|
||||
keyframes['100%'] = { backgroundColor: rainbowColours[0] }
|
||||
return keyframes
|
||||
}, [rainbowColours])
|
||||
|
||||
if (props?.loaded) {
|
||||
return props?.children
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
cursor='pointer'
|
||||
w='10px'
|
||||
h='8px'
|
||||
borderRadius={'2px'}
|
||||
sx={{
|
||||
'@keyframes placeholder-rainbow': keyframes,
|
||||
'@keyframes placeholder-opacity': {
|
||||
'0%': { opacity: 0.2 },
|
||||
'100%': { opacity: 1 }
|
||||
},
|
||||
// add delay
|
||||
animation: `placeholder-rainbow 8s infinite linear, placeholder-opacity 1.3s linear 0s infinite alternate none running}`
|
||||
}}
|
||||
{...props}
|
||||
></Flex>
|
||||
)
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { renderToString } from 'react-dom/server'
|
||||
import { RemixServer } from '@remix-run/react'
|
||||
import type { EntryContext } from '@remix-run/react/dist/entry'
|
||||
|
||||
export default function handleRequest (
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
let markup = renderToString(
|
||||
<RemixServer context={remixContext} url={request.url} />
|
||||
)
|
||||
|
||||
responseHeaders.set('Content-Type', 'text/html')
|
||||
|
||||
return new Response('<!DOCTYPE html>' + markup, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders
|
||||
})
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
export default class Sample {
|
||||
constructor({ x, y, progress, segment }) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.progress = progress;
|
||||
this.segment = segment;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { getMiddleSample } from './_utils';
|
||||
|
||||
export default class Segment {
|
||||
constructor({ samples }) {
|
||||
this.samples = samples;
|
||||
this.progress = getMiddleSample(samples).progress;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
export const DEFAULT_PRECISION = 2;
|
@ -1,2 +0,0 @@
|
||||
export { default as GradientPath } from './GradientPath';
|
||||
export { getData, strokeToFill } from './_data';
|
@ -5,7 +5,8 @@
|
||||
"build": "remix build",
|
||||
"dev": "remix dev --port 9999",
|
||||
"lint": "eslint --ext .js,.ts,.jsx,.tsx .",
|
||||
"lint-fix": "eslint --ext .js,.ts,.jsx,.tsx . --fix"
|
||||
"lint-fix": "eslint --ext .js,.ts,.jsx,.tsx . --fix",
|
||||
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,scss,md}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@chakra-ui/react": "^2.7.1",
|
||||
|
@ -2,12 +2,12 @@
|
||||
* @type {import('@remix-run/dev').AppConfig}
|
||||
*/
|
||||
module.exports = {
|
||||
ignoredRouteFiles: ['**/.*']
|
||||
ignoredRouteFiles: ["**/.*"],
|
||||
// future: {
|
||||
// unstable_dev: true
|
||||
// appServerPort: 3999
|
||||
// }
|
||||
// appDirectory: "app",
|
||||
appDirectory: "src",
|
||||
// assetsBuildDirectory: "public/build",
|
||||
// serverBuildPath: "build/index.js",
|
||||
// publicPath: "/build/",
|
||||
|
6
app/src/Providers/Chakra/ChakraWrapper.tsx
Normal file
6
app/src/Providers/Chakra/ChakraWrapper.tsx
Normal file
@ -0,0 +1,6 @@
|
||||
import { ChakraProvider } from "@chakra-ui/react"
|
||||
|
||||
import { theme } from "./theme"
|
||||
export const ChakraWrapper = (props) => {
|
||||
return <ChakraProvider theme={theme}>{props.children}</ChakraProvider>
|
||||
}
|
@ -1,11 +1,17 @@
|
||||
const g = {
|
||||
grey: "#F1F1F3",
|
||||
greys: {
|
||||
const greys = {
|
||||
light: "#F1F1F3",
|
||||
lightt: "#E7E6E8",
|
||||
medium: "#E0E0E0",
|
||||
dark: "#BDBDBD",
|
||||
},
|
||||
}
|
||||
|
||||
const grey = "#F1F1F3"
|
||||
|
||||
const g = {
|
||||
gray: grey,
|
||||
grey,
|
||||
grays: greys,
|
||||
greys,
|
||||
}
|
||||
|
||||
// for bad spellers
|
||||
@ -33,6 +39,22 @@ export const colors = {
|
||||
indigo: "#5C6BC0",
|
||||
violet: "#AB47BC",
|
||||
},
|
||||
chakrasDark: {
|
||||
root: "#8E0000",
|
||||
sacral: "#E65100",
|
||||
solarPlexus: "#FDD835",
|
||||
heart: "#33691E",
|
||||
throat: "#1E88E5",
|
||||
thirdEye: "#3949AB",
|
||||
crown: "#6A1B9A",
|
||||
red: "#8E0000",
|
||||
orange: "#E65100",
|
||||
yellow: "#FDD835",
|
||||
green: "#33691E",
|
||||
blue: "#1E88E5",
|
||||
indigo: "#3949AB",
|
||||
violet: "#6A1B9A",
|
||||
},
|
||||
// all colors of the rainbow
|
||||
rainbow: {
|
||||
red: "#FF0000",
|
@ -2,8 +2,8 @@ import React, { createContext } from "react"
|
||||
import flatted, { parse, stringify } from "flatted"
|
||||
import { Subject } from "rxjs"
|
||||
|
||||
import { sanitise } from "~/functions/sanitise"
|
||||
import { smarts } from "~/smarts"
|
||||
import { sanitise } from "../functions/sanitise"
|
||||
import { smarts } from "../smarts"
|
||||
|
||||
export interface ThingtimeContextInterface {
|
||||
thingtime: any
|
||||
@ -292,7 +292,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
const [thingtimeReference, rawSet] = React.useState()
|
||||
|
||||
const thingtimeRef = React.useRef(thingtimeReference)
|
||||
const stateRef = React.useRef({
|
||||
const stateRef: any = React.useRef({
|
||||
c: 1,
|
||||
})
|
||||
|
||||
@ -409,7 +409,7 @@ export const ThingtimeProvider = (props: any): JSX.Element => {
|
||||
}
|
||||
}
|
||||
|
||||
const newThingtime = thingtimeReference
|
||||
const newThingtime: any = thingtimeReference || {}
|
||||
|
||||
const paths = smarts.parsePropertyPath(path)
|
||||
|
32
app/src/components/Buttons/Attention.tsx
Normal file
32
app/src/components/Buttons/Attention.tsx
Normal file
@ -0,0 +1,32 @@
|
||||
import React from "react"
|
||||
import { Flex } from "@chakra-ui/react"
|
||||
|
||||
export const Attention = (props) => {
|
||||
return (
|
||||
<Flex
|
||||
{...props}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexDirection="column"
|
||||
cursor="pointer"
|
||||
>
|
||||
<Flex
|
||||
sx={{
|
||||
"@keyframes moving-rainbow": {
|
||||
"0%": { backgroundPosition: "0 0" },
|
||||
"100%": { backgroundPosition: "200% 0" },
|
||||
},
|
||||
// add delay
|
||||
animation: `moving-rainbow 3s infinite linear`,
|
||||
}}
|
||||
width={props.w || "40px"}
|
||||
height="2px"
|
||||
marginBottom="10px"
|
||||
background="linear-gradient(to right, #f34a4a, #ffbc48, #58ca70, #47b5e6, #a555e8, #f34a4a);"
|
||||
backgroundSize="200%"
|
||||
borderBottomRadius="20px"
|
||||
transition="all 0.5s ease-in-out"
|
||||
></Flex>
|
||||
</Flex>
|
||||
)
|
||||
}
|
38
app/src/components/Buttons/Hamburger.tsx
Normal file
38
app/src/components/Buttons/Hamburger.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import React from "react"
|
||||
import { Flex } from "@chakra-ui/react"
|
||||
|
||||
export const Hamburger = (props) => {
|
||||
const lineCount = [1, 2, 3]
|
||||
|
||||
return (
|
||||
<Flex
|
||||
{...props}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexDirection="column"
|
||||
cursor="pointer"
|
||||
>
|
||||
{lineCount.map((line, idx) => {
|
||||
return (
|
||||
<Flex
|
||||
key={idx}
|
||||
sx={{
|
||||
"@keyframes moving-rainbow": {
|
||||
"0%": { backgroundPosition: "0 0" },
|
||||
"100%": { backgroundPosition: "200% 0" },
|
||||
},
|
||||
// add delay
|
||||
animation: `moving-rainbow 3s infinite linear -${idx * 0.3}s}`,
|
||||
}}
|
||||
width="40px"
|
||||
height="3px"
|
||||
marginBottom="10px"
|
||||
background="linear-gradient(to right, #f34a4a, #ffbc48, #58ca70, #47b5e6, #a555e8, #f34a4a);"
|
||||
backgroundSize="200%"
|
||||
borderRadius="9px"
|
||||
></Flex>
|
||||
)
|
||||
})}
|
||||
</Flex>
|
||||
)
|
||||
}
|
@ -468,6 +468,7 @@ export const Commander = (props) => {
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
// color: "white",
|
||||
},
|
||||
}}
|
||||
width="100%"
|
@ -564,6 +564,7 @@ export const CommanderV1 = (props) => {
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
// color: "white",
|
||||
},
|
||||
}}
|
||||
zIndex={9999}
|
||||
@ -601,6 +602,8 @@ export const CommanderV1 = (props) => {
|
||||
sx={{
|
||||
"&::placeholder": {
|
||||
color: "greys.dark",
|
||||
// color: "white",
|
||||
// textShadow: "0 0 5px black",
|
||||
},
|
||||
}}
|
||||
zIndex={9999}
|
225
app/src/components/Icon/Icon.tsx
Normal file
225
app/src/components/Icon/Icon.tsx
Normal file
@ -0,0 +1,225 @@
|
||||
import React from "react"
|
||||
import { Center } from "@chakra-ui/react"
|
||||
import emojis from "emojis-list"
|
||||
|
||||
export const Icon = (props) => {
|
||||
const name = props?.name
|
||||
|
||||
const icon = React.useMemo(() => {
|
||||
// nothing
|
||||
if (["⚙️", "gear", "cog"]?.includes(name)) {
|
||||
return "⚙️"
|
||||
}
|
||||
if (["🔮", "crystal"]?.includes(name)) {
|
||||
return "🔮"
|
||||
}
|
||||
if (["🌺", "flower", "hibiscus"]?.includes(name)) {
|
||||
return "🌺"
|
||||
}
|
||||
if (["✨", "sparke", "magic"]?.includes(name)) {
|
||||
return "✨"
|
||||
}
|
||||
if (["🧙♂️", "wizard", "gandalf"]?.includes(name)) {
|
||||
return "🧙♂️"
|
||||
}
|
||||
if (["👀", "two eyes"]?.includes(name)) {
|
||||
return "👀"
|
||||
}
|
||||
if (["📦", "box", "thing", "object"]?.includes(name)) {
|
||||
return "📦"
|
||||
}
|
||||
if (["✏️", "pencil"]?.includes(name)) {
|
||||
return "✏️"
|
||||
}
|
||||
if (["🎨", "edit", "paint", "create"]?.includes(name)) {
|
||||
return "🎨"
|
||||
}
|
||||
if (["📚", "book", "books"]?.includes(name)) {
|
||||
return "📚"
|
||||
}
|
||||
if (["🪄", "any", "magic wand"]?.includes(name)) {
|
||||
return "🪄"
|
||||
}
|
||||
if (["📖", "book-open", "books-open"]?.includes(name)) {
|
||||
return "📖"
|
||||
}
|
||||
if (["👩🏫", "book-reader", "books-reader"]?.includes(name)) {
|
||||
return "👩🏫"
|
||||
}
|
||||
if (["💯", "number", "hundred"]?.includes(name)) {
|
||||
return "💯"
|
||||
}
|
||||
if (["🧩", "puzzle", "types"]?.includes(name)) {
|
||||
return "🧩"
|
||||
}
|
||||
if (["❤️", "heart"]?.includes(name)) {
|
||||
return "❤️"
|
||||
}
|
||||
if (["💔", "heart-broken"]?.includes(name)) {
|
||||
return "💔"
|
||||
}
|
||||
if (["💗", "heart-pulse"]?.includes(name)) {
|
||||
return "💗"
|
||||
}
|
||||
if (["💬", "string", "text"]?.includes(name)) {
|
||||
return "💬"
|
||||
}
|
||||
if (["📚", "array", "list"]?.includes(name)) {
|
||||
return "📚"
|
||||
}
|
||||
if (["🌗", "boolean", "bool"]?.includes(name)) {
|
||||
return "🌗"
|
||||
// return "⚖️"
|
||||
}
|
||||
if (["🌈", "rainbow"]?.includes(name)) {
|
||||
return "🌈"
|
||||
}
|
||||
if (["☀️", "sun"]?.includes(name)) {
|
||||
return "☀️"
|
||||
}
|
||||
if (["🌙", "moon"]?.includes(name)) {
|
||||
return "🌙"
|
||||
}
|
||||
if (["🦄", "unicorn"]?.includes(name)) {
|
||||
return "🦄"
|
||||
}
|
||||
if (["👤", "user", "person"]?.includes(name)) {
|
||||
return "👤"
|
||||
}
|
||||
if (["👥", "group", "team"]?.includes(name)) {
|
||||
return "👥"
|
||||
}
|
||||
if (["✅", "success", "check"]?.includes(name)) {
|
||||
return "✅"
|
||||
}
|
||||
if (["❌", "error", "stop"]?.includes(name)) {
|
||||
return "❌"
|
||||
}
|
||||
if (["⚠️", "warning", "alert"]?.includes(name)) {
|
||||
return "⚠️"
|
||||
}
|
||||
if (["⏰", "time", "clock"]?.includes(name)) {
|
||||
return "⏰"
|
||||
}
|
||||
if (["⭐", "star", "favorite"]?.includes(name)) {
|
||||
return "⭐"
|
||||
}
|
||||
if (["🌟", "glowing star", "glowing favorite"]?.includes(name)) {
|
||||
return "🌟"
|
||||
}
|
||||
if (["❓", "question", "help"]?.includes(name)) {
|
||||
return "❓"
|
||||
}
|
||||
if (["🎥", "video", "media"]?.includes(name)) {
|
||||
return "🎥"
|
||||
}
|
||||
if (["🎵", "music", "audio"]?.includes(name)) {
|
||||
return "🎵"
|
||||
}
|
||||
if (["🖼️", "image", "picture"]?.includes(name)) {
|
||||
return "🖼️"
|
||||
}
|
||||
if (["✉️", "email", "mail"]?.includes(name)) {
|
||||
return "✉️"
|
||||
}
|
||||
if (["💻", "computer", "laptop"]?.includes(name)) {
|
||||
return "💻"
|
||||
}
|
||||
if (["📱", "mobile", "phone"]?.includes(name)) {
|
||||
return "📱"
|
||||
}
|
||||
if (["🌍", "world", "globe"]?.includes(name)) {
|
||||
return "🌍"
|
||||
}
|
||||
if (["🚀", "rocket", "launch"]?.includes(name)) {
|
||||
return "🚀"
|
||||
}
|
||||
if (["✏️", "pencil", "edit"]?.includes(name)) {
|
||||
return "✏️"
|
||||
}
|
||||
if (["🔍", "search", "magnify"]?.includes(name)) {
|
||||
return "🔍"
|
||||
}
|
||||
if (["🔒", "lock", "secure"]?.includes(name)) {
|
||||
return "🔒"
|
||||
}
|
||||
if (["🔓", "unlock", "access"]?.includes(name)) {
|
||||
return "🔓"
|
||||
}
|
||||
if (["👍", "thumb-up", "like"]?.includes(name)) {
|
||||
return "👍"
|
||||
}
|
||||
if (["👎", "thumb-down", "dislike"]?.includes(name)) {
|
||||
return "👎"
|
||||
}
|
||||
if (["➕", "plus", "add"]?.includes(name)) {
|
||||
return "➕"
|
||||
}
|
||||
if (["🌱", "seedling", "seed"]?.includes(name)) {
|
||||
return "🌱"
|
||||
}
|
||||
if (["❓", "undefined", "null", "question", "confused"]?.includes(name)) {
|
||||
return "❓"
|
||||
// return "🌫️"
|
||||
}
|
||||
if (["🤖", "codex", "robot", "ai", "chatgpt"]?.includes(name)) {
|
||||
return "🤖"
|
||||
}
|
||||
if (["🗑️", "trash", "bin", "delete", "remove"]?.includes(name)) {
|
||||
return "🗑️"
|
||||
}
|
||||
if (["cash", "money"]?.includes(name)) {
|
||||
// return "💰"
|
||||
return "💵"
|
||||
}
|
||||
if (["💰", "money bag"]?.includes(name)) {
|
||||
return "💰"
|
||||
}
|
||||
if (["🌀", "cyclone", "tornado"]?.includes(name)) {
|
||||
return "🌀"
|
||||
}
|
||||
if (["thingtime"]?.includes(name)) {
|
||||
if (Math.random() > 0.5) {
|
||||
return "🌳"
|
||||
}
|
||||
return "🌀"
|
||||
}
|
||||
if (["📐", "function", "lambda"]?.includes(name)) {
|
||||
return "📐"
|
||||
}
|
||||
if (["📌", "pin", "pinned", "located"]?.includes(name)) {
|
||||
return "📌"
|
||||
}
|
||||
|
||||
if (["🎁", "wrap", "wrapped"]?.includes(name)) {
|
||||
return "🎁"
|
||||
// return "🎀"
|
||||
}
|
||||
|
||||
if (["🦕", "dinosaur", "dino"]?.includes(name)) {
|
||||
return "🦕"
|
||||
}
|
||||
|
||||
if (emojis?.includes(name)) {
|
||||
return name
|
||||
}
|
||||
|
||||
if (["random"]?.includes(name)) {
|
||||
return emojis[Math.floor(Math.random() * emojis.length)]
|
||||
}
|
||||
|
||||
// question mark
|
||||
return "🤷♂️"
|
||||
}, [name])
|
||||
|
||||
return (
|
||||
<Center
|
||||
transition="all 0.2s ease-out"
|
||||
{...props}
|
||||
{...props?.chakras}
|
||||
fontSize={props?.size}
|
||||
>
|
||||
{icon}
|
||||
</Center>
|
||||
)
|
||||
}
|
92
app/src/components/Login/Login.tsx
Normal file
92
app/src/components/Login/Login.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Flex, Button, FormControl, Input } from '@chakra-ui/react';
|
||||
|
||||
export const Login = (props) => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
|
||||
const handleLogin = (props) => {
|
||||
// handle login
|
||||
|
||||
const { username, password } = props;
|
||||
|
||||
console.log('nik username', username);
|
||||
console.log('nik password', password);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<form>
|
||||
<Flex flexDirection="column" gap={4} width="255px" maxWidth="100%">
|
||||
<FormControl>
|
||||
<Input
|
||||
sx={{
|
||||
'&::placeholder': {
|
||||
color: 'greys.dark'
|
||||
// color: "white",
|
||||
}
|
||||
}}
|
||||
background="grey"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="Email"
|
||||
type="username"
|
||||
value={email}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<Input
|
||||
sx={{
|
||||
'&::placeholder': {
|
||||
color: 'greys.dark'
|
||||
// color: "white",
|
||||
}
|
||||
}}
|
||||
background="grey"
|
||||
border="none"
|
||||
borderRadius="5px"
|
||||
outline="none"
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="Password"
|
||||
type="password"
|
||||
value={password}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
sx={{
|
||||
'@keyframes moving-rainbow': {
|
||||
'0%': { backgroundPosition: '0 0' },
|
||||
'100%': { backgroundPosition: 'calc(100px + 400%) 0' }
|
||||
},
|
||||
animation: 'moving-rainbow 40s infinite linear'
|
||||
}}
|
||||
type="submit"
|
||||
display="Flex"
|
||||
justifyContent="flex-start"
|
||||
width="100%"
|
||||
color="white"
|
||||
fontWeight="bold"
|
||||
background="chakras.violet"
|
||||
backgroundSize="calc(100px + 400%)"
|
||||
// Add rainbow animation background gradient right to left
|
||||
bgGradient="linear-gradient(to right, #47b5e6, #a555e8, #f34a4a, #ffbc48, #58ca70, #47b5e6)"
|
||||
borderRadius={6}
|
||||
_hover={{
|
||||
opacity: 0.9
|
||||
}}
|
||||
cursor="pointer"
|
||||
transition="all 150ms ease-in-out"
|
||||
paddingX={4}
|
||||
paddingY={2}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Flex>
|
||||
</form>
|
||||
</>
|
||||
);
|
||||
};
|
@ -3,7 +3,7 @@ import { Box } from "@chakra-ui/react"
|
||||
|
||||
import { useThingtime } from "../Thingtime/useThingtime"
|
||||
|
||||
export const MagicInput = React.forwardRef((props, ref) => {
|
||||
export const MagicInput = React.forwardRef((props: any, ref) => {
|
||||
const { thingtime, setThingtime, loading } = useThingtime()
|
||||
|
||||
const [inputValue, setInputValue] = React.useState()
|
||||
@ -153,7 +153,7 @@ export const MagicInput = React.forwardRef((props, ref) => {
|
||||
contentEditable={!props?.readonly ? true : false}
|
||||
dangerouslySetInnerHTML={dangerouslySetInnerHTML}
|
||||
onFocus={onFocus}
|
||||
onInput={(value) => {
|
||||
onInput={(value: EventTarget | any) => {
|
||||
const innerText = value?.target?.innerText
|
||||
console.log("MagicInput got onInput event value", value)
|
||||
console.log("MagicInput got onInput event innerText", innerText)
|
@ -3,7 +3,7 @@ import { Box, Center, Flex, Text } from "@chakra-ui/react"
|
||||
import { Link, useLocation, useNavigate } from "@remix-run/react"
|
||||
|
||||
import { Commander } from "../Commander/Commander"
|
||||
import { CommanderV1 } from "../Commander/CommanderV1"
|
||||
import { CommanderV1 } from "../Commander/CommanderV2"
|
||||
import { Icon } from "../Icon/Icon"
|
||||
import { RainbowSkeleton } from "../Skeleton/RainbowSkeleton"
|
||||
import { ProfileDrawer } from "./ProfileDrawer"
|
@ -1,90 +1,83 @@
|
||||
import React from "react"
|
||||
import { Box, Center, Flex } from "@chakra-ui/react"
|
||||
import { Link, useLocation, useNavigate } from "@remix-run/react"
|
||||
import React from 'react';
|
||||
import { Box, Center, Flex } from '@chakra-ui/react';
|
||||
import { Link, useLocation, useNavigate } from '@remix-run/react';
|
||||
|
||||
import { Commander } from "../Commander/Commander"
|
||||
import { CommanderV1 } from "../Commander/CommanderV1"
|
||||
import { Icon } from "../Icon/Icon"
|
||||
import { RainbowSkeleton } from "../Skeleton/RainbowSkeleton"
|
||||
import { ProfileDrawer } from "./ProfileDrawer"
|
||||
import { Commander } from '../Commander/Commander';
|
||||
import { CommanderV1 } from '../Commander/CommanderV2';
|
||||
import { Icon } from '../Icon/Icon';
|
||||
import { RainbowSkeleton } from '../Skeleton/RainbowSkeleton';
|
||||
import { ProfileDrawer } from './ProfileDrawer';
|
||||
|
||||
export const Nav = (props) => {
|
||||
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false)
|
||||
const [profileDrawerOpen, setProfileDrawerOpen] = React.useState(false);
|
||||
|
||||
const { pathname } = useLocation()
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const toggleProfileDrawer = React.useCallback(() => {
|
||||
setProfileDrawerOpen(!profileDrawerOpen)
|
||||
}, [profileDrawerOpen])
|
||||
setProfileDrawerOpen(!profileDrawerOpen);
|
||||
}, [profileDrawerOpen]);
|
||||
|
||||
const inEditorMode = React.useMemo(() => {
|
||||
if (pathname.slice(0, 7) === "/editor") {
|
||||
return true
|
||||
if (pathname.slice(0, 7) === '/editor') {
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
return false;
|
||||
}, [pathname]);
|
||||
|
||||
const inEditMode = React.useMemo(() => {
|
||||
if (pathname.slice(0, 5) === "/edit") {
|
||||
return true
|
||||
if (pathname.slice(0, 5) === '/edit') {
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
return false;
|
||||
}, [pathname]);
|
||||
|
||||
const editorToggleable = React.useMemo(() => {
|
||||
if (pathname.slice(0, 7) === "/things") {
|
||||
return true
|
||||
} else if (pathname.slice(0, 5) === "/edit") {
|
||||
return true
|
||||
if (pathname.slice(0, 7) === '/things') {
|
||||
return true;
|
||||
} else if (pathname.slice(0, 5) === '/edit') {
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
}, [pathname])
|
||||
return false;
|
||||
}, [pathname]);
|
||||
|
||||
const toggleEdit = React.useCallback(
|
||||
(e) => {
|
||||
// if first characters of pathname are /things replace with /edit
|
||||
// or if first characters of pathname are /edit replace with /things
|
||||
if (pathname.slice(0, 7) === "/things") {
|
||||
const newPathname = pathname.replace("/things", "/edit")
|
||||
navigate(newPathname)
|
||||
} else if (pathname.slice(0, 7) === "/editor") {
|
||||
const newPathname = pathname.replace("/editor", "/things")
|
||||
navigate(newPathname)
|
||||
} else if (pathname.slice(0, 5) === "/edit") {
|
||||
const newPathname = pathname.replace("/edit", "/things")
|
||||
navigate(newPathname)
|
||||
if (pathname.slice(0, 7) === '/things') {
|
||||
const newPathname = pathname.replace('/things', '/edit');
|
||||
navigate(newPathname);
|
||||
} else if (pathname.slice(0, 7) === '/editor') {
|
||||
const newPathname = pathname.replace('/editor', '/things');
|
||||
navigate(newPathname);
|
||||
} else if (pathname.slice(0, 5) === '/edit') {
|
||||
const newPathname = pathname.replace('/edit', '/things');
|
||||
navigate(newPathname);
|
||||
}
|
||||
},
|
||||
[pathname, navigate]
|
||||
)
|
||||
);
|
||||
|
||||
const toggleEditor = React.useCallback(
|
||||
(e) => {
|
||||
// if first characters of pathname are /things replace with /edit
|
||||
// or if first characters of pathname are /edit replace with /things
|
||||
if (pathname.slice(0, 7) === "/editor") {
|
||||
const newPathname = pathname.replace("/editor", "/edit")
|
||||
navigate(newPathname)
|
||||
} else if (pathname.slice(0, 5) === "/edit") {
|
||||
const newPathname = pathname.replace("/edit", "/editor")
|
||||
navigate(newPathname)
|
||||
if (pathname.slice(0, 7) === '/editor') {
|
||||
const newPathname = pathname.replace('/editor', '/edit');
|
||||
navigate(newPathname);
|
||||
} else if (pathname.slice(0, 5) === '/edit') {
|
||||
const newPathname = pathname.replace('/edit', '/editor');
|
||||
navigate(newPathname);
|
||||
}
|
||||
},
|
||||
[pathname, navigate]
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
position="fixed"
|
||||
zIndex={9999}
|
||||
top={0}
|
||||
right={0}
|
||||
left={0}
|
||||
maxWidth="100vw"
|
||||
>
|
||||
<Box position="fixed" zIndex={9999} top={0} right={0} left={0} maxWidth="100vw">
|
||||
<Flex
|
||||
as="nav"
|
||||
position="relative"
|
||||
@ -99,25 +92,15 @@ export const Nav = (props) => {
|
||||
// bg='white'
|
||||
// boxShadow={'0px 0px 10px rgba(0,0,0,0.1)'}
|
||||
>
|
||||
<Center
|
||||
className="nav-left-section"
|
||||
display={["none", "flex"]}
|
||||
height="100%"
|
||||
marginRight="auto"
|
||||
>
|
||||
<Center className="nav-left-section" display={['none', 'flex']} height="100%" marginRight="auto">
|
||||
<Center transform="scaleX(-100%)" cursor="pointer">
|
||||
<Link to="/">
|
||||
<Icon size="12px" name="unicorn"></Icon>
|
||||
<Icon size="12px" name="🦄"></Icon>
|
||||
</Link>
|
||||
</Center>
|
||||
</Center>
|
||||
<CommanderV1 global id="nav" rainbow={false}></CommanderV1>
|
||||
<Center
|
||||
className="nav-right-section"
|
||||
columnGap={[3, 8]}
|
||||
height="100%"
|
||||
marginLeft="auto"
|
||||
>
|
||||
<Center className="nav-right-section" columnGap={[3, 8]} height="100%" marginLeft="auto">
|
||||
{inEditMode && (
|
||||
<Center
|
||||
// transform="scaleX(-100%)"
|
||||
@ -126,10 +109,10 @@ export const Nav = (props) => {
|
||||
>
|
||||
<Icon
|
||||
chakras={{
|
||||
opacity: inEditorMode ? 1 : 0.3,
|
||||
opacity: inEditorMode ? 1 : 0.3
|
||||
}}
|
||||
size="12px"
|
||||
name="two eyes"
|
||||
name="👀"
|
||||
></Icon>
|
||||
{/* <Icon
|
||||
size="12px"
|
||||
@ -138,17 +121,13 @@ export const Nav = (props) => {
|
||||
</Center>
|
||||
)}
|
||||
{editorToggleable && (
|
||||
<Center
|
||||
transform="scaleX(-100%)"
|
||||
cursor="pointer"
|
||||
onClick={toggleEdit}
|
||||
>
|
||||
<Center transform="scaleX(-100%)" cursor="pointer" onClick={toggleEdit}>
|
||||
<Icon
|
||||
chakras={{
|
||||
opacity: inEditMode ? 1 : 0.3,
|
||||
opacity: inEditMode ? 1 : 0.3
|
||||
}}
|
||||
size="12px"
|
||||
name="paint"
|
||||
name="🎨"
|
||||
></Icon>
|
||||
{/* <Icon
|
||||
size="12px"
|
||||
@ -156,12 +135,12 @@ export const Nav = (props) => {
|
||||
></Icon> */}
|
||||
</Center>
|
||||
)}
|
||||
<Center transform={["", "scaleX(-100%)"]} cursor="pointer">
|
||||
<Icon size="12px" name="rainbow"></Icon>
|
||||
<Center transform={['', 'scaleX(-100%)']} cursor="pointer">
|
||||
<Icon size="12px" name="🌈"></Icon>
|
||||
</Center>
|
||||
<Center display={["flex", "none"]} cursor="pointer">
|
||||
<Center display={['flex', 'none']} cursor="pointer">
|
||||
<Link to="/">
|
||||
<Icon size="12px" name="unicorn"></Icon>
|
||||
<Icon size="12px" name="🦄"></Icon>
|
||||
</Link>
|
||||
</Center>
|
||||
</Center>
|
||||
@ -181,5 +160,5 @@ export const Nav = (props) => {
|
||||
</Box>
|
||||
{/* <ProfileDrawer isOpen={profileDrawerOpen}></ProfileDrawer> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import { Drawer, DrawerBody, DrawerContent, Flex } from '@chakra-ui/react'
|
||||
import React from "react"
|
||||
import { Drawer, DrawerBody, DrawerContent, Flex } from "@chakra-ui/react"
|
||||
|
||||
export const ProfileDrawer = props => {
|
||||
const navItems = ['settings']
|
||||
export const ProfileDrawer = (props) => {
|
||||
const navItems = ["settings"]
|
||||
|
||||
const onClose = React.useCallback(() => {
|
||||
// nothing
|
||||
@ -10,12 +10,12 @@ export const ProfileDrawer = props => {
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
placement='right'
|
||||
size='md'
|
||||
isOpen={props?.isOpen}
|
||||
onClose={onClose}
|
||||
placement="right"
|
||||
size="md"
|
||||
>
|
||||
<DrawerContent maxW={'90%'} mt={'35px'} className='ProfileDrawer'>
|
||||
<DrawerContent className="ProfileDrawer" maxWidth="90%" marginTop="35px">
|
||||
<DrawerBody>
|
||||
{navItems.map((item, idx) => {
|
||||
return <Flex key={idx}>{item}</Flex>
|
70
app/src/components/Nav/ReactiveNav.tsx
Normal file
70
app/src/components/Nav/ReactiveNav.tsx
Normal file
@ -0,0 +1,70 @@
|
||||
import React from "react"
|
||||
import { Flex } from "@chakra-ui/react"
|
||||
|
||||
import { Attention } from "../Buttons/Attention"
|
||||
import { Hamburger } from "../Buttons/Hamburger"
|
||||
import { RightNav } from "./ReactiveRightNav"
|
||||
|
||||
export const ReactiveNav = (props) => {
|
||||
const [navItems] = React.useState([{}])
|
||||
|
||||
const setNav = React.useCallback((active) => {
|
||||
setMt(active ? "100%" : "0%")
|
||||
setNavActive(active)
|
||||
}, [])
|
||||
|
||||
React.useEffect(() => {
|
||||
// react to mouse move event
|
||||
const listener = (event) => {
|
||||
if (event?.clientY <= 20) {
|
||||
setNav(true)
|
||||
} else if (event?.clientY >= 100) {
|
||||
setNav(false)
|
||||
}
|
||||
}
|
||||
window.addEventListener("mousemove", listener)
|
||||
return () => {
|
||||
window.removeEventListener("mousemove", listener)
|
||||
}
|
||||
}, [setNav])
|
||||
|
||||
const [navActive, setNavActive] = React.useState(false)
|
||||
|
||||
const [mt, setMt] = React.useState("0%")
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex
|
||||
as="nav"
|
||||
position="fixed"
|
||||
right={0}
|
||||
bottom="100%"
|
||||
left={0}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
flexDirection="row"
|
||||
width="100%"
|
||||
transform={`translateY(${mt})`}
|
||||
transition="all 0.3s ease-in-out"
|
||||
// top={'100%'}
|
||||
// top={0}
|
||||
paddingX="1%"
|
||||
paddingY="1%"
|
||||
>
|
||||
<Hamburger marginLeft="auto"></Hamburger>
|
||||
<Flex
|
||||
position="absolute"
|
||||
top="100%"
|
||||
left="50%"
|
||||
opacity={navActive ? 0 : 1}
|
||||
transition="all 0.2s ease-in-out"
|
||||
translateX="-50%"
|
||||
>
|
||||
{/* w={navActive ? '0px' : '40px'} */}
|
||||
<Attention></Attention>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<RightNav></RightNav>
|
||||
</>
|
||||
)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import { Center, Text } from '@chakra-ui/react'
|
||||
import React from "react"
|
||||
import { Center, Text } from "@chakra-ui/react"
|
||||
|
||||
export const ReactiveRightNav = props => {
|
||||
const setNav = React.useCallback(active => {
|
||||
setMr(active ? '0%' : '-100%')
|
||||
export const ReactiveRightNav = (props) => {
|
||||
const setNav = React.useCallback((active) => {
|
||||
setMr(active ? "0%" : "-100%")
|
||||
setNavActive(active)
|
||||
}, [])
|
||||
|
||||
@ -11,7 +11,7 @@ export const ReactiveRightNav = props => {
|
||||
|
||||
React.useEffect(() => {
|
||||
// react to mouse move event
|
||||
const listener = event => {
|
||||
const listener = (event) => {
|
||||
const windowWidth = window.innerWidth
|
||||
if (!entered) {
|
||||
if (event?.clientX >= windowWidth - 60) {
|
||||
@ -21,56 +21,56 @@ export const ReactiveRightNav = props => {
|
||||
}
|
||||
}
|
||||
}
|
||||
window.addEventListener('mousemove', listener)
|
||||
window.addEventListener("mousemove", listener)
|
||||
return () => {
|
||||
window.removeEventListener('mousemove', listener)
|
||||
window.removeEventListener("mousemove", listener)
|
||||
}
|
||||
}, [setNav, entered])
|
||||
|
||||
const [navActive, setNavActive] = React.useState(false)
|
||||
|
||||
// const defaultMr = '-100%'
|
||||
const defaultMr = '0%'
|
||||
const defaultMr = "0%"
|
||||
|
||||
const [mr, setMr] = React.useState(defaultMr)
|
||||
|
||||
const navItems = React.useMemo(() => {
|
||||
return ['home', 'imagine', 'create', 'share']
|
||||
return ["home", "imagine", "create", "share"]
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<Center
|
||||
mr={mr}
|
||||
transition={'all 0.3s ease-in-out'}
|
||||
position='fixed'
|
||||
position="fixed"
|
||||
top={0}
|
||||
right={0}
|
||||
h='100%'
|
||||
flexDir='column'
|
||||
flexDirection="column"
|
||||
height="100%"
|
||||
marginRight={mr}
|
||||
transition="all 0.3s ease-in-out"
|
||||
>
|
||||
<Center
|
||||
flexDirection="column"
|
||||
minWidth="600px"
|
||||
maxWidth="100%"
|
||||
height="85%"
|
||||
background="rgba(0, 0, 0, 0.01)"
|
||||
borderLeftRadius="40px"
|
||||
onMouseEnter={() => setEntered(true)}
|
||||
onMouseLeave={() => setEntered(false)}
|
||||
flexDir={'column'}
|
||||
borderLeftRadius={'40px'}
|
||||
h='85%'
|
||||
minW={'600px'}
|
||||
maxW='100%'
|
||||
py={80}
|
||||
px={100}
|
||||
bg={`rgba(0, 0, 0, 0.01)`}
|
||||
paddingX={100}
|
||||
paddingY={80}
|
||||
>
|
||||
{navItems.map((navItem, idx) => {
|
||||
return (
|
||||
<Text
|
||||
color='black'
|
||||
my={'auto'}
|
||||
fontWeight='semibold'
|
||||
key={idx}
|
||||
textTransform={'capitalize'}
|
||||
as='h2'
|
||||
cursor='pointer'
|
||||
fontSize='30px'
|
||||
as="h2"
|
||||
color="black"
|
||||
fontSize="30px"
|
||||
fontWeight="semibold"
|
||||
textTransform="capitalize"
|
||||
cursor="pointer"
|
||||
marginY="auto"
|
||||
>
|
||||
{navItem}
|
||||
</Text>
|
@ -1,6 +1,6 @@
|
||||
import { safe } from '~/functions/safe'
|
||||
import { safe } from "~/functions/safe"
|
||||
|
||||
export const Safe = props => {
|
||||
export const Safe = (props) => {
|
||||
// do not render more than the limit of things to prevent infinite loops
|
||||
|
||||
return safe(props)
|
46
app/src/components/Skeleton/RainbowSkeleton.tsx
Normal file
46
app/src/components/Skeleton/RainbowSkeleton.tsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React from "react"
|
||||
import { Flex } from "@chakra-ui/react"
|
||||
|
||||
export const RainbowSkeleton = (props) => {
|
||||
const [rainbowColours] = React.useState([
|
||||
"#f34a4a",
|
||||
"#ffbc48",
|
||||
"#58ca70",
|
||||
"#47b5e6",
|
||||
"#a555e8",
|
||||
])
|
||||
|
||||
const keyframes = React.useMemo(() => {
|
||||
const keyframes = {}
|
||||
rainbowColours.forEach((colour, idx) => {
|
||||
keyframes[Math.round((idx * 100) / rainbowColours.length) + "%"] = {
|
||||
backgroundColor: colour,
|
||||
}
|
||||
})
|
||||
keyframes["100%"] = { backgroundColor: rainbowColours[0] }
|
||||
return keyframes
|
||||
}, [rainbowColours])
|
||||
|
||||
if (props?.loaded) {
|
||||
return props?.children
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex
|
||||
sx={{
|
||||
"@keyframes placeholder-rainbow": keyframes,
|
||||
"@keyframes placeholder-opacity": {
|
||||
"0%": { opacity: 0.2 },
|
||||
"100%": { opacity: 1 },
|
||||
},
|
||||
// add delay
|
||||
animation: `placeholder-rainbow 8s infinite linear, placeholder-opacity 1.3s linear 0s infinite alternate none running}`,
|
||||
}}
|
||||
width="10px"
|
||||
height="8px"
|
||||
borderRadius="2px"
|
||||
cursor="pointer"
|
||||
{...props}
|
||||
></Flex>
|
||||
)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { useContext } from "react"
|
||||
|
||||
import { ThingtimeContext } from "~/Providers/ThingtimeProvider"
|
||||
import { ThingtimeContext } from "../../Providers/ThingtimeProvider"
|
||||
|
||||
const getGlobal = () => {
|
||||
try {
|
||||
@ -10,7 +10,7 @@ const getGlobal = () => {
|
||||
}
|
||||
}
|
||||
|
||||
export const useThingtime = (props?: any) => {
|
||||
export const useThingtime = (props?: any): any => {
|
||||
const value = useContext(ThingtimeContext)
|
||||
|
||||
// const { thingtime, setThingtime, getThingtime, thingtimeRef } = value
|
@ -1,6 +1,6 @@
|
||||
import { hydrate } from 'react-dom'
|
||||
import { hydrate } from "react-dom"
|
||||
// import { RemixBrowser } from "remix";
|
||||
import { RemixBrowser } from '@remix-run/react'
|
||||
import { RemixBrowser } from "@remix-run/react"
|
||||
try {
|
||||
window.process = {}
|
||||
} catch (err) {
|
21
app/src/entry.server.tsx
Normal file
21
app/src/entry.server.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { renderToString } from "react-dom/server"
|
||||
import { RemixServer } from "@remix-run/react"
|
||||
import type { EntryContext } from "@remix-run/react/dist/entry"
|
||||
|
||||
export default function handleRequest(
|
||||
request: Request,
|
||||
responseStatusCode: number,
|
||||
responseHeaders: Headers,
|
||||
remixContext: EntryContext
|
||||
) {
|
||||
const markup = renderToString(
|
||||
<RemixServer context={remixContext} url={request.url} />
|
||||
)
|
||||
|
||||
responseHeaders.set("Content-Type", "text/html")
|
||||
|
||||
return new Response("<!DOCTYPE html>" + markup, {
|
||||
status: responseStatusCode,
|
||||
headers: responseHeaders,
|
||||
})
|
||||
}
|
21
app/src/global-types.d.ts
vendored
Normal file
21
app/src/global-types.d.ts
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
// modify window / globalThis to support any properties
|
||||
// so there's no property does not exist on window typescript errors
|
||||
|
||||
// Path: app/src/global-types.d.ts
|
||||
declare global {
|
||||
interface Window {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
// Modify React component props/args to allow anything
|
||||
// so we don't get errors
|
||||
// Property 'fullPath' does not exist on type '{ children?: ReactNode; }'.
|
||||
|
||||
interface ForwardRefRenderFunction {
|
||||
T: any;
|
||||
(props: any, ref: React.Ref<any>): React.ReactElement | null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {};
|
8
app/src/gp/Sample.js
Normal file
8
app/src/gp/Sample.js
Normal file
@ -0,0 +1,8 @@
|
||||
export default class Sample {
|
||||
constructor({ x, y, progress, segment }) {
|
||||
this.x = x
|
||||
this.y = y
|
||||
this.progress = progress
|
||||
this.segment = segment
|
||||
}
|
||||
}
|
8
app/src/gp/Segment.js
Normal file
8
app/src/gp/Segment.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { getMiddleSample } from "./_utils"
|
||||
|
||||
export default class Segment {
|
||||
constructor({ samples }) {
|
||||
this.samples = samples
|
||||
this.progress = getMiddleSample(samples).progress
|
||||
}
|
||||
}
|
1
app/src/gp/_constants.js
Normal file
1
app/src/gp/_constants.js
Normal file
@ -0,0 +1 @@
|
||||
export const DEFAULT_PRECISION = 2
|
@ -1,7 +1,7 @@
|
||||
import Sample from './Sample';
|
||||
import Segment from './Segment';
|
||||
import { convertPathToNode } from './_utils';
|
||||
import { DEFAULT_PRECISION } from './_constants';
|
||||
import { DEFAULT_PRECISION } from "./_constants"
|
||||
import { convertPathToNode } from "./_utils"
|
||||
import Sample from "./Sample"
|
||||
import Segment from "./Segment"
|
||||
|
||||
// The main function responsible for getting data
|
||||
// This will take a path, number of samples, number of samples, and a precision value
|
||||
@ -11,34 +11,34 @@ export const getData = ({
|
||||
path,
|
||||
segments,
|
||||
samples,
|
||||
precision = DEFAULT_PRECISION
|
||||
precision = DEFAULT_PRECISION,
|
||||
}) => {
|
||||
// Convert the given path to a DOM node if it isn't already one
|
||||
path = convertPathToNode(path);
|
||||
path = convertPathToNode(path)
|
||||
|
||||
// We decrement the number of samples per segment because when we group them later we will add on the first sample of the following segment
|
||||
if (samples > 1) samples--;
|
||||
if (samples > 1) samples--
|
||||
|
||||
// Get total length of path, total number of samples we will be generating, and two blank arrays to hold samples and segments
|
||||
const pathLength = path.getTotalLength(),
|
||||
totalSamples = segments * samples,
|
||||
allSamples = [],
|
||||
allSegments = [];
|
||||
allSegments = []
|
||||
|
||||
// For the number of total samples, get the x, y, and progress values for each sample along the path
|
||||
for (let sample = 0; sample <= totalSamples; sample++) {
|
||||
const progress = sample / totalSamples;
|
||||
const progress = sample / totalSamples
|
||||
|
||||
let { x, y } = path.getPointAtLength(progress * pathLength);
|
||||
let { x, y } = path.getPointAtLength(progress * pathLength)
|
||||
|
||||
// If the user asks to round our x and y values, do so
|
||||
if (precision) {
|
||||
x = +x.toFixed(precision);
|
||||
y = +y.toFixed(precision);
|
||||
x = +x.toFixed(precision)
|
||||
y = +y.toFixed(precision)
|
||||
}
|
||||
|
||||
// Create a new Sample and push it onto the allSamples array
|
||||
allSamples.push(new Sample({ x, y, progress }));
|
||||
allSamples.push(new Sample({ x, y, progress }))
|
||||
}
|
||||
|
||||
// Out of all the samples gathered previously, sort them into groups of segments
|
||||
@ -46,23 +46,23 @@ export const getData = ({
|
||||
for (let segment = 0; segment < segments; segment++) {
|
||||
const currentStart = segment * samples,
|
||||
nextStart = currentStart + samples,
|
||||
segmentSamples = [];
|
||||
segmentSamples = []
|
||||
|
||||
// Push all current samples onto segmentSamples
|
||||
for (let samInSeg = 0; samInSeg < samples; samInSeg++) {
|
||||
segmentSamples.push(allSamples[currentStart + samInSeg]);
|
||||
segmentSamples.push(allSamples[currentStart + samInSeg])
|
||||
}
|
||||
|
||||
// Push the first sample from the next segment onto segmentSamples
|
||||
segmentSamples.push(allSamples[nextStart]);
|
||||
segmentSamples.push(allSamples[nextStart])
|
||||
|
||||
// Create a new Segment with the samples from segmentSamples
|
||||
allSegments.push(new Segment({ samples: segmentSamples }));
|
||||
allSegments.push(new Segment({ samples: segmentSamples }))
|
||||
}
|
||||
|
||||
// Return our group of segments
|
||||
return allSegments;
|
||||
};
|
||||
return allSegments
|
||||
}
|
||||
|
||||
// The function responsible for converting strokable data (from getData()) into fillable data
|
||||
// This allows any SVG path to be filled instead of just stroked, allowing for the user to fill and stroke paths simultaneously
|
||||
@ -73,10 +73,10 @@ export const strokeToFill = (data, width, precision, pathClosed) => {
|
||||
outlinedStrokes,
|
||||
precision,
|
||||
pathClosed
|
||||
);
|
||||
)
|
||||
|
||||
return averagedSegmentJoins;
|
||||
};
|
||||
return averagedSegmentJoins
|
||||
}
|
||||
|
||||
// An internal function for outlining stroked data
|
||||
const outlineStrokes = (data, width, precision) => {
|
||||
@ -85,53 +85,53 @@ const outlineStrokes = (data, width, precision) => {
|
||||
const p0 = new Sample({
|
||||
...startPoint,
|
||||
x: Math.sin(angle) * radius + startPoint.x,
|
||||
y: -Math.cos(angle) * radius + startPoint.y
|
||||
y: -Math.cos(angle) * radius + startPoint.y,
|
||||
}),
|
||||
p1 = new Sample({
|
||||
...startPoint,
|
||||
x: -Math.sin(angle) * radius + startPoint.x,
|
||||
y: Math.cos(angle) * radius + startPoint.y
|
||||
});
|
||||
y: Math.cos(angle) * radius + startPoint.y,
|
||||
})
|
||||
|
||||
// If the user asks to round our x and y values, do so
|
||||
if (precision) {
|
||||
p0.x = +p0.x.toFixed(precision);
|
||||
p0.y = +p0.y.toFixed(precision);
|
||||
p1.x = +p1.x.toFixed(precision);
|
||||
p1.y = +p1.y.toFixed(precision);
|
||||
p0.x = +p0.x.toFixed(precision)
|
||||
p0.y = +p0.y.toFixed(precision)
|
||||
p1.x = +p1.x.toFixed(precision)
|
||||
p1.y = +p1.y.toFixed(precision)
|
||||
}
|
||||
|
||||
return [p0, p1];
|
||||
};
|
||||
return [p0, p1]
|
||||
}
|
||||
|
||||
// We need to set the radius (half of the width) and have a holding array for outlined Segments
|
||||
const radius = width / 2,
|
||||
outlinedData = [];
|
||||
outlinedData = []
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
const samples = data[i].samples,
|
||||
segmentSamples = [];
|
||||
segmentSamples = []
|
||||
|
||||
// For each sample point and the following sample point (if there is one) compute the angle
|
||||
// Also compute the sample's various perpendicular points (with a distance of radius away from the sample point)
|
||||
for (let j = 0; j < samples.length; j++) {
|
||||
// If we're at the end of the segment and there are no further points, get outta here!
|
||||
if (samples[j + 1] === undefined) break;
|
||||
if (samples[j + 1] === undefined) break
|
||||
|
||||
const p0 = samples[j], // First point
|
||||
p1 = samples[j + 1], // Second point
|
||||
angle = Math.atan2(p1.y - p0.y, p1.x - p0.x), // Perpendicular angle to p0 and p1
|
||||
p0Perps = getPerpSamples(angle, radius, precision, p0), // Get perpedicular points with a distance of radius away from p0
|
||||
p1Perps = getPerpSamples(angle, radius, precision, p1); // Get perpedicular points with a distance of radius away from p1
|
||||
p1Perps = getPerpSamples(angle, radius, precision, p1) // Get perpedicular points with a distance of radius away from p1
|
||||
|
||||
// We only need the p0 perpendenciular points for the first sample
|
||||
// The p0 for j > 0 will always be the same as p1 anyhow, so let's not add redundant points
|
||||
if (j === 0) {
|
||||
segmentSamples.push(...p0Perps);
|
||||
segmentSamples.push(...p0Perps)
|
||||
}
|
||||
|
||||
// Always push the second sample point's perpendicular points
|
||||
segmentSamples.push(...p1Perps);
|
||||
segmentSamples.push(...p1Perps)
|
||||
}
|
||||
|
||||
// segmentSamples is out of order...
|
||||
@ -140,14 +140,14 @@ const outlineStrokes = (data, width, precision) => {
|
||||
new Segment({
|
||||
samples: [
|
||||
...segmentSamples.filter((s, i) => i % 2 === 0),
|
||||
...segmentSamples.filter((s, i) => i % 2 === 1).reverse()
|
||||
]
|
||||
...segmentSamples.filter((s, i) => i % 2 === 1).reverse(),
|
||||
],
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
return outlinedData;
|
||||
};
|
||||
return outlinedData
|
||||
}
|
||||
|
||||
// An internal function taking outlinedData (from outlineStrokes()) and averaging adjacent edges
|
||||
// If we didn't do this, our data would be fillable, but it would look stroked
|
||||
@ -156,17 +156,17 @@ const averageSegmentJoins = (outlinedData, precision, pathClosed) => {
|
||||
// Find the average x and y between two points (p0 and p1)
|
||||
const avg = (p0, p1) => ({
|
||||
x: (p0.x + p1.x) / 2,
|
||||
y: (p0.y + p1.y) / 2
|
||||
});
|
||||
y: (p0.y + p1.y) / 2,
|
||||
})
|
||||
|
||||
// Recombine the new x and y positions with all the other keys in the object
|
||||
const combine = (segment, pos, avg) => ({
|
||||
...segment[pos],
|
||||
x: avg.x,
|
||||
y: avg.y
|
||||
});
|
||||
y: avg.y,
|
||||
})
|
||||
|
||||
const init_outlinedData = JSON.parse(JSON.stringify(outlinedData)); //clone initial outlinedData Object
|
||||
const init_outlinedData = JSON.parse(JSON.stringify(outlinedData)) //clone initial outlinedData Object
|
||||
|
||||
for (let i = 0; i < outlinedData.length; i++) {
|
||||
// If path is closed: the current segment's samples;
|
||||
@ -186,34 +186,34 @@ const averageSegmentJoins = (outlinedData, precision, pathClosed) => {
|
||||
? outlinedData[i + 1].samples
|
||||
: init_outlinedData[0].samples,
|
||||
currentMiddle = currentSamples.length / 2, // The "middle" sample in the current segment's samples
|
||||
nextEnd = nextSamples.length - 1; // The last sample in the next segment's samples
|
||||
nextEnd = nextSamples.length - 1 // The last sample in the next segment's samples
|
||||
|
||||
// Average two sets of outlined samples to create p0Average and p1Average
|
||||
const p0Average = avg(currentSamples[currentMiddle - 1], nextSamples[0]),
|
||||
p1Average = avg(currentSamples[currentMiddle], nextSamples[nextEnd]);
|
||||
p1Average = avg(currentSamples[currentMiddle], nextSamples[nextEnd])
|
||||
|
||||
// If the user asks to round our x and y values, do so
|
||||
if (precision) {
|
||||
p0Average.x = +p0Average.x.toFixed(precision);
|
||||
p0Average.y = +p0Average.y.toFixed(precision);
|
||||
p1Average.x = +p1Average.x.toFixed(precision);
|
||||
p1Average.y = +p1Average.y.toFixed(precision);
|
||||
p0Average.x = +p0Average.x.toFixed(precision)
|
||||
p0Average.y = +p0Average.y.toFixed(precision)
|
||||
p1Average.x = +p1Average.x.toFixed(precision)
|
||||
p1Average.y = +p1Average.y.toFixed(precision)
|
||||
}
|
||||
|
||||
// Replace the previous values with new Samples
|
||||
currentSamples[currentMiddle - 1] = new Sample({
|
||||
...combine(currentSamples, currentMiddle - 1, p0Average)
|
||||
});
|
||||
...combine(currentSamples, currentMiddle - 1, p0Average),
|
||||
})
|
||||
currentSamples[currentMiddle] = new Sample({
|
||||
...combine(currentSamples, currentMiddle, p1Average)
|
||||
});
|
||||
...combine(currentSamples, currentMiddle, p1Average),
|
||||
})
|
||||
nextSamples[0] = new Sample({
|
||||
...combine(nextSamples, 0, p0Average)
|
||||
});
|
||||
...combine(nextSamples, 0, p0Average),
|
||||
})
|
||||
nextSamples[nextEnd] = new Sample({
|
||||
...combine(nextSamples, nextEnd, p1Average)
|
||||
});
|
||||
...combine(nextSamples, nextEnd, p1Average),
|
||||
})
|
||||
}
|
||||
|
||||
return outlinedData;
|
||||
};
|
||||
return outlinedData
|
||||
}
|
@ -15,13 +15,7 @@ export const svgElem = (type, attrs) => {
|
||||
}
|
||||
|
||||
// An internal function to help with the repetition of adding fill, stroke, and stroke-width attributes
|
||||
export const styleAttrs = (
|
||||
fill,
|
||||
stroke,
|
||||
strokeWidth,
|
||||
progress,
|
||||
animation
|
||||
) => {
|
||||
export const styleAttrs = (fill, stroke, strokeWidth, progress, animation) => {
|
||||
const determineColor = (type, progress) =>
|
||||
typeof type === "string" ? type : tinygradient(type).rgbAt(progress)
|
||||
|
2
app/src/gp/index.js
Normal file
2
app/src/gp/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { default as GradientPath } from "./GradientPath"
|
||||
export { getData, strokeToFill } from "./_data"
|
@ -1,6 +1,6 @@
|
||||
export const config = { runtime: 'edge' }
|
||||
export const config = { runtime: "edge" }
|
||||
|
||||
export default function Edge () {
|
||||
export default function Edge() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to Thingtime@Edge</h1>
|
@ -15,7 +15,7 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
"~/*": ["./src/*"]
|
||||
},
|
||||
|
||||
// Remix takes care of building everything in `remix build`.
|
||||
|
Loading…
Reference in New Issue
Block a user