Abhiram commited on
Commit
34e68e7
·
verified ·
1 Parent(s): a2b21ff

Can u create a block editor notes app

Browse files
Files changed (5) hide show
  1. README.md +8 -5
  2. components/navbar.js +52 -0
  3. index.html +99 -19
  4. script.js +201 -0
  5. style.css +65 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Noteblocks Modular Note Magic
3
- emoji: 👁
4
- colorFrom: blue
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: NoteBlocks - Modular Note Magic ✏️
3
+ colorFrom: green
4
+ colorTo: purple
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/navbar.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .theme-toggle {
7
+ transition: all 0.3s ease;
8
+ }
9
+ </style>
10
+ <nav class="bg-white dark:bg-secondary-800 shadow-sm">
11
+ <div class="container mx-auto px-4 py-3 flex justify-between items-center">
12
+ <div class="flex items-center gap-2">
13
+ <i data-feather="edit-3" class="text-primary-500"></i>
14
+ <span class="text-xl font-bold text-secondary-900 dark:text-white">NoteBlocks</span>
15
+ </div>
16
+ <div class="flex items-center gap-4">
17
+ <button id="theme-toggle" class="p-2 rounded-full hover:bg-secondary-100 dark:hover:bg-secondary-700 theme-toggle">
18
+ <i data-feather="moon" class="hidden dark:block"></i>
19
+ <i data-feather="sun" class="dark:hidden"></i>
20
+ </button>
21
+ </div>
22
+ </div>
23
+ </nav>
24
+ `;
25
+
26
+ // Theme toggle functionality
27
+ const themeToggle = this.shadowRoot.getElementById('theme-toggle');
28
+ themeToggle.addEventListener('click', () => {
29
+ const html = document.documentElement;
30
+ if (html.classList.contains('dark')) {
31
+ html.classList.remove('dark');
32
+ localStorage.setItem('theme', 'light');
33
+ } else {
34
+ html.classList.add('dark');
35
+ localStorage.setItem('theme', 'dark');
36
+ }
37
+ feather.replace();
38
+ });
39
+
40
+ // Check for saved theme preference
41
+ if (localStorage.getItem('theme') === 'dark' ||
42
+ (!localStorage.getItem('theme') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
43
+ document.documentElement.classList.add('dark');
44
+ } else {
45
+ document.documentElement.classList.remove('dark');
46
+ }
47
+
48
+ feather.replace();
49
+ }
50
+ }
51
+
52
+ customElements.define('custom-navbar', CustomNavbar);
index.html CHANGED
@@ -1,19 +1,99 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="light">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>NoteBlocks - Modular Note Editor</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script>
12
+ tailwind.config = {
13
+ darkMode: 'class',
14
+ theme: {
15
+ extend: {
16
+ colors: {
17
+ primary: {
18
+ 50: '#eef2ff',
19
+ 100: '#e0e7ff',
20
+ 500: '#6366f1',
21
+ 600: '#4f46e5',
22
+ 700: '#4338ca',
23
+ },
24
+ secondary: {
25
+ 100: '#f3f4f6',
26
+ 200: '#e5e7eb',
27
+ 600: '#4b5563',
28
+ 700: '#374151',
29
+ 900: '#111827',
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ </script>
36
+ </head>
37
+ <body class="bg-secondary-100 dark:bg-secondary-900 min-h-screen transition-colors duration-200">
38
+ <custom-navbar></custom-navbar>
39
+ <main class="container mx-auto px-4 py-8">
40
+ <div class="flex justify-between items-center mb-8">
41
+ <h1 class="text-3xl font-bold text-secondary-900 dark:text-white">My Notes</h1>
42
+ <button id="new-note-btn" class="flex items-center gap-2 bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg transition">
43
+ <i data-feather="plus"></i>
44
+ New Note
45
+ </button>
46
+ </div>
47
+
48
+ <div id="notes-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
49
+ <!-- Notes will be dynamically inserted here -->
50
+ </div>
51
+
52
+ <div id="editor-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
53
+ <div class="bg-white dark:bg-secondary-700 rounded-lg shadow-xl w-full max-w-4xl max-h-[90vh] flex flex-col">
54
+ <div class="flex justify-between items-center p-4 border-b border-secondary-200 dark:border-secondary-600">
55
+ <input type="text" id="note-title" placeholder="Note title" class="text-xl font-bold bg-transparent outline-none w-full text-secondary-900 dark:text-white">
56
+ <div class="flex gap-2">
57
+ <button id="save-note" class="p-2 rounded-full hover:bg-secondary-100 dark:hover:bg-secondary-600">
58
+ <i data-feather="save"></i>
59
+ </button>
60
+ <button id="close-editor" class="p-2 rounded-full hover:bg-secondary-100 dark:hover:bg-secondary-600">
61
+ <i data-feather="x"></i>
62
+ </button>
63
+ </div>
64
+ </div>
65
+ <div id="editor-toolbar" class="p-2 border-b border-secondary-200 dark:border-secondary-600 flex gap-1 flex-wrap">
66
+ <button data-type="paragraph" class="p-2 rounded hover:bg-secondary-100 dark:hover:bg-secondary-600">
67
+ <i data-feather="align-left"></i>
68
+ </button>
69
+ <button data-type="heading" class="p-2 rounded hover:bg-secondary-100 dark:hover:bg-secondary-600">
70
+ <i data-feather="type"></i>
71
+ </button>
72
+ <button data-type="list" class="p-2 rounded hover:bg-secondary-100 dark:hover:bg-secondary-600">
73
+ <i data-feather="list"></i>
74
+ </button>
75
+ <button data-type="image" class="p-2 rounded hover:bg-secondary-100 dark:hover:bg-secondary-600">
76
+ <i data-feather="image"></i>
77
+ </button>
78
+ <button data-type="quote" class="p-2 rounded hover:bg-secondary-100 dark:hover:bg-secondary-600">
79
+ <i data-feather="quote"></i>
80
+ </button>
81
+ <button data-type="code" class="p-2 rounded hover:bg-secondary-100 dark:hover:bg-secondary-600">
82
+ <i data-feather="code"></i>
83
+ </button>
84
+ </div>
85
+ <div id="editor-content" class="p-4 overflow-y-auto flex-1 outline-none" contenteditable="true">
86
+ <!-- Editor content goes here -->
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </main>
91
+
92
+ <script src="components/navbar.js"></script>
93
+ <script src="script.js"></script>
94
+ <script>
95
+ feather.replace();
96
+ </script>
97
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
98
+ </body>
99
+ </html>
script.js ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ document.addEventListener('DOMContentLoaded', () => {
2
+ // Sample notes data
3
+ let notes = JSON.parse(localStorage.getItem('notes')) || [];
4
+ let currentNoteId = null;
5
+
6
+ // DOM elements
7
+ const notesGrid = document.getElementById('notes-grid');
8
+ const newNoteBtn = document.getElementById('new-note-btn');
9
+ const editorModal = document.getElementById('editor-modal');
10
+ const noteTitle = document.getElementById('note-title');
11
+ const editorContent = document.getElementById('editor-content');
12
+ const saveNoteBtn = document.getElementById('save-note');
13
+ const closeEditorBtn = document.getElementById('close-editor');
14
+ const toolbarButtons = document.querySelectorAll('#editor-toolbar button');
15
+
16
+ // Render all notes
17
+ function renderNotes() {
18
+ notesGrid.innerHTML = '';
19
+ notes.forEach(note => {
20
+ const preview = note.blocks.find(block => block.type === 'paragraph')?.content || 'No content';
21
+
22
+ const noteElement = document.createElement('div');
23
+ noteElement.className = 'bg-white dark:bg-secondary-700 rounded-lg shadow-md overflow-hidden hover:shadow-lg transition cursor-pointer';
24
+ noteElement.innerHTML = `
25
+ <div class="p-4">
26
+ <h3 class="font-bold text-lg mb-2 text-secondary-900 dark:text-white">${note.title || 'Untitled Note'}</h3>
27
+ <p class="text-secondary-600 dark:text-secondary-300 line-clamp-3">${preview}</p>
28
+ </div>
29
+ <div class="px-4 py-2 bg-secondary-50 dark:bg-secondary-800 text-secondary-500 dark:text-secondary-400 text-sm flex justify-between items-center">
30
+ <span>${new Date(note.updatedAt).toLocaleDateString()}</span>
31
+ <button class="text-red-500 hover:text-red-700 delete-note" data-id="${note.id}">
32
+ <i data-feather="trash-2"></i>
33
+ </button>
34
+ </div>
35
+ `;
36
+
37
+ noteElement.addEventListener('click', () => openEditor(note.id));
38
+ notesGrid.appendChild(noteElement);
39
+ });
40
+
41
+ // Add event listeners to delete buttons
42
+ document.querySelectorAll('.delete-note').forEach(btn => {
43
+ btn.addEventListener('click', (e) => {
44
+ e.stopPropagation();
45
+ deleteNote(btn.dataset.id);
46
+ });
47
+ });
48
+
49
+ feather.replace();
50
+ }
51
+
52
+ // Create a new note
53
+ function createNewNote() {
54
+ const newNote = {
55
+ id: Date.now().toString(),
56
+ title: '',
57
+ blocks: [{ type: 'paragraph', content: '' }],
58
+ createdAt: new Date(),
59
+ updatedAt: new Date()
60
+ };
61
+
62
+ notes.unshift(newNote);
63
+ saveNotes();
64
+ openEditor(newNote.id);
65
+ }
66
+
67
+ // Open editor with note content
68
+ function openEditor(noteId) {
69
+ const note = notes.find(n => n.id === noteId);
70
+ if (!note) return;
71
+
72
+ currentNoteId = noteId;
73
+ noteTitle.value = note.title;
74
+ editorContent.innerHTML = '';
75
+
76
+ note.blocks.forEach(block => {
77
+ const blockElement = createBlockElement(block);
78
+ editorContent.appendChild(blockElement);
79
+ });
80
+
81
+ editorModal.classList.remove('hidden');
82
+ document.body.style.overflow = 'hidden';
83
+ }
84
+
85
+ // Close editor
86
+ function closeEditor() {
87
+ editorModal.classList.add('hidden');
88
+ document.body.style.overflow = '';
89
+ currentNoteId = null;
90
+ }
91
+
92
+ // Save note
93
+ function saveNote() {
94
+ if (!currentNoteId) return;
95
+
96
+ const noteIndex = notes.findIndex(n => n.id === currentNoteId);
97
+ if (noteIndex === -1) return;
98
+
99
+ const title = noteTitle.value.trim();
100
+ const blocks = [];
101
+
102
+ // Get all blocks from editor
103
+ editorContent.querySelectorAll('[data-block]').forEach(blockEl => {
104
+ const type = blockEl.dataset.block;
105
+ let content = '';
106
+
107
+ if (type === 'image') {
108
+ content = blockEl.querySelector('img')?.src || '';
109
+ } else {
110
+ content = blockEl.textContent;
111
+ }
112
+
113
+ blocks.push({ type, content });
114
+ });
115
+
116
+ notes[noteIndex] = {
117
+ ...notes[noteIndex],
118
+ title,
119
+ blocks,
120
+ updatedAt: new Date()
121
+ };
122
+
123
+ saveNotes();
124
+ renderNotes();
125
+ }
126
+
127
+ // Delete note
128
+ function deleteNote(noteId) {
129
+ if (confirm('Are you sure you want to delete this note?')) {
130
+ notes = notes.filter(note => note.id !== noteId);
131
+ saveNotes();
132
+ renderNotes();
133
+ }
134
+ }
135
+
136
+ // Save notes to localStorage
137
+ function saveNotes() {
138
+ localStorage.setItem('notes', JSON.stringify(notes));
139
+ }
140
+
141
+ // Create a block element
142
+ function createBlockElement(block) {
143
+ const blockElement = document.createElement('div');
144
+ blockElement.dataset.block = block.type;
145
+ blockElement.className = `block-${block.type} mb-4`;
146
+ blockElement.contentEditable = true;
147
+
148
+ switch (block.type) {
149
+ case 'heading':
150
+ blockElement.innerHTML = `<h2>${block.content || 'Heading'}</h2>`;
151
+ break;
152
+ case 'list':
153
+ blockElement.innerHTML = `<ul><li>${block.content || 'List item'}</li></ul>`;
154
+ break;
155
+ case 'image':
156
+ blockElement.innerHTML = `<img src="${block.content || 'https://via.placeholder.com/600x400'}" alt="Image" class="rounded-lg w-full">`;
157
+ break;
158
+ case 'quote':
159
+ blockElement.innerHTML = `<blockquote>${block.content || 'Quote'}</blockquote>`;
160
+ break;
161
+ case 'code':
162
+ blockElement.innerHTML = `<pre><code>${block.content || 'Code'}</code></pre>`;
163
+ break;
164
+ default: // paragraph
165
+ blockElement.textContent = block.content || '';
166
+ }
167
+
168
+ return blockElement;
169
+ }
170
+
171
+ // Add block to editor
172
+ function addBlock(type) {
173
+ const block = { type, content: '' };
174
+ const blockElement = createBlockElement(block);
175
+
176
+ // Add to end of content
177
+ editorContent.appendChild(blockElement);
178
+
179
+ // Focus the new block
180
+ blockElement.focus();
181
+ }
182
+
183
+ // Event listeners
184
+ newNoteBtn.addEventListener('click', createNewNote);
185
+ saveNoteBtn.addEventListener('click', saveNote);
186
+ closeEditorBtn.addEventListener('click', closeEditor);
187
+
188
+ toolbarButtons.forEach(btn => {
189
+ btn.addEventListener('click', () => addBlock(btn.dataset.type));
190
+ });
191
+
192
+ // Close modal when clicking outside
193
+ editorModal.addEventListener('click', (e) => {
194
+ if (e.target === editorModal) {
195
+ closeEditor();
196
+ }
197
+ });
198
+
199
+ // Initialize
200
+ renderNotes();
201
+ });
style.css CHANGED
@@ -1,28 +1,74 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Custom styles that can't be done with Tailwind */
2
+ #editor-content:focus {
3
+ outline: none;
4
  }
5
 
6
+ /* Custom scrollbar for editor */
7
+ #editor-content::-webkit-scrollbar {
8
+ width: 8px;
9
  }
10
 
11
+ #editor-content::-webkit-scrollbar-track {
12
+ background: #f1f1f1;
 
 
 
13
  }
14
 
15
+ #editor-content::-webkit-scrollbar-thumb {
16
+ background: #888;
17
+ border-radius: 4px;
 
 
 
18
  }
19
 
20
+ .dark #editor-content::-webkit-scrollbar-track {
21
+ background: #374151;
22
  }
23
+
24
+ .dark #editor-content::-webkit-scrollbar-thumb {
25
+ background: #4b5563;
26
+ }
27
+
28
+ /* Block styles */
29
+ .block-paragraph {
30
+ margin-bottom: 1rem;
31
+ }
32
+
33
+ .block-heading {
34
+ font-size: 1.5rem;
35
+ font-weight: bold;
36
+ margin: 1rem 0;
37
+ }
38
+
39
+ .block-list {
40
+ padding-left: 2rem;
41
+ margin-bottom: 1rem;
42
+ }
43
+
44
+ .block-list li {
45
+ list-style-type: disc;
46
+ margin-bottom: 0.5rem;
47
+ }
48
+
49
+ .block-quote {
50
+ border-left: 4px solid #6366f1;
51
+ padding-left: 1rem;
52
+ margin: 1rem 0;
53
+ font-style: italic;
54
+ color: #6b7280;
55
+ }
56
+
57
+ .block-code {
58
+ background-color: #f3f4f6;
59
+ padding: 1rem;
60
+ border-radius: 0.5rem;
61
+ font-family: monospace;
62
+ margin: 1rem 0;
63
+ white-space: pre-wrap;
64
+ }
65
+
66
+ .dark .block-code {
67
+ background-color: #1f2937;
68
+ }
69
+
70
+ .block-image {
71
+ margin: 1rem 0;
72
+ max-width: 100%;
73
+ border-radius: 0.5rem;
74
+ }