mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	added attributes sorting
This commit is contained in:
		
							
								
								
									
										1
									
								
								db/migrations/0074__add_position_to_attribute.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								db/migrations/0074__add_position_to_attribute.sql
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| ALTER TABLE attributes ADD COLUMN position INT NOT NULL DEFAULT 0; | ||||
| @@ -1,8 +1,10 @@ | ||||
| "use strict"; | ||||
|  | ||||
| const attributesDialog = (function() { | ||||
|     const dialogEl = $("#attributes-dialog"); | ||||
|     const saveAttributesButton = $("#save-attributes-button"); | ||||
|     const $dialog = $("#attributes-dialog"); | ||||
|     const $saveAttributesButton = $("#save-attributes-button"); | ||||
|     const $attributesBody = $('#attributes-table tbody'); | ||||
|  | ||||
|     const attributesModel = new AttributesModel(); | ||||
|     let attributeNames = []; | ||||
|  | ||||
| @@ -24,10 +26,24 @@ const attributesDialog = (function() { | ||||
|  | ||||
|             // attribute might not be rendered immediatelly so could not focus | ||||
|             setTimeout(() => $(".attribute-name:last").focus(), 100); | ||||
|  | ||||
|             $attributesBody.sortable({ | ||||
|                 handle: '.handle', | ||||
|                 containment: $attributesBody, | ||||
|                 update: function() { | ||||
|                     let position = 0; | ||||
|  | ||||
|                     $attributesBody.find('input[name="position"]').each(function() { | ||||
|                         const attr = self.getTargetAttribute(this); | ||||
|  | ||||
|                         attr().position = position++; | ||||
|                     }); | ||||
|                 } | ||||
|             }); | ||||
|         }; | ||||
|  | ||||
|         this.deleteAttribute = function(data, event) { | ||||
|             const attr = self.getTargetAttribute(event); | ||||
|             const attr = self.getTargetAttribute(event.target); | ||||
|             const attrData = attr(); | ||||
|  | ||||
|             if (attrData) { | ||||
| @@ -53,7 +69,7 @@ const attributesDialog = (function() { | ||||
|             // we need to defocus from input (in case of enter-triggered save) because value is updated | ||||
|             // on blur event (because of conflict with jQuery UI Autocomplete). Without this, input would | ||||
|             // stay in focus, blur wouldn't be triggered and change wouldn't be updated in the viewmodel. | ||||
|             saveAttributesButton.focus(); | ||||
|             $saveAttributesButton.focus(); | ||||
|  | ||||
|             if (!isValid()) { | ||||
|                 alert("Please fix all validation errors and try saving again."); | ||||
| @@ -86,7 +102,8 @@ const attributesDialog = (function() { | ||||
|                     attributeId: '', | ||||
|                     name: '', | ||||
|                     value: '', | ||||
|                     isDeleted: 0 | ||||
|                     isDeleted: 0, | ||||
|                     position: 0 | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
| @@ -94,7 +111,7 @@ const attributesDialog = (function() { | ||||
|         this.attributeChanged = function (data, event) { | ||||
|             addLastEmptyRow(); | ||||
|  | ||||
|             const attr = self.getTargetAttribute(event); | ||||
|             const attr = self.getTargetAttribute(event.target); | ||||
|  | ||||
|             attr.valueHasMutated(); | ||||
|         }; | ||||
| @@ -123,8 +140,8 @@ const attributesDialog = (function() { | ||||
|             return cur.name.trim() === "" && (cur.attributeId !== "" || cur.value !== ""); | ||||
|         }; | ||||
|  | ||||
|         this.getTargetAttribute = function(event) { | ||||
|             const context = ko.contextFor(event.target); | ||||
|         this.getTargetAttribute = function(target) { | ||||
|             const context = ko.contextFor(target); | ||||
|             const index = context.$index(); | ||||
|  | ||||
|             return self.attributes()[index]; | ||||
| @@ -132,11 +149,11 @@ const attributesDialog = (function() { | ||||
|     } | ||||
|  | ||||
|     async function showDialog() { | ||||
|         glob.activeDialog = dialogEl; | ||||
|         glob.activeDialog = $dialog; | ||||
|  | ||||
|         await attributesModel.loadAttributes(); | ||||
|  | ||||
|         dialogEl.dialog({ | ||||
|         $dialog.dialog({ | ||||
|             modal: true, | ||||
|             width: 800, | ||||
|             height: 500 | ||||
|   | ||||
| @@ -12,7 +12,7 @@ const attributes = require('../../services/attributes'); | ||||
| router.get('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||
|     const noteId = req.params.noteId; | ||||
|  | ||||
|     res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY dateCreated", [noteId])); | ||||
|     res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); | ||||
| })); | ||||
|  | ||||
| router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||
| @@ -23,8 +23,8 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, | ||||
|     await sql.doInTransaction(async () => { | ||||
|         for (const attr of attributes) { | ||||
|             if (attr.attributeId) { | ||||
|                 await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ?, isDeleted = ? WHERE attributeId = ?", | ||||
|                     [attr.name, attr.value, now, attr.isDeleted, attr.attributeId]); | ||||
|                 await sql.execute("UPDATE attributes SET name = ?, value = ?, dateModified = ?, isDeleted = ?, position = ? WHERE attributeId = ?", | ||||
|                     [attr.name, attr.value, now, attr.isDeleted, attr.position, attr.attributeId]); | ||||
|             } | ||||
|             else { | ||||
|                 // if it was "created" and then immediatelly deleted, we just don't create it at all | ||||
| @@ -35,13 +35,14 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, | ||||
|                 attr.attributeId = utils.newAttributeId(); | ||||
|  | ||||
|                 await sql.insert("attributes", { | ||||
|                    attributeId: attr.attributeId, | ||||
|                    noteId: noteId, | ||||
|                    name: attr.name, | ||||
|                    value: attr.value, | ||||
|                    dateCreated: now, | ||||
|                    dateModified: now, | ||||
|                    isDeleted: false | ||||
|                     attributeId: attr.attributeId, | ||||
|                     noteId: noteId, | ||||
|                     name: attr.name, | ||||
|                     value: attr.value, | ||||
|                     position: attr.position, | ||||
|                     dateCreated: now, | ||||
|                     dateModified: now, | ||||
|                     isDeleted: false | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
| @@ -49,7 +50,7 @@ router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY dateCreated", [noteId])); | ||||
|     res.send(await sql.getRows("SELECT * FROM attributes WHERE isDeleted = 0 AND noteId = ? ORDER BY position, dateCreated", [noteId])); | ||||
| })); | ||||
|  | ||||
| router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| const build = require('./build'); | ||||
| const packageJson = require('../../package'); | ||||
|  | ||||
| const APP_DB_VERSION = 73; | ||||
| const APP_DB_VERSION = 74; | ||||
|  | ||||
| module.exports = { | ||||
|     app_version: packageJson.version, | ||||
|   | ||||
| @@ -383,6 +383,7 @@ | ||||
|         <table id="attributes-table" class="table"> | ||||
|           <thead> | ||||
|             <tr> | ||||
|               <th></th> | ||||
|               <th>ID</th> | ||||
|               <th>Name</th> | ||||
|               <th>Value</th> | ||||
| @@ -390,8 +391,13 @@ | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody data-bind="foreach: attributes"> | ||||
|            <tr data-bind="if: isDeleted == 0"> | ||||
|               <td data-bind="text: attributeId"></td> | ||||
|             <tr data-bind="if: isDeleted == 0"> | ||||
|               <td class="handle"> | ||||
|                 <span class="glyphicon glyphicon-resize-vertical"></span> | ||||
|                 <input type="hidden" name="position" data-bind="value: position"/> | ||||
|               </td> | ||||
|               <!-- ID column has specific width because if it's empty its size can be deformed when dragging --> | ||||
|               <td data-bind="text: attributeId" style="width: 150px;"></td> | ||||
|               <td> | ||||
|                 <!-- Change to valueUpdate: blur is necessary because jQuery UI autocomplete hijacks change event --> | ||||
|                 <input type="text" class="attribute-name" data-bind="value: name, valueUpdate: 'blur',  event: { blur: $parent.attributeChanged }"/> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user