zen-wdg 2.2.7 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1073 @@
1
+ <template>
2
+ <div class="notes-widget">
3
+ <div class="notes-header">
4
+ <h2 class="notes-title">📝 Notas</h2>
5
+ <div class="header-actions">
6
+ <button @click="showCategoryManager = !showCategoryManager" class="icon-btn" title="Gestionar Categorías">
7
+ 🏷️
8
+ </button>
9
+ <button @click="exportNotes" class="icon-btn" title="Exportar Notas">
10
+ 💾
11
+ </button>
12
+ <button @click="toggleNewNote" class="icon-btn add-btn" title="Nueva Nota">
13
+
14
+ </button>
15
+ </div>
16
+ </div>
17
+
18
+ <!-- Category Manager Modal -->
19
+ <transition name="modal">
20
+ <div v-if="showCategoryManager" class="modal-overlay" @click="showCategoryManager = false">
21
+ <div class="modal-content" @click.stop>
22
+ <div class="modal-header">
23
+ <h3>🏷️ Gestionar Categorías</h3>
24
+ <button @click="showCategoryManager = false" class="close-btn">✕</button>
25
+ </div>
26
+ <div class="modal-body">
27
+ <div class="category-form">
28
+ <input
29
+ v-model="newCategoryName"
30
+ @keypress.enter="addCategory"
31
+ placeholder="Nombre de categoría"
32
+ class="category-input"
33
+ />
34
+ <input
35
+ v-model="newCategoryColor"
36
+ type="color"
37
+ class="color-picker"
38
+ />
39
+ <button @click="addCategory" class="add-category-btn">Agregar</button>
40
+ </div>
41
+ <div class="categories-list">
42
+ <div
43
+ v-for="category in categories"
44
+ :key="category.id"
45
+ class="category-item"
46
+ >
47
+ <span
48
+ class="category-color-preview"
49
+ :style="{ backgroundColor: category.color }"
50
+ ></span>
51
+ <span class="category-name">{{ category.name }}</span>
52
+ <button
53
+ v-if="category.id !== 'default'"
54
+ @click="deleteCategory(category.id)"
55
+ class="delete-category-btn"
56
+ title="Eliminar"
57
+ >
58
+ 🗑️
59
+ </button>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </transition>
66
+
67
+ <!-- New Note Form -->
68
+ <transition name="slide">
69
+ <div v-if="showNewNoteForm" class="new-note-form">
70
+ <input
71
+ v-model="newNote.title"
72
+ placeholder="Título de la nota..."
73
+ class="note-title-input"
74
+ ref="titleInput"
75
+ />
76
+ <textarea
77
+ v-model="newNote.content"
78
+ placeholder="Escribe tu nota aquí..."
79
+ class="note-content-input"
80
+ rows="4"
81
+ ></textarea>
82
+ <div class="note-form-actions">
83
+ <select v-model="newNote.categoryId" class="category-select">
84
+ <option value="">Sin categoría</option>
85
+ <option
86
+ v-for="category in categories.filter(c => c.id !== 'default')"
87
+ :key="category.id"
88
+ :value="category.id"
89
+ >
90
+ {{ category.name }}
91
+ </option>
92
+ </select>
93
+ <div class="form-buttons">
94
+ <button @click="cancelNewNote" class="cancel-btn">Cancelar</button>
95
+ <button @click="saveNote" class="save-btn">Guardar</button>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </transition>
100
+
101
+ <!-- Filter and Sort -->
102
+ <div class="notes-controls">
103
+ <div class="filter-controls">
104
+ <button
105
+ @click="filterCategory = null"
106
+ :class="['filter-btn', { active: filterCategory === null }]"
107
+ >
108
+ 📋 Todas
109
+ </button>
110
+ <button
111
+ @click="filterCategory = 'pinned'"
112
+ :class="['filter-btn', { active: filterCategory === 'pinned' }]"
113
+ >
114
+ 📌 Fijadas
115
+ </button>
116
+ <button
117
+ v-for="category in categories.filter(c => c.id !== 'default')"
118
+ :key="category.id"
119
+ @click="filterCategory = category.id"
120
+ :class="['filter-btn', { active: filterCategory === category.id }]"
121
+ :style="{ borderColor: category.color, color: filterCategory === category.id ? category.color : '' }"
122
+ >
123
+ {{ category.name }}
124
+ </button>
125
+ </div>
126
+ <div class="sort-controls">
127
+ <select v-model="sortBy" class="sort-select">
128
+ <option value="date">📅 Fecha</option>
129
+ <option value="title">🔤 Título</option>
130
+ <option value="category">🏷️ Categoría</option>
131
+ </select>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Notes List -->
136
+ <div class="notes-list">
137
+ <div v-if="filteredNotes.length === 0" class="empty-state">
138
+ <div class="empty-icon">📭</div>
139
+ <p>No hay notas</p>
140
+ <button @click="toggleNewNote" class="create-first-note-btn">
141
+ Crear primera nota
142
+ </button>
143
+ </div>
144
+
145
+ <transition-group name="note-list" tag="div" class="notes-grid">
146
+ <div
147
+ v-for="note in filteredNotes"
148
+ :key="note.id"
149
+ :class="['note-card', { pinned: note.isPinned }]"
150
+ :style="{ borderLeftColor: getNoteColor(note) }"
151
+ >
152
+ <div class="note-card-header">
153
+ <button
154
+ @click="togglePin(note.id)"
155
+ :class="['pin-btn', { pinned: note.isPinned }]"
156
+ title="Fijar/Desfijar"
157
+ >
158
+ {{ note.isPinned ? '📌' : '📍' }}
159
+ </button>
160
+ <button @click="deleteNote(note.id)" class="delete-btn" title="Eliminar">
161
+ 🗑️
162
+ </button>
163
+ </div>
164
+
165
+ <h3 class="note-title">{{ note.title || 'Sin título' }}</h3>
166
+ <p class="note-content">{{ note.content }}</p>
167
+
168
+ <div class="note-footer">
169
+ <span
170
+ v-if="note.categoryId"
171
+ class="note-category"
172
+ :style="{
173
+ backgroundColor: getCategoryColor(note.categoryId),
174
+ color: 'white'
175
+ }"
176
+ >
177
+ {{ getCategoryName(note.categoryId) }}
178
+ </span>
179
+ <span class="note-date">{{ formatDate(note.createdAt) }}</span>
180
+ </div>
181
+ </div>
182
+ </transition-group>
183
+ </div>
184
+
185
+ <!-- Stats Footer -->
186
+ <div class="notes-stats">
187
+ <span>Total: {{ notes.length }} notas</span>
188
+ <span v-if="pinnedNotesCount > 0">📌 {{ pinnedNotesCount }} fijadas</span>
189
+ </div>
190
+ </div>
191
+ </template>
192
+
193
+ <script>
194
+ export default {
195
+ name: 'ZNotesWidget',
196
+ data() {
197
+ return {
198
+ notes: [],
199
+ categories: [
200
+ { id: 'default', name: 'Sin categoría', color: '#6b7280' }
201
+ ],
202
+ showNewNoteForm: false,
203
+ showCategoryManager: false,
204
+ newNote: {
205
+ title: '',
206
+ content: '',
207
+ categoryId: ''
208
+ },
209
+ newCategoryName: '',
210
+ newCategoryColor: '#3b82f6',
211
+ filterCategory: null,
212
+ sortBy: 'date',
213
+ storageKey: 'zen-notes-widget',
214
+ categoriesKey: 'zen-notes-categories'
215
+ }
216
+ },
217
+ computed: {
218
+ filteredNotes() {
219
+ let filtered = [...this.notes]
220
+
221
+ // Filter by category
222
+ if (this.filterCategory === 'pinned') {
223
+ filtered = filtered.filter(note => note.isPinned)
224
+ } else if (this.filterCategory) {
225
+ filtered = filtered.filter(note => note.categoryId === this.filterCategory)
226
+ }
227
+
228
+ // Sort
229
+ filtered.sort((a, b) => {
230
+ if (this.sortBy === 'date') {
231
+ return new Date(b.createdAt) - new Date(a.createdAt)
232
+ } else if (this.sortBy === 'title') {
233
+ return (a.title || '').localeCompare(b.title || '')
234
+ } else if (this.sortBy === 'category') {
235
+ const catA = this.getCategoryName(a.categoryId)
236
+ const catB = this.getCategoryName(b.categoryId)
237
+ return catA.localeCompare(catB)
238
+ }
239
+ return 0
240
+ })
241
+
242
+ // Pinned notes always first
243
+ return filtered.sort((a, b) => {
244
+ if (a.isPinned && !b.isPinned) return -1
245
+ if (!a.isPinned && b.isPinned) return 1
246
+ return 0
247
+ })
248
+ },
249
+ pinnedNotesCount() {
250
+ return this.notes.filter(note => note.isPinned).length
251
+ }
252
+ },
253
+ methods: {
254
+ toggleNewNote() {
255
+ this.showNewNoteForm = !this.showNewNoteForm
256
+ if (this.showNewNoteForm) {
257
+ this.$nextTick(() => {
258
+ this.$refs.titleInput?.focus()
259
+ })
260
+ }
261
+ },
262
+ saveNote() {
263
+ if (!this.newNote.content.trim()) return
264
+
265
+ const note = {
266
+ id: Date.now().toString(),
267
+ title: this.newNote.title.trim(),
268
+ content: this.newNote.content.trim(),
269
+ categoryId: this.newNote.categoryId,
270
+ isPinned: false,
271
+ createdAt: new Date().toISOString()
272
+ }
273
+
274
+ this.notes.unshift(note)
275
+ this.saveToStorage()
276
+ this.cancelNewNote()
277
+ },
278
+ cancelNewNote() {
279
+ this.newNote = {
280
+ title: '',
281
+ content: '',
282
+ categoryId: ''
283
+ }
284
+ this.showNewNoteForm = false
285
+ },
286
+ async deleteNote(id) {
287
+ const confirmed = await window.ZenModals.showDeleteConfirm('esta nota')
288
+ if (confirmed) {
289
+ this.notes = this.notes.filter(note => note.id !== id)
290
+ this.saveToStorage()
291
+ }
292
+ },
293
+ togglePin(id) {
294
+ const note = this.notes.find(n => n.id === id)
295
+ if (note) {
296
+ note.isPinned = !note.isPinned
297
+ this.saveToStorage()
298
+ }
299
+ },
300
+ addCategory() {
301
+ if (!this.newCategoryName.trim()) return
302
+
303
+ const category = {
304
+ id: Date.now().toString(),
305
+ name: this.newCategoryName.trim(),
306
+ color: this.newCategoryColor
307
+ }
308
+
309
+ this.categories.push(category)
310
+ this.saveCategoriesToStorage()
311
+ this.newCategoryName = ''
312
+ this.newCategoryColor = '#3b82f6'
313
+ },
314
+ async deleteCategory(id) {
315
+ const confirmed = await window.ZenModals.showConfirm({
316
+ title: '¿Eliminar categoría?',
317
+ message: 'Las notas de esta categoría no se eliminarán, solo la categoría.',
318
+ confirmText: 'Eliminar',
319
+ cancelText: 'Cancelar',
320
+ confirmButtonClass: 'danger'
321
+ })
322
+ if (confirmed) {
323
+ this.categories = this.categories.filter(cat => cat.id !== id)
324
+ this.saveCategoriesToStorage()
325
+ }
326
+ },
327
+ getCategoryName(categoryId) {
328
+ const category = this.categories.find(cat => cat.id === categoryId)
329
+ return category ? category.name : 'Sin categoría'
330
+ },
331
+ getCategoryColor(categoryId) {
332
+ const category = this.categories.find(cat => cat.id === categoryId)
333
+ return category ? category.color : '#6b7280'
334
+ },
335
+ getNoteColor(note) {
336
+ if (note.categoryId) {
337
+ return this.getCategoryColor(note.categoryId)
338
+ }
339
+ return '#6b7280'
340
+ },
341
+ formatDate(dateString) {
342
+ const date = new Date(dateString)
343
+ const now = new Date()
344
+ const diff = now - date
345
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24))
346
+
347
+ if (days === 0) {
348
+ const hours = Math.floor(diff / (1000 * 60 * 60))
349
+ if (hours === 0) {
350
+ const minutes = Math.floor(diff / (1000 * 60))
351
+ return minutes === 0 ? 'Ahora' : `Hace ${minutes}m`
352
+ }
353
+ return `Hace ${hours}h`
354
+ } else if (days === 1) {
355
+ return 'Ayer'
356
+ } else if (days < 7) {
357
+ return `Hace ${days} días`
358
+ } else {
359
+ return date.toLocaleDateString('es-ES', {
360
+ day: 'numeric',
361
+ month: 'short',
362
+ year: date.getFullYear() !== now.getFullYear() ? 'numeric' : undefined
363
+ })
364
+ }
365
+ },
366
+ exportNotes() {
367
+ if (this.notes.length === 0) {
368
+ alert('No hay notas para exportar')
369
+ return
370
+ }
371
+
372
+ let exportText = '# MIS NOTAS\n'
373
+ exportText += `# Exportado el ${new Date().toLocaleString('es-ES')}\n\n`
374
+ exportText += '═'.repeat(60) + '\n\n'
375
+
376
+ const notesByCategory = {}
377
+
378
+ this.notes.forEach(note => {
379
+ const categoryName = this.getCategoryName(note.categoryId)
380
+ if (!notesByCategory[categoryName]) {
381
+ notesByCategory[categoryName] = []
382
+ }
383
+ notesByCategory[categoryName].push(note)
384
+ })
385
+
386
+ Object.keys(notesByCategory).sort().forEach(categoryName => {
387
+ exportText += `## 🏷️ ${categoryName.toUpperCase()}\n`
388
+ exportText += '─'.repeat(60) + '\n\n'
389
+
390
+ notesByCategory[categoryName].forEach(note => {
391
+ exportText += `### ${note.title || 'Sin título'}`
392
+ if (note.isPinned) exportText += ' 📌'
393
+ exportText += '\n'
394
+ exportText += `Fecha: ${new Date(note.createdAt).toLocaleString('es-ES')}\n\n`
395
+ exportText += `${note.content}\n\n`
396
+ exportText += '─'.repeat(60) + '\n\n'
397
+ })
398
+ })
399
+
400
+ exportText += '\n═'.repeat(60) + '\n'
401
+ exportText += `Total de notas: ${this.notes.length}\n`
402
+ exportText += `Notas fijadas: ${this.pinnedNotesCount}\n`
403
+
404
+ // Create and download file
405
+ const blob = new Blob([exportText], { type: 'text/plain;charset=utf-8' })
406
+ const url = URL.createObjectURL(blob)
407
+ const link = document.createElement('a')
408
+ link.href = url
409
+ link.download = `notas_${new Date().toISOString().split('T')[0]}.txt`
410
+ document.body.appendChild(link)
411
+ link.click()
412
+ document.body.removeChild(link)
413
+ URL.revokeObjectURL(url)
414
+
415
+ alert('✅ Notas exportadas exitosamente')
416
+ },
417
+ saveToStorage() {
418
+ try {
419
+ localStorage.setItem(this.storageKey, JSON.stringify(this.notes))
420
+ } catch (e) {
421
+ console.error('Error al guardar notas:', e)
422
+ }
423
+ },
424
+ loadFromStorage() {
425
+ try {
426
+ const data = localStorage.getItem(this.storageKey)
427
+ if (data) {
428
+ this.notes = JSON.parse(data)
429
+ }
430
+ } catch (e) {
431
+ console.error('Error al cargar notas:', e)
432
+ }
433
+ },
434
+ saveCategoriesToStorage() {
435
+ try {
436
+ localStorage.setItem(this.categoriesKey, JSON.stringify(this.categories))
437
+ } catch (e) {
438
+ console.error('Error al guardar categorías:', e)
439
+ }
440
+ },
441
+ loadCategoriesFromStorage() {
442
+ try {
443
+ const data = localStorage.getItem(this.categoriesKey)
444
+ if (data) {
445
+ const loaded = JSON.parse(data)
446
+ // Ensure default category exists
447
+ if (!loaded.find(c => c.id === 'default')) {
448
+ loaded.unshift({ id: 'default', name: 'Sin categoría', color: '#6b7280' })
449
+ }
450
+ this.categories = loaded
451
+ }
452
+ } catch (e) {
453
+ console.error('Error al cargar categorías:', e)
454
+ }
455
+ }
456
+ },
457
+ mounted() {
458
+ this.loadCategoriesFromStorage()
459
+ this.loadFromStorage()
460
+ }
461
+ }
462
+ </script>
463
+
464
+ <style scoped>
465
+ .notes-widget {
466
+ width: 100%;
467
+ max-width: 900px;
468
+ background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
469
+ border-radius: 20px;
470
+ padding: 25px;
471
+ color: #e2e8f0;
472
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
473
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
474
+ }
475
+
476
+ /* Header */
477
+ .notes-header {
478
+ display: flex;
479
+ justify-content: space-between;
480
+ align-items: center;
481
+ margin-bottom: 20px;
482
+ padding-bottom: 15px;
483
+ border-bottom: 2px solid rgba(255, 255, 255, 0.1);
484
+ }
485
+
486
+ .notes-title {
487
+ margin: 0;
488
+ font-size: 1.8rem;
489
+ background: linear-gradient(135deg, #60a5fa 0%, #3b82f6 100%);
490
+ -webkit-background-clip: text;
491
+ -webkit-text-fill-color: transparent;
492
+ background-clip: text;
493
+ }
494
+
495
+ .header-actions {
496
+ display: flex;
497
+ gap: 10px;
498
+ }
499
+
500
+ .icon-btn {
501
+ width: 40px;
502
+ height: 40px;
503
+ border: none;
504
+ background: rgba(59, 130, 246, 0.2);
505
+ border-radius: 10px;
506
+ font-size: 1.2rem;
507
+ cursor: pointer;
508
+ transition: all 0.3s ease;
509
+ display: flex;
510
+ align-items: center;
511
+ justify-content: center;
512
+ }
513
+
514
+ .icon-btn:hover {
515
+ background: rgba(59, 130, 246, 0.3);
516
+ transform: translateY(-2px);
517
+ }
518
+
519
+ .add-btn {
520
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
521
+ }
522
+
523
+ .add-btn:hover {
524
+ background: linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%);
525
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
526
+ }
527
+
528
+ /* Modal */
529
+ .modal-overlay {
530
+ position: fixed;
531
+ top: 0;
532
+ left: 0;
533
+ right: 0;
534
+ bottom: 0;
535
+ background: rgba(0, 0, 0, 0.7);
536
+ display: flex;
537
+ align-items: center;
538
+ justify-content: center;
539
+ z-index: 1000;
540
+ padding: 20px;
541
+ }
542
+
543
+ .modal-content {
544
+ background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
545
+ border-radius: 20px;
546
+ padding: 30px;
547
+ max-width: 500px;
548
+ width: 100%;
549
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
550
+ }
551
+
552
+ .modal-header {
553
+ display: flex;
554
+ justify-content: space-between;
555
+ align-items: center;
556
+ margin-bottom: 20px;
557
+ }
558
+
559
+ .modal-header h3 {
560
+ margin: 0;
561
+ color: #e2e8f0;
562
+ font-size: 1.5rem;
563
+ }
564
+
565
+ .close-btn {
566
+ width: 35px;
567
+ height: 35px;
568
+ border: none;
569
+ background: rgba(239, 68, 68, 0.2);
570
+ border-radius: 50%;
571
+ font-size: 1.2rem;
572
+ cursor: pointer;
573
+ transition: all 0.3s ease;
574
+ color: #ef4444;
575
+ }
576
+
577
+ .close-btn:hover {
578
+ background: #ef4444;
579
+ color: white;
580
+ transform: rotate(90deg);
581
+ }
582
+
583
+ .modal-body {
584
+ max-height: 400px;
585
+ overflow-y: auto;
586
+ }
587
+
588
+ /* Category Form */
589
+ .category-form {
590
+ display: flex;
591
+ gap: 10px;
592
+ margin-bottom: 20px;
593
+ }
594
+
595
+ .category-input {
596
+ flex: 1;
597
+ padding: 12px;
598
+ background: rgba(255, 255, 255, 0.05);
599
+ border: 2px solid rgba(255, 255, 255, 0.1);
600
+ border-radius: 10px;
601
+ color: #e2e8f0;
602
+ font-size: 0.95rem;
603
+ }
604
+
605
+ .category-input:focus {
606
+ outline: none;
607
+ border-color: #3b82f6;
608
+ background: rgba(255, 255, 255, 0.08);
609
+ }
610
+
611
+ .color-picker {
612
+ width: 60px;
613
+ height: 45px;
614
+ border: none;
615
+ border-radius: 10px;
616
+ cursor: pointer;
617
+ background: transparent;
618
+ }
619
+
620
+ .add-category-btn {
621
+ padding: 0 20px;
622
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
623
+ border: none;
624
+ border-radius: 10px;
625
+ color: white;
626
+ font-weight: 600;
627
+ cursor: pointer;
628
+ transition: all 0.3s ease;
629
+ }
630
+
631
+ .add-category-btn:hover {
632
+ transform: translateY(-2px);
633
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4);
634
+ }
635
+
636
+ /* Categories List */
637
+ .categories-list {
638
+ display: flex;
639
+ flex-direction: column;
640
+ gap: 10px;
641
+ }
642
+
643
+ .category-item {
644
+ display: flex;
645
+ align-items: center;
646
+ gap: 12px;
647
+ padding: 12px;
648
+ background: rgba(255, 255, 255, 0.05);
649
+ border-radius: 10px;
650
+ transition: all 0.3s ease;
651
+ }
652
+
653
+ .category-item:hover {
654
+ background: rgba(255, 255, 255, 0.08);
655
+ }
656
+
657
+ .category-color-preview {
658
+ width: 25px;
659
+ height: 25px;
660
+ border-radius: 6px;
661
+ border: 2px solid rgba(255, 255, 255, 0.2);
662
+ }
663
+
664
+ .category-name {
665
+ flex: 1;
666
+ color: #e2e8f0;
667
+ font-weight: 500;
668
+ }
669
+
670
+ .delete-category-btn {
671
+ width: 35px;
672
+ height: 35px;
673
+ border: none;
674
+ background: rgba(239, 68, 68, 0.2);
675
+ border-radius: 8px;
676
+ cursor: pointer;
677
+ font-size: 1rem;
678
+ transition: all 0.3s ease;
679
+ }
680
+
681
+ .delete-category-btn:hover {
682
+ background: #ef4444;
683
+ transform: scale(1.1);
684
+ }
685
+
686
+ /* New Note Form */
687
+ .new-note-form {
688
+ background: rgba(255, 255, 255, 0.05);
689
+ border: 2px solid rgba(59, 130, 246, 0.3);
690
+ border-radius: 15px;
691
+ padding: 20px;
692
+ margin-bottom: 20px;
693
+ }
694
+
695
+ .note-title-input,
696
+ .note-content-input {
697
+ width: 100%;
698
+ padding: 12px;
699
+ background: rgba(255, 255, 255, 0.05);
700
+ border: 2px solid rgba(255, 255, 255, 0.1);
701
+ border-radius: 10px;
702
+ color: #e2e8f0;
703
+ font-size: 0.95rem;
704
+ font-family: inherit;
705
+ margin-bottom: 12px;
706
+ }
707
+
708
+ .note-title-input {
709
+ font-size: 1.1rem;
710
+ font-weight: 600;
711
+ }
712
+
713
+ .note-title-input:focus,
714
+ .note-content-input:focus {
715
+ outline: none;
716
+ border-color: #3b82f6;
717
+ background: rgba(255, 255, 255, 0.08);
718
+ }
719
+
720
+ .note-content-input {
721
+ resize: vertical;
722
+ min-height: 100px;
723
+ }
724
+
725
+ .note-form-actions {
726
+ display: flex;
727
+ justify-content: space-between;
728
+ align-items: center;
729
+ gap: 10px;
730
+ }
731
+
732
+ .category-select {
733
+ padding: 10px 15px;
734
+ background: rgba(255, 255, 255, 0.05);
735
+ border: 2px solid rgba(255, 255, 255, 0.1);
736
+ border-radius: 10px;
737
+ color: #e2e8f0;
738
+ cursor: pointer;
739
+ font-size: 0.9rem;
740
+ }
741
+
742
+ .category-select:focus {
743
+ outline: none;
744
+ border-color: #3b82f6;
745
+ }
746
+
747
+ .form-buttons {
748
+ display: flex;
749
+ gap: 10px;
750
+ }
751
+
752
+ .cancel-btn,
753
+ .save-btn {
754
+ padding: 10px 20px;
755
+ border: none;
756
+ border-radius: 10px;
757
+ font-weight: 600;
758
+ cursor: pointer;
759
+ transition: all 0.3s ease;
760
+ }
761
+
762
+ .cancel-btn {
763
+ background: rgba(239, 68, 68, 0.2);
764
+ color: #ef4444;
765
+ }
766
+
767
+ .cancel-btn:hover {
768
+ background: rgba(239, 68, 68, 0.3);
769
+ }
770
+
771
+ .save-btn {
772
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
773
+ color: white;
774
+ }
775
+
776
+ .save-btn:hover {
777
+ transform: translateY(-2px);
778
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
779
+ }
780
+
781
+ /* Controls */
782
+ .notes-controls {
783
+ display: flex;
784
+ justify-content: space-between;
785
+ align-items: center;
786
+ gap: 15px;
787
+ margin-bottom: 20px;
788
+ flex-wrap: wrap;
789
+ }
790
+
791
+ .filter-controls {
792
+ display: flex;
793
+ gap: 8px;
794
+ flex-wrap: wrap;
795
+ }
796
+
797
+ .filter-btn {
798
+ padding: 8px 16px;
799
+ background: rgba(255, 255, 255, 0.05);
800
+ border: 2px solid rgba(255, 255, 255, 0.1);
801
+ border-radius: 20px;
802
+ color: #94a3b8;
803
+ cursor: pointer;
804
+ font-size: 0.85rem;
805
+ font-weight: 500;
806
+ transition: all 0.3s ease;
807
+ }
808
+
809
+ .filter-btn:hover {
810
+ background: rgba(255, 255, 255, 0.08);
811
+ color: #e2e8f0;
812
+ }
813
+
814
+ .filter-btn.active {
815
+ background: rgba(59, 130, 246, 0.2);
816
+ border-color: #3b82f6;
817
+ color: #60a5fa;
818
+ }
819
+
820
+ .sort-controls {
821
+ display: flex;
822
+ gap: 10px;
823
+ }
824
+
825
+ .sort-select {
826
+ padding: 8px 12px;
827
+ background: rgba(255, 255, 255, 0.05);
828
+ border: 2px solid rgba(255, 255, 255, 0.1);
829
+ border-radius: 10px;
830
+ color: #e2e8f0;
831
+ cursor: pointer;
832
+ font-size: 0.85rem;
833
+ }
834
+
835
+ .sort-select:focus {
836
+ outline: none;
837
+ border-color: #3b82f6;
838
+ }
839
+
840
+ /* Notes List */
841
+ .notes-list {
842
+ margin-bottom: 20px;
843
+ }
844
+
845
+ .empty-state {
846
+ text-align: center;
847
+ padding: 60px 20px;
848
+ color: #64748b;
849
+ }
850
+
851
+ .empty-icon {
852
+ font-size: 4rem;
853
+ margin-bottom: 15px;
854
+ opacity: 0.5;
855
+ }
856
+
857
+ .empty-state p {
858
+ font-size: 1.2rem;
859
+ margin-bottom: 20px;
860
+ }
861
+
862
+ .create-first-note-btn {
863
+ padding: 12px 30px;
864
+ background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
865
+ border: none;
866
+ border-radius: 12px;
867
+ color: white;
868
+ font-weight: 600;
869
+ cursor: pointer;
870
+ transition: all 0.3s ease;
871
+ }
872
+
873
+ .create-first-note-btn:hover {
874
+ transform: translateY(-2px);
875
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
876
+ }
877
+
878
+ /* Notes Grid */
879
+ .notes-grid {
880
+ display: grid;
881
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
882
+ gap: 15px;
883
+ }
884
+
885
+ .note-card {
886
+ background: rgba(255, 255, 255, 0.05);
887
+ backdrop-filter: blur(10px);
888
+ border-left: 4px solid #6b7280;
889
+ border-radius: 12px;
890
+ padding: 15px;
891
+ transition: all 0.3s ease;
892
+ display: flex;
893
+ flex-direction: column;
894
+ gap: 10px;
895
+ }
896
+
897
+ .note-card:hover {
898
+ background: rgba(255, 255, 255, 0.08);
899
+ transform: translateY(-3px);
900
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
901
+ }
902
+
903
+ .note-card.pinned {
904
+ background: rgba(59, 130, 246, 0.1);
905
+ border-left-color: #3b82f6;
906
+ }
907
+
908
+ .note-card-header {
909
+ display: flex;
910
+ justify-content: space-between;
911
+ gap: 5px;
912
+ }
913
+
914
+ .pin-btn,
915
+ .delete-btn {
916
+ width: 30px;
917
+ height: 30px;
918
+ border: none;
919
+ background: rgba(255, 255, 255, 0.05);
920
+ border-radius: 6px;
921
+ cursor: pointer;
922
+ font-size: 1rem;
923
+ transition: all 0.3s ease;
924
+ }
925
+
926
+ .pin-btn:hover {
927
+ background: rgba(59, 130, 246, 0.2);
928
+ transform: scale(1.1);
929
+ }
930
+
931
+ .pin-btn.pinned {
932
+ background: rgba(59, 130, 246, 0.2);
933
+ }
934
+
935
+ .delete-btn:hover {
936
+ background: rgba(239, 68, 68, 0.2);
937
+ transform: scale(1.1);
938
+ }
939
+
940
+ .note-title {
941
+ margin: 0;
942
+ font-size: 1.1rem;
943
+ color: #f1f5f9;
944
+ font-weight: 600;
945
+ overflow: hidden;
946
+ text-overflow: ellipsis;
947
+ white-space: nowrap;
948
+ }
949
+
950
+ .note-content {
951
+ margin: 0;
952
+ color: #cbd5e1;
953
+ font-size: 0.9rem;
954
+ line-height: 1.6;
955
+ flex: 1;
956
+ overflow: hidden;
957
+ display: -webkit-box;
958
+ -webkit-line-clamp: 4;
959
+ line-clamp: 4;
960
+ -webkit-box-orient: vertical;
961
+ }
962
+
963
+ .note-footer {
964
+ display: flex;
965
+ justify-content: space-between;
966
+ align-items: center;
967
+ gap: 10px;
968
+ padding-top: 10px;
969
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
970
+ }
971
+
972
+ .note-category {
973
+ padding: 4px 10px;
974
+ border-radius: 12px;
975
+ font-size: 0.75rem;
976
+ font-weight: 600;
977
+ text-transform: uppercase;
978
+ letter-spacing: 0.5px;
979
+ }
980
+
981
+ .note-date {
982
+ color: #64748b;
983
+ font-size: 0.75rem;
984
+ }
985
+
986
+ /* Stats */
987
+ .notes-stats {
988
+ display: flex;
989
+ justify-content: space-between;
990
+ padding: 15px;
991
+ background: rgba(255, 255, 255, 0.05);
992
+ border-radius: 10px;
993
+ color: #94a3b8;
994
+ font-size: 0.9rem;
995
+ font-weight: 500;
996
+ }
997
+
998
+ /* Animations */
999
+ .modal-enter-active,
1000
+ .modal-leave-active {
1001
+ transition: all 0.3s ease;
1002
+ }
1003
+
1004
+ .modal-enter-from,
1005
+ .modal-leave-to {
1006
+ opacity: 0;
1007
+ transform: scale(0.9);
1008
+ }
1009
+
1010
+ .slide-enter-active,
1011
+ .slide-leave-active {
1012
+ transition: all 0.3s ease;
1013
+ }
1014
+
1015
+ .slide-enter-from {
1016
+ opacity: 0;
1017
+ transform: translateY(-20px);
1018
+ }
1019
+
1020
+ .slide-leave-to {
1021
+ opacity: 0;
1022
+ transform: translateY(-20px);
1023
+ }
1024
+
1025
+ .note-list-enter-active,
1026
+ .note-list-leave-active {
1027
+ transition: all 0.3s ease;
1028
+ }
1029
+
1030
+ .note-list-enter-from {
1031
+ opacity: 0;
1032
+ transform: scale(0.9);
1033
+ }
1034
+
1035
+ .note-list-leave-to {
1036
+ opacity: 0;
1037
+ transform: scale(0.9);
1038
+ }
1039
+
1040
+ .note-list-move {
1041
+ transition: transform 0.3s ease;
1042
+ }
1043
+
1044
+ /* Responsive */
1045
+ @media (max-width: 768px) {
1046
+ .notes-widget {
1047
+ padding: 15px;
1048
+ }
1049
+
1050
+ .notes-title {
1051
+ font-size: 1.5rem;
1052
+ }
1053
+
1054
+ .notes-grid {
1055
+ grid-template-columns: 1fr;
1056
+ }
1057
+
1058
+ .notes-controls {
1059
+ flex-direction: column;
1060
+ align-items: stretch;
1061
+ }
1062
+
1063
+ .filter-controls,
1064
+ .sort-controls {
1065
+ width: 100%;
1066
+ }
1067
+
1068
+ .filter-btn {
1069
+ flex: 1;
1070
+ text-align: center;
1071
+ }
1072
+ }
1073
+ </style>