Canvas CSS update

This commit is contained in:
Peaceultime 2024-08-29 17:45:43 +02:00
parent c694d28982
commit 2a8abb4796
11 changed files with 108 additions and 69 deletions

View File

@ -1,14 +1,9 @@
<style>
/* width */
::-webkit-scrollbar {
width: 12px;
height: 12px;
}
/* Track */
::-webkit-scrollbar-track {
}
/* Handle */
::-webkit-scrollbar-thumb {
@apply bg-light-40;
@apply dark:bg-dark-40;
@ -19,7 +14,6 @@
@apply bg-clip-padding;
}
/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
@apply bg-light-50;
@apply dark:bg-dark-50;

View File

@ -20,8 +20,8 @@ const isDark = computed({
</span>
<div class="
before:absolute before:top-0 before:left-0 before:right-0 before:bottom-0 before:opacity-0 before:block before:z-10
after:transition-all after:w-4 after:h-4 after:border after:border-light-30 dark:after:border-dark-30 after:block after:m-[3px] after:top-[-1px] after:pointer-events-none after:absolute after:left-0 after:bg-light-0 after:rounded-full after:translate-x-[1px] dark:after:translate-x-[26px]
inline-block relative cursor-pointer w-[50px] h-[22px] select-none rounded-full border border-light-30 dark:border-dark-30 bg-light-0 dark:bg-dark-30 dark:hover:border-dark-35" @click="isDark = !isDark"></div>
after:transition-all after:w-4 after:h-4 after:border after:border-light-30 dark:after:border-dark-30 after:block after:m-[3px] after:top-[-1px] after:pointer-events-none after:absolute after:left-0 after:bg-light-0 after:translate-x-[1px] dark:after:translate-x-[26px]
inline-block relative cursor-pointer w-[50px] h-[22px] select-none border border-light-35 dark:border-dark-35 bg-light-0 dark:bg-dark-30 dark:hover:border-dark-35" @click="isDark = !isDark"></div>
<span class="block dark:hidden absolute top-[3px] left-[22px] z-[1] px-[5px]">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-4 h-4 stroke-light-100 dark:stroke-dark-100 ">
<circle cx="12" cy="12" r="4"></circle>

View File

@ -1,43 +1,33 @@
<script setup lang="ts">
import type { CanvasColor } from "~/types/canvas";
type Direction = 'bottom' | 'top' | 'left' | 'right';
interface Props
{
path: {
path: string;
from: { x: number; y: number };
to: { x: number; y: number };
side: 'bottom' | 'top' | 'left' | 'right';
side: Direction;
};
color?: string;
color?: CanvasColor;
label?: string;
}
const props = defineProps<Props>();
const rotation = {
const rotation: Record<Direction, string> = {
top: "180",
bottom: "0",
left: "90",
right: "270"
};
function hexToRgb(hex: string): string {
return `${parseInt(hex.substring(1, 3), 16)}, ${parseInt(hex.substring(3, 5), 16)}, ${parseInt(hex.substring(5, 7), 16)}`;
}
const classes: any = { 'is-themed': props.color !== undefined, 'mod-canvas-color-custom': (props?.color?.startsWith('#') ?? false) };
if (props.color !== undefined) {
if (!props.color.startsWith('#'))
classes['mod-canvas-color-' + props.color] = true;
}
</script>
<template>
<g :class="classes"
:style="{ '--canvas-color': props?.color?.startsWith('#') ? hexToRgb(props.color) : undefined }">
<path class="canvas-display-path" :d="props.path.path"></path>
<g>
<path :style="`stroke-linecap: butt; stroke-width: calc(3px * var(--zoom-multiplier)); --canvas-color: ${color?.hex}`" :class="(color?.class ?? undefined) ?? ((color && color?.hex !== undefined) ? 'stroke-[var(--canvas-color)]' : 'stroke-light-40 dark:stroke-dark-40')" class="fill-none stroke-[4px]" :d="path.path"></path>
</g>
<g :class="classes"
:style="{ '--canvas-color': props?.color?.startsWith('#') ? hexToRgb(props.color) : undefined, transform: `translate(${props.path.to.x}px, ${props.path.to.y}px) rotate(${rotation[props.path.side]}deg)` }">
<polygon class="canvas-path-end" points="0,0 6.5,10.4 -6.5,10.4"></polygon>
<g :style="`transform: translate(${path.to.x}px, ${path.to.y}px) scale(var(--zoom-multiplier)) rotate(${rotation[path.side]}deg); --canvas-color: ${color?.hex}`">
<polygon :class="(color?.class ?? undefined) ?? ((color && color?.hex !== undefined) ? 'fill-[var(--canvas-color)]' : 'fill-light-40 dark:fill-dark-40')" points="0,0 6.5,10.4 -6.5,10.4"></polygon>
</g>
</template>

View File

@ -25,35 +25,20 @@ function darken(rgb: string): boolean
const props = defineProps<Props>();
const classes: Record<string, boolean> = { 'canvas-node-group': props.node.type === 'group', 'is-themed': props.node.color !== undefined, 'mod-canvas-color-custom': (props.node?.color?.startsWith('#') ?? false) };
const size = Math.max(props.node.width, props.node.height);
if(props.node.color !== undefined)
{
if (!props.node.color.startsWith('#'))
classes['mod-canvas-color-' + props.node.color] = true;
}
</script>
<template>
<div class="canvas-node" :class="classes"
:style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`, '--canvas-node-width': `${node.width}px`, '--canvas-node-height': `${node.height}px`, '--canvas-color': node?.color?.startsWith('#') ? hexToRgb(node.color) : undefined}">
<div class="canvas-node-container">
<div class="absolute" :style="{transform: `translate(${node.x}px, ${node.y}px)`, width: `${node.width}px`, height: `${node.height}px`}">
<div :class="{'z-0': node.type === 'group', 'z-[2]': node.type !== 'group'}" class="border-2 border-light-40 dark:border-dark-40 bg-light-20 dark:bg-dark-20 overflow-hidden contain-strict w-full h-full py-2 px-4 flex">
<template v-if="node.type === 'group' || zoom > Math.min(0.38, 1000 / size)">
<div class="canvas-node-content markdown-embed">
<div v-if="node.text?.length > 0" class="markdown-embed-content node-insert-event" style="">
<div
class="markdown-preview-view markdown-rendered node-insert-event show-indentation-guide allow-fold-headings allow-fold-lists">
<div class="markdown-preview-sizer markdown-preview-section">
<div v-if="node.text?.length > 0" class="flex items-center">
<Markdown v-model="node.text" />
</div>
</div>
</div>
</div>
</template>
<template v-else>
<div class="canvas-node-placeholder">
<div class="canvas-icon-placeholder">
<div class="flex flex-1 justify-center items-center bg-light-30 dark:bg-dark-30">
<div class="">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="svg-icon lucide-align-left">
@ -65,8 +50,6 @@ if(props.node.color !== undefined)
</div>
</template>
</div>
<ClientOnly><div v-if="node.type === 'group' && node.label !== undefined" class="canvas-group-label"
:class="{ 'mod-foreground-dark': darken(getColor(props.node?.color ?? '')), 'mod-foreground-light': !darken(getColor(props.node?.color ?? ''))}">
{{ node.label }}</div></ClientOnly>
<div v-if="node.type === 'group' && node.label !== undefined" style="max-width: 100%; font-size: calc(16px * var(--zoom-multiplier))" class="origin-bottom-left tracking-wider truncate inline-block bg-light-40 dark:bg-dark-40 text-light-100 dark:text-dark-100 absolute bottom-[100%] mb-2 px-2 py-1 font-thin">{{ node.label }}</div>
</div>
</template>

View File

@ -47,7 +47,7 @@ const onResize = (event?: Event) => {
maxX.value = _maxX = _maxX + 32;
maxY.value = _maxY = _maxY + 32;
minZoom.value = Math.min((canvas.value?.clientWidth ?? 1920) / (_maxX - _minX), (canvas.value?.clientHeight ?? 1080) / (_maxY - _minY)) * 0.9;
minZoom.value = Math.min((canvas.value?.clientWidth ?? 1920) / (_maxX - _minX), (canvas.value?.clientHeight ?? 1080) / (_maxY - _minY), 0.01) * 0.9;
zoom.value = clamp(zoom.value, minZoom.value, 3);
bbox.value = (canvas.value ?? document.getElementById('canvas'))?.getBoundingClientRect();
@ -216,7 +216,7 @@ function getCenter(n: { x: number, y: number }, i: { x: number, y: number }, r:
<Suspense>
<template #default>
<div id="canvas" ref="canvas" @pointerdown="onPointerDown" @wheel.passive="onWheel" @touchstart.passive="onTouchStart"
@dragstart.prevent="" class="relative overflow-hidden"
@dragstart.prevent="" class="absolute top-0 left-0 overflow-hidden w-full h-full"
:style="{ '--zoom-multiplier': (1 / Math.pow(zoom, 0.7)) }">
<div class="border border-light-35 dark:border-dark-35 bg-light-10 dark:bg-dark-10 absolute top-2 left-2 z-[100] overflow-hidden">
<div @click="zoom = clamp(zoom * 1.1, minZoom, 3)" class="w-8 h-8 flex justify-center items-center p-2 hover:bg-light-30 dark:hover:bg-dark-30 cursor-pointer"
@ -257,18 +257,18 @@ function getCenter(n: { x: number, y: number }, i: { x: number, y: number }, r:
</svg>
</div>
</div>
<div class="canvas"
<div class="absolute top-0 left-0 w-full h-full origin-top pointer-events-none z-10"
:style="{transform: `translate(${centerX}px, ${centerY}px) scale(${zoom}) translate(${dispX}px, ${dispY}px)`}">
<svg class="canvas-edges">
<svg class="absolute top-0 left-0 overflow-visible w-full h-full origin-top pointer-events-none z-[1]">
<CanvasEdge v-for="edge of props.canvas.edges" :key="edge.id"
:path="path(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide)"
:color="edge.color" :label="edge.label" />
</svg>
<CanvasNode v-for="node of props.canvas.nodes" :key="node.id" :node="node" :zoom="zoom" />
<template v-for="edge of props.canvas.edges">
<div :key="edge.id" v-if="edge.label" class="canvas-path-label-wrapper"
<div :key="edge.id" v-if="edge.label" class="absolute z-10"
:style="{ transform: labelCenter(getNode(edge.fromNode)!, edge.fromSide, getNode(edge.toNode)!, edge.toSide) }">
<div class="canvas-path-label">{{ edge.label }}</div>
<div class="relative bg-light-20 dark:bg-dark-20 border border-light-35 dark:border-dark-35 px-4 py-2 -translate-x-[50%] -translate-y-[50%]">{{ edge.label }}</div>
</div>
</template>
</div>
@ -279,3 +279,33 @@ function getCenter(n: { x: number, y: number }, i: { x: number, y: number }, r:
</template>
</Suspense>
</template>
<style>
.useless
{
@apply fill-light-red;
@apply dark:fill-dark-red;
@apply stroke-light-red;
@apply dark:stroke-dark-red;
@apply fill-light-orange;
@apply dark:fill-dark-orange;
@apply stroke-light-orange;
@apply dark:stroke-dark-orange;
@apply fill-light-yellow;
@apply dark:fill-dark-yellow;
@apply stroke-light-yellow;
@apply dark:stroke-dark-yellow;
@apply fill-light-green;
@apply dark:fill-dark-green;
@apply stroke-light-green;
@apply dark:stroke-dark-green;
@apply fill-light-cyan;
@apply dark:fill-dark-cyan;
@apply stroke-light-cyan;
@apply dark:stroke-dark-cyan;
@apply fill-light-purple;
@apply dark:fill-dark-purple;
@apply stroke-light-purple;
@apply dark:stroke-dark-purple;
}
</style>

BIN
db.sqlite

Binary file not shown.

View File

@ -17,7 +17,7 @@
<p>Copyright Peaceultime - 2024</p>
</div>
</div>
<div class="flex-1 flex items-baseline overflow-auto">
<div class="flex-1 flex items-baseline overflow-auto p-8 relative">
<slot></slot>
</div>
</template>

View File

@ -19,7 +19,7 @@
<p>Copyright Peaceultime - 2024</p>
</div>
</div>
<div class="flex-1 flex items-baseline overflow-auto">
<div class="flex-1 flex items-baseline overflow-auto p-8 relative">
<slot></slot>
</div>
</template>

View File

@ -9,6 +9,7 @@ export default defineNuxtConfig({
}
},
tailwindcss: {
viewer: false,
config: {
theme: {
extend: {

View File

@ -1,8 +1,10 @@
import useDatabase from "~/composables/useDatabase";
import { extname, basename } from 'node:path';
import type { File } from '~/types/api';
import type { File, FileType } from '~/types/api';
import { InputTypeHTMLAttribute } from "vue";
import { CanvasColor, CanvasContent } from "~/types/canvas";
const typeMapping: Record<string, string> = {
const typeMapping: Record<string, FileType> = {
".md": "Markdown",
".canvas": "Canvas"
}
@ -36,7 +38,7 @@ export default defineTask({
order: order && order[1] ? order[1] : 50,
title: order && order[2] ? order[2] : title,
type: 'Folder',
content: undefined
content: null
}
}
@ -44,13 +46,14 @@ export default defineTask({
const title = basename(e.path, extension);
const order = /(\d+)\. ?(.+)/gsmi.exec(title);
const path = (e.path as string).split('/').map(f => { const check = /(\d+)\. ?(.+)/gsmi.exec(f); return check && check[2] ? check[2] : f }).join('/');
const content = (await $fetch(`https://git.peaceultime.com/api/v1/repos/peaceultime/system-aspect/raw/${encodeURIComponent(e.path)}`));
return {
path: (extension === '.md' ? path.replace(extension, '') : path).toLowerCase().replaceAll(" ", "-").normalize("NFD").replace(/[\u0300-\u036f]/g, ""),
order: order && order[1] ? order[1] : 50,
title: order && order[2] ? order[2] : title,
type: (typeMapping[extension] ?? 'File'),
content: await $fetch(`https://git.peaceultime.com/api/v1/repos/peaceultime/system-aspect/raw/${encodeURIComponent(e.path)}`)
content: reshapeContent(content as string, typeMapping[extension] ?? 'File')
}
}));
@ -101,3 +104,36 @@ export default defineTask({
}
},
})
function reshapeContent(content: string, type: FileType): string | null
{
switch(type)
{
case "Markdown":
case "File":
return content;
case "Canvas":
const data = JSON.parse(content) as CanvasContent;
data.edges.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined);
data.nodes.forEach(e => e.color = typeof e.color === 'string' ? getColor(e.color) : undefined);
return JSON.stringify(data);
default:
case 'Folder':
return null;
}
}
function getColor(color: string): CanvasColor
{
const colors: Record<string, string> = {
'1': 'fill-light-red dark:fill-dark-red stroke-light-red dark:stroke-dark-red',
'2': 'fill-light-orange dark:fill-dark-orange stroke-light-orange dark:stroke-dark-orange',
'3': 'fill-light-yellow dark:fill-dark-yellow stroke-light-yellow dark:stroke-dark-yellow',
'4': 'fill-light-green dark:fill-dark-green stroke-light-green dark:stroke-dark-green',
'5': 'fill-light-cyan dark:fill-dark-cyan stroke-light-cyan dark:stroke-dark-cyan',
'6': 'fill-light-purple dark:fill-dark-purple stroke-light-purple dark:stroke-dark-purple',
};
if(colors.hasOwnProperty(color))
return { class: colors[color] };
else
return { hex: color };
}

9
types/canvas.d.ts vendored
View File

@ -3,6 +3,11 @@ export interface CanvasContent {
edges: CanvasEdge[];
groups: CanvasGroup[];
}
export type CanvasColor = {
class?: string;
} & {
hex?: string;
}
export interface CanvasNode {
type: 'group' | 'text';
id: string;
@ -10,7 +15,7 @@ export interface CanvasNode {
y: number;
width: number;
height: number;
color?: string;
color?: CanvasColor;
label?: string;
text?: any;
};
@ -20,7 +25,7 @@ export interface CanvasEdge {
fromSide: 'bottom' | 'top' | 'left' | 'right';
toNode: string;
toSide: 'bottom' | 'top' | 'left' | 'right';
color?: string;
color?: CanvasColor;
label?: string;
};
export interface CanvasGroup {