Sono andato avanti.

This commit is contained in:
Mancee28
2026-03-21 00:46:59 +01:00
parent 173afd3224
commit 53513ac8c2
12 changed files with 255 additions and 64 deletions

View File

@@ -1 +1 @@
- Puoi modificare direttamente i tag e lo stile di componenti e pagine.
- Puoi modificare direttamente i tag e lo stile di componenti e pagine, non puoi modificare la sezione di codice spontaneamente.

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="8.5" stroke="#222222"/>
<path d="M16.2426 7.75736C15.1174 6.63214 13.5913 6 12 6C10.4087 6 8.88258 6.63214 7.75736 7.75736C6.63214 8.88258 6 10.4087 6 12C6 13.5913 6.63214 15.1174 7.75736 16.2426L12 12L16.2426 7.75736Z" fill="#222222"/>
</svg>

After

Width:  |  Height:  |  Size: 491 B

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import NoteCard from '$lib/components/board/Note.svelte';
import NoteCard from '$lib/components/board/NoteCard.svelte';
import type { BoardNote } from '$lib/components/board/script/types';
import { BOARD_GRID_SIZE, BOARD_HEIGHT, BOARD_WIDTH } from '$lib/components/board/script/constants';
@@ -15,7 +15,6 @@
<section
class="board"
role="application"
aria-label="Bacheca note"
onpointerdown={onStartPan}
style={`--board-width:${BOARD_WIDTH}px; --board-height:${BOARD_HEIGHT}px; --grid-size:${BOARD_GRID_SIZE}px;`}
>
@@ -42,8 +41,8 @@
height: var(--board-height);
padding: 90px 30px 60px;
background-image:
linear-gradient(to right, rgba(140, 155, 176, 0.14) 1px, transparent 1px),
linear-gradient(to bottom, rgba(140, 155, 176, 0.14) 1px, transparent 1px);
linear-gradient(to right, var(--grid-line, rgba(140, 155, 176, 0.14)) 1px, transparent 1px),
linear-gradient(to bottom, var(--grid-line, rgba(140, 155, 176, 0.14)) 1px, transparent 1px);
background-size: var(--grid-size) var(--grid-size);
}

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import type { Note } from '$lib/components/editor/script/editor';
import type { Note } from '$lib/components/editor/script/types';
type Props = {
note: Note;

View File

@@ -1,7 +1,7 @@
export const BOARD_WIDTH = 5400;
export const BOARD_HEIGHT = 5400;
export const BOARD_GRID_SIZE = 16;
export const BOARD_GRID_SIZE = 32;
export const NOTE_CARD_WIDTH = 280;
export const NOTE_CARD_MIN_HEIGHT = 140;

View File

@@ -0,0 +1,62 @@
<script lang="ts">
import { palette } from "./script/constant";
type Props = {
selectedColor: string;
onSelect(color: string): void;
};
let { selectedColor , onSelect }: Props = $props();
</script>
<div class="color-tooltip" role="dialog" aria-label="Seleziona un colore">
{#each palette as color (color)}
<button
type="button"
class="color-dot"
class:selected={selectedColor === color}
style={`background:${color};`}
aria-label={`Colore ${color}`}
title={color}
onclick={() => {
selectedColor = color;
onSelect(color);
}}
></button>
{/each}
</div>
<style>
.color-tooltip {
position: absolute;
left: 0;
bottom: calc(100% + 8px);
display: flex;
align-items: center;
gap: 8px;
padding: 8px 10px;
border-radius: 14px;
border: 1px solid #e0e0e0;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 20;
}
.color-dot {
width: 28px;
height: 28px;
border-radius: 999px;
border: 1px solid rgba(0, 0, 0, 0.12);
cursor: pointer;
transition: transform 0.12s ease;
}
.color-dot:hover {
transform: scale(1.08);
}
.color-dot.selected {
outline: 2px solid #5f6368;
outline-offset: 1px;
}
</style>

View File

@@ -5,28 +5,19 @@
import imageSvg from '$lib/assets/composer/image.svg';
import fileSvg from '$lib/assets/composer/file.svg';
import paletteSvg from '$lib/assets/composer/palette.svg';
import { palette } from './script/constant';
import ColorSelector from './ColorSelector.svelte';
type Props = {
note?: Note;
note: Note;
onSubmit: (note: Note) => void;
onClose?: () => void;
onClose: () => void;
};
let { note, onSubmit, onClose }: Props = $props();
let showColorSelector = $state(false);
let colorSelectorWrapper: HTMLDivElement | undefined = $state();
onMount(() => {
if (!note) {
note = {
id: nanoid(),
title: '',
content: '',
color: palette[0],
date: new Date(),
images: [],
files: []
};
}
return () => {};
});
@@ -37,11 +28,32 @@
onSubmit({ ...note, date: new Date() });
}
}
onClose?.();
onClose();
};
const handleColorSelectorClick = (e : MouseEvent & {currentTarget: EventTarget & Window}) => {
if (!showColorSelector || !colorSelectorWrapper) return;
if (!colorSelectorWrapper.contains(e.target as Node)) {
showColorSelector = false;
}
}
const handleColorSelectorKeydown = (e: KeyboardEvent & {currentTarget: EventTarget & Window} ) => {
if (e.key === 'Escape') {
showColorSelector = false;
}
}
const onColorSelect = (color: string) => {
note = { ...note, color };
}
</script>
<svelte:window />
<svelte:window
onkeydown={handleColorSelectorKeydown}
onclick={handleColorSelectorClick}
/>
{#if note}
<section class="note-editor" style={`background: ${note.color};`}>
@@ -62,14 +74,25 @@
<footer class="editor-footer">
<div class="left-actions" aria-label="Azioni nota">
<button type="button" class="icon-btn" aria-label="Sfondo colore">
<img src={paletteSvg} alt="" class="icon-svg" />
<div class="color-selector-wrapper" bind:this={colorSelectorWrapper}>
<button
type="button"
class="icon-btn"
aria-label="Sfondo colore"
aria-expanded={showColorSelector}
onclick={() => (showColorSelector = !showColorSelector)}
>
<img src={paletteSvg} alt="Palette" class="icon-svg" />
</button>
{#if showColorSelector}
<ColorSelector selectedColor={note.color} onSelect={onColorSelect}/>
{/if}
</div>
<button type="button" class="icon-btn" aria-label="Immagine">
<img src={imageSvg} alt="" class="icon-svg" />
</button>
<button type="button" class="icon-btn" aria-label="File">
<img src={fileSvg} alt="" class="icon-svg" />
<img src={fileSvg} alt="File" class="icon-svg" />
</button>
</div>
@@ -134,7 +157,8 @@
align-items: center;
justify-content: space-between;
gap: 12px;
padding-top: 4px;
padding-top: 10px;
min-height: 44px;
}
.left-actions {
@@ -144,9 +168,13 @@
gap: 2px;
}
.color-selector-wrapper {
position: relative;
}
.icon-btn {
width: 30px;
height: 30px;
width: 34px;
height: 34px;
border: 0;
border-radius: 999px;
background: transparent;
@@ -157,8 +185,8 @@
}
.icon-svg {
width: 18px;
height: 18px;
width: 20px;
height: 20px;
display: block;
object-fit: contain;
pointer-events: none;
@@ -175,14 +203,14 @@
}
.close-btn {
height: 32px;
height: 36px;
border: 0;
border-radius: 6px;
background: transparent;
color: #202124;
font-size: 1.45rem;
font-size: 1.55rem;
font-weight: 500;
padding: 0 10px;
padding: 0 12px;
cursor: pointer;
}

View File

@@ -1 +1,27 @@
export const palette = ['#ffffff', '#fef3c7', '#dbeafe', '#dcfce7', '#fee2e2', '#f3e8ff'];
import { nanoid } from "nanoid";
import type { Note } from "./types";
export const palette = [
'#ffffff',
'#f28b82',
'#f6a04d',
'#fff475',
'#ccff90',
'#a7ffeb',
'#cbf0f8',
'#aecbfa',
'#d7aefb',
'#fdcfe8',
'#e6c9a8',
'#e8eaed'
];
export const createEmptyNote = (): Note => ({
id: nanoid(),
title: '',
content: '',
color: palette[0],
date: new Date(),
images: [],
files: []
});

View File

@@ -0,0 +1,52 @@
<script lang="ts">
import colorMode from '$lib/assets/ui/color-mode.svg';
type Props = {
onToggle: () => void
}
let { onToggle }: Props = $props();
</script>
<button
class="theme-toggle"
type="button"
onclick={onToggle}
>
<img src={colorMode} alt="" aria-hidden="true" class="theme-toggle-icon" />
</button>
<style>
.theme-toggle {
position: fixed;
right: 16px;
bottom: 16px;
z-index: 100;
display: inline-flex;
align-items: center;
gap: 8px;
height: 40px;
padding: 0 12px;
border-radius: 10px;
border: 1px solid var(--theme-toggle-border);
background: var(--theme-toggle-bg);
color: var(--theme-toggle-text);
font-size: 1.3rem;
font-weight: 600;
letter-spacing: 0.01em;
cursor: pointer;
backdrop-filter: blur(6px);
box-shadow: var(--theme-toggle-shadow);
}
.theme-toggle:hover {
transform: translateY(-1px);
}
.theme-toggle-icon {
width: 18px;
height: 18px;
display: block;
}
</style>

View File

@@ -15,10 +15,10 @@
const minimapHeight = (MINIMAP_WIDTH * BOARD_HEIGHT) / BOARD_WIDTH;
const viewportLeftPercent = $derived((viewport.scrollLeft / BOARD_WIDTH) * 100);
const viewportTopPercent = $derived((viewport.scrollTop / BOARD_HEIGHT) * 100);
const viewportWidthPercent = $derived((viewport.width / BOARD_WIDTH) * 100);
const viewportHeightPercent = $derived((viewport.height / BOARD_HEIGHT) * 100);
let viewportLeftPercent = $derived((viewport.scrollLeft / BOARD_WIDTH) * 100);
let viewportTopPercent = $derived((viewport.scrollTop / BOARD_HEIGHT) * 100);
let viewportWidthPercent = $derived((viewport.width / BOARD_WIDTH) * 100);
let viewportHeightPercent = $derived((viewport.height / BOARD_HEIGHT) * 100);
const navigateFromPointer = (event: PointerEvent) => {
const element = event.currentTarget as HTMLElement;
@@ -55,7 +55,7 @@
{#each notes as note (note.id)}
<span
class="note-dot"
style={`left:${(note.x / BOARD_WIDTH) * 100}%; top:${(note.y / BOARD_HEIGHT) * 100}%;`}
style={`left:${(note.x / BOARD_WIDTH) * 100}%; top:${(note.y / BOARD_HEIGHT) * 100}%; background:${note.color};`}
></span>
{/each}
@@ -74,7 +74,7 @@
z-index: 90;
padding: 6px;
border-radius: 10px;
background: rgba(255, 255, 255, 0.68);
background: var(--minimap-shell-bg, rgba(255, 255, 255, 0.68));
backdrop-filter: blur(5px);
box-shadow: 0 4px 14px rgba(32, 33, 36, 0.16);
}
@@ -87,10 +87,10 @@
overflow: hidden;
cursor: pointer;
background-image:
linear-gradient(to right, rgba(140, 155, 176, 0.14) 1px, transparent 1px),
linear-gradient(to bottom, rgba(140, 155, 176, 0.14) 1px, transparent 1px);
linear-gradient(to right, var(--grid-line, rgba(140, 155, 176, 0.14)) 1px, transparent 1px),
linear-gradient(to bottom, var(--grid-line, rgba(140, 155, 176, 0.14)) 1px, transparent 1px);
background-size: 10px 10px;
background-color: rgba(234, 239, 245, 0.92);
background-color: var(--minimap-board-bg, rgba(234, 239, 245, 0.92));
}
.note-dot {
@@ -98,14 +98,14 @@
width: 5px;
height: 5px;
border-radius: 999px;
background: rgba(50, 84, 122, 0.76);
border: 1px solid rgba(17, 24, 39, 0.3);
transform: translate(-50%, -50%);
}
.viewport-frame {
position: absolute;
border: 1px solid rgba(17, 80, 145, 0.9);
background: rgba(63, 134, 206, 0.12);
border: 1px solid var(--minimap-frame-border, rgba(17, 80, 145, 0.9));
background: var(--minimap-frame-bg, rgba(63, 134, 206, 0.12));
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.75);
pointer-events: none;
}

View File

@@ -1,6 +1,7 @@
<script lang="ts">
import type { Note } from '$lib/components/editor/script/types';
import NoteEditor from '../editor/NoteEditor.svelte';
import { createEmptyNote } from '../editor/script/constant';
type Props = {
onCreate: (note: Note) => void;
@@ -9,6 +10,7 @@
let { onCreate }: Props = $props();
let expanded = $state(false);
let editorNote = $state<Note>(createEmptyNote());
</script>
<svelte:window
@@ -21,28 +23,35 @@
/>
{#if !expanded}
<button class="note-navbar-trigger" type="button" onclick={() => (expanded = true)}>
<button
class="note-navbar-trigger"
type="button"
onclick={() => {
editorNote = createEmptyNote();
expanded = true;
}}
>
Scrivi una nota...
</button>
{:else}
<div class="note-editor-container">
<NoteEditor note={undefined} onSubmit={onCreate} onClose={() => (expanded = false)} />
<NoteEditor note={editorNote} onSubmit={onCreate} onClose={() => (expanded = false)} />
</div>
{/if}
<style>
.note-navbar-trigger {
width: 100%;
min-height: 52px;
border: 1px solid #d3d3d3;
min-height: 46px;
border: 1px solid var(--surface-border, #d3d3d3);
border-radius: 10px;
background: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
padding: 0 18px;
background: var(--surface, #fff);
box-shadow: var(--surface-shadow, 0 1px 3px rgba(0, 0, 0, 0.18));
padding: 0 16px;
text-align: left;
font-size: 1.95rem;
font-size: 1.75rem;
line-height: 1;
color: #5f6368;
color: var(--text-muted, #5f6368);
cursor: text;
transition:
box-shadow 0.15s ease,
@@ -51,7 +60,7 @@
.note-navbar-trigger:hover,
.note-navbar-trigger:focus-visible {
border-color: #c6c6c6;
border-color: color-mix(in srgb, var(--surface-border, #d3d3d3) 82%, #ffffff 18%);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
outline: none;
}

View File

@@ -33,10 +33,6 @@
};
};
onMount(() => {
updateViewportMetrics();
});
const handleCreate = (note: Note) => {
const pos = getRandomPosition(notes.length);
maxZ += 1;
@@ -111,6 +107,10 @@
viewportEl.scrollTop = clamp(scrollTop, 0, BOARD_HEIGHT - viewportEl.clientHeight);
updateViewportMetrics();
};
onMount(() => {
updateViewportMetrics();
});
</script>
<svelte:window
@@ -147,10 +147,19 @@
}
.board-viewport {
--app-bg: radial-gradient(circle at top right, #f8fbff, #eef2f7 45%, #e6ebf2 100%);
--surface: #ffffff;
--surface-border: #d3d3d3;
--surface-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
--text-muted: #5f6368;
--grid-line: rgba(140, 155, 176, 0.14);
--minimap-shell-bg: rgba(255, 255, 255, 0.68);
--minimap-board-bg: rgba(234, 239, 245, 0.92);
--minimap-frame-border: rgba(17, 80, 145, 0.9);
--minimap-frame-bg: rgba(63, 134, 206, 0.12);
height: 100vh;
overflow: auto;
background:
radial-gradient(circle at top right, #f8fbff, #eef2f7 45%, #e6ebf2 100%);
background: var(--app-bg);
scrollbar-width: none;
-ms-overflow-style: none;
cursor: grab;
@@ -180,4 +189,5 @@
margin: 0 auto;
pointer-events: auto;
}
</style>