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"> <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 type { BoardNote } from '$lib/components/board/script/types';
import { BOARD_GRID_SIZE, BOARD_HEIGHT, BOARD_WIDTH } from '$lib/components/board/script/constants'; import { BOARD_GRID_SIZE, BOARD_HEIGHT, BOARD_WIDTH } from '$lib/components/board/script/constants';
@@ -15,7 +15,6 @@
<section <section
class="board" class="board"
role="application" role="application"
aria-label="Bacheca note"
onpointerdown={onStartPan} onpointerdown={onStartPan}
style={`--board-width:${BOARD_WIDTH}px; --board-height:${BOARD_HEIGHT}px; --grid-size:${BOARD_GRID_SIZE}px;`} 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); height: var(--board-height);
padding: 90px 30px 60px; padding: 90px 30px 60px;
background-image: background-image:
linear-gradient(to right, 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, 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); background-size: var(--grid-size) var(--grid-size);
} }

View File

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

View File

@@ -1,7 +1,7 @@
export const BOARD_WIDTH = 5400; export const BOARD_WIDTH = 5400;
export const BOARD_HEIGHT = 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_WIDTH = 280;
export const NOTE_CARD_MIN_HEIGHT = 140; 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 imageSvg from '$lib/assets/composer/image.svg';
import fileSvg from '$lib/assets/composer/file.svg'; import fileSvg from '$lib/assets/composer/file.svg';
import paletteSvg from '$lib/assets/composer/palette.svg'; import paletteSvg from '$lib/assets/composer/palette.svg';
import { palette } from './script/constant'; import ColorSelector from './ColorSelector.svelte';
type Props = { type Props = {
note?: Note; note: Note;
onSubmit: (note: Note) => void; onSubmit: (note: Note) => void;
onClose?: () => void; onClose: () => void;
}; };
let { note, onSubmit, onClose }: Props = $props(); let { note, onSubmit, onClose }: Props = $props();
let showColorSelector = $state(false);
let colorSelectorWrapper: HTMLDivElement | undefined = $state();
onMount(() => { onMount(() => {
if (!note) {
note = {
id: nanoid(),
title: '',
content: '',
color: palette[0],
date: new Date(),
images: [],
files: []
};
}
return () => {}; return () => {};
}); });
@@ -37,11 +28,32 @@
onSubmit({ ...note, date: new Date() }); 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> </script>
<svelte:window /> <svelte:window
onkeydown={handleColorSelectorKeydown}
onclick={handleColorSelectorClick}
/>
{#if note} {#if note}
<section class="note-editor" style={`background: ${note.color};`}> <section class="note-editor" style={`background: ${note.color};`}>
@@ -62,14 +74,25 @@
<footer class="editor-footer"> <footer class="editor-footer">
<div class="left-actions" aria-label="Azioni nota"> <div class="left-actions" aria-label="Azioni nota">
<button type="button" class="icon-btn" aria-label="Sfondo colore"> <div class="color-selector-wrapper" bind:this={colorSelectorWrapper}>
<img src={paletteSvg} alt="" class="icon-svg" /> <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> </button>
{#if showColorSelector}
<ColorSelector selectedColor={note.color} onSelect={onColorSelect}/>
{/if}
</div>
<button type="button" class="icon-btn" aria-label="Immagine"> <button type="button" class="icon-btn" aria-label="Immagine">
<img src={imageSvg} alt="" class="icon-svg" /> <img src={imageSvg} alt="" class="icon-svg" />
</button> </button>
<button type="button" class="icon-btn" aria-label="File"> <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> </button>
</div> </div>
@@ -134,7 +157,8 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 12px; gap: 12px;
padding-top: 4px; padding-top: 10px;
min-height: 44px;
} }
.left-actions { .left-actions {
@@ -144,9 +168,13 @@
gap: 2px; gap: 2px;
} }
.color-selector-wrapper {
position: relative;
}
.icon-btn { .icon-btn {
width: 30px; width: 34px;
height: 30px; height: 34px;
border: 0; border: 0;
border-radius: 999px; border-radius: 999px;
background: transparent; background: transparent;
@@ -157,8 +185,8 @@
} }
.icon-svg { .icon-svg {
width: 18px; width: 20px;
height: 18px; height: 20px;
display: block; display: block;
object-fit: contain; object-fit: contain;
pointer-events: none; pointer-events: none;
@@ -175,14 +203,14 @@
} }
.close-btn { .close-btn {
height: 32px; height: 36px;
border: 0; border: 0;
border-radius: 6px; border-radius: 6px;
background: transparent; background: transparent;
color: #202124; color: #202124;
font-size: 1.45rem; font-size: 1.55rem;
font-weight: 500; font-weight: 500;
padding: 0 10px; padding: 0 12px;
cursor: pointer; 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 minimapHeight = (MINIMAP_WIDTH * BOARD_HEIGHT) / BOARD_WIDTH;
const viewportLeftPercent = $derived((viewport.scrollLeft / BOARD_WIDTH) * 100); let viewportLeftPercent = $derived((viewport.scrollLeft / BOARD_WIDTH) * 100);
const viewportTopPercent = $derived((viewport.scrollTop / BOARD_HEIGHT) * 100); let viewportTopPercent = $derived((viewport.scrollTop / BOARD_HEIGHT) * 100);
const viewportWidthPercent = $derived((viewport.width / BOARD_WIDTH) * 100); let viewportWidthPercent = $derived((viewport.width / BOARD_WIDTH) * 100);
const viewportHeightPercent = $derived((viewport.height / BOARD_HEIGHT) * 100); let viewportHeightPercent = $derived((viewport.height / BOARD_HEIGHT) * 100);
const navigateFromPointer = (event: PointerEvent) => { const navigateFromPointer = (event: PointerEvent) => {
const element = event.currentTarget as HTMLElement; const element = event.currentTarget as HTMLElement;
@@ -55,7 +55,7 @@
{#each notes as note (note.id)} {#each notes as note (note.id)}
<span <span
class="note-dot" 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> ></span>
{/each} {/each}
@@ -74,7 +74,7 @@
z-index: 90; z-index: 90;
padding: 6px; padding: 6px;
border-radius: 10px; 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); backdrop-filter: blur(5px);
box-shadow: 0 4px 14px rgba(32, 33, 36, 0.16); box-shadow: 0 4px 14px rgba(32, 33, 36, 0.16);
} }
@@ -87,10 +87,10 @@
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
background-image: background-image:
linear-gradient(to right, 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, 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-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 { .note-dot {
@@ -98,14 +98,14 @@
width: 5px; width: 5px;
height: 5px; height: 5px;
border-radius: 999px; border-radius: 999px;
background: rgba(50, 84, 122, 0.76); border: 1px solid rgba(17, 24, 39, 0.3);
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }
.viewport-frame { .viewport-frame {
position: absolute; position: absolute;
border: 1px solid rgba(17, 80, 145, 0.9); border: 1px solid var(--minimap-frame-border, rgba(17, 80, 145, 0.9));
background: rgba(63, 134, 206, 0.12); background: var(--minimap-frame-bg, rgba(63, 134, 206, 0.12));
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.75); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.75);
pointer-events: none; pointer-events: none;
} }

View File

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

View File

@@ -33,10 +33,6 @@
}; };
}; };
onMount(() => {
updateViewportMetrics();
});
const handleCreate = (note: Note) => { const handleCreate = (note: Note) => {
const pos = getRandomPosition(notes.length); const pos = getRandomPosition(notes.length);
maxZ += 1; maxZ += 1;
@@ -111,6 +107,10 @@
viewportEl.scrollTop = clamp(scrollTop, 0, BOARD_HEIGHT - viewportEl.clientHeight); viewportEl.scrollTop = clamp(scrollTop, 0, BOARD_HEIGHT - viewportEl.clientHeight);
updateViewportMetrics(); updateViewportMetrics();
}; };
onMount(() => {
updateViewportMetrics();
});
</script> </script>
<svelte:window <svelte:window
@@ -147,10 +147,19 @@
} }
.board-viewport { .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; height: 100vh;
overflow: auto; overflow: auto;
background: background: var(--app-bg);
radial-gradient(circle at top right, #f8fbff, #eef2f7 45%, #e6ebf2 100%);
scrollbar-width: none; scrollbar-width: none;
-ms-overflow-style: none; -ms-overflow-style: none;
cursor: grab; cursor: grab;
@@ -180,4 +189,5 @@
margin: 0 auto; margin: 0 auto;
pointer-events: auto; pointer-events: auto;
} }
</style> </style>