mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 10:26:08 +01:00 
			
		
		
		
	autocomplete for attribute names, issue #31
This commit is contained in:
		| @@ -3,6 +3,7 @@ | |||||||
| const attributesDialog = (function() { | const attributesDialog = (function() { | ||||||
|     const dialogEl = $("#attributes-dialog"); |     const dialogEl = $("#attributes-dialog"); | ||||||
|     const attributesModel = new AttributesModel(); |     const attributesModel = new AttributesModel(); | ||||||
|  |     let attributeNames = []; | ||||||
|  |  | ||||||
|     function AttributesModel() { |     function AttributesModel() { | ||||||
|         const self = this; |         const self = this; | ||||||
| @@ -17,6 +18,10 @@ const attributesDialog = (function() { | |||||||
|             self.attributes(attributes.map(ko.observable)); |             self.attributes(attributes.map(ko.observable)); | ||||||
|  |  | ||||||
|             addLastEmptyRow(); |             addLastEmptyRow(); | ||||||
|  |  | ||||||
|  |             attributeNames = await server.get('attributes/names'); | ||||||
|  |  | ||||||
|  |             $(".attribute-name:last").focus(); | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         function isValid() { |         function isValid() { | ||||||
| @@ -54,11 +59,7 @@ const attributesDialog = (function() { | |||||||
|             const attrs = self.attributes(); |             const attrs = self.attributes(); | ||||||
|             const last = attrs[attrs.length - 1](); |             const last = attrs[attrs.length - 1](); | ||||||
|  |  | ||||||
| //            console.log("last", attrs.map(attr => attr())); |  | ||||||
|  |  | ||||||
|             if (last.name.trim() !== "" || last.value !== "") { |             if (last.name.trim() !== "" || last.value !== "") { | ||||||
|                 console.log("Adding new row"); |  | ||||||
|  |  | ||||||
|                 self.attributes.push(ko.observable({ |                 self.attributes.push(ko.observable({ | ||||||
|                     attributeId: '', |                     attributeId: '', | ||||||
|                     name: '', |                     name: '', | ||||||
| @@ -68,8 +69,6 @@ const attributesDialog = (function() { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         this.attributeChanged = function (row) { |         this.attributeChanged = function (row) { | ||||||
|             console.log(row); |  | ||||||
|  |  | ||||||
|             addLastEmptyRow(); |             addLastEmptyRow(); | ||||||
|  |  | ||||||
|             for (const attr of self.attributes()) { |             for (const attr of self.attributes()) { | ||||||
| @@ -124,6 +123,22 @@ const attributesDialog = (function() { | |||||||
|  |  | ||||||
|     ko.applyBindings(attributesModel, document.getElementById('attributes-dialog')); |     ko.applyBindings(attributesModel, document.getElementById('attributes-dialog')); | ||||||
|  |  | ||||||
|  |     $(document).on('focus', '.attribute-name:not(.ui-autocomplete-input)', function (e) { | ||||||
|  |         $(this).autocomplete({ | ||||||
|  |             // shouldn't be required and autocomplete should just accept array of strings, but that fails | ||||||
|  |             // because we have overriden filter() function in init.js | ||||||
|  |             source: attributeNames.map(attr => { | ||||||
|  |                 return { | ||||||
|  |                     label: attr, | ||||||
|  |                     value: attr | ||||||
|  |                 } | ||||||
|  |             }), | ||||||
|  |             minLength: 0 | ||||||
|  |         }); | ||||||
|  |  | ||||||
|  |         $(this).autocomplete("search", $(this).val()); | ||||||
|  |     }); | ||||||
|  |  | ||||||
|     return { |     return { | ||||||
|         showDialog |         showDialog | ||||||
|     }; |     }; | ||||||
|   | |||||||
| @@ -105,7 +105,7 @@ $(window).on('beforeunload', () => { | |||||||
| // Overrides the default autocomplete filter function to search for matched on atleast 1 word in each of the input term's words | // Overrides the default autocomplete filter function to search for matched on atleast 1 word in each of the input term's words | ||||||
| $.ui.autocomplete.filter = (array, terms) => { | $.ui.autocomplete.filter = (array, terms) => { | ||||||
|     if (!terms) { |     if (!terms) { | ||||||
|         return []; |         return array; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const startDate = new Date(); |     const startDate = new Date(); | ||||||
|   | |||||||
| @@ -7,14 +7,15 @@ const auth = require('../../services/auth'); | |||||||
| const sync_table = require('../../services/sync_table'); | const sync_table = require('../../services/sync_table'); | ||||||
| const utils = require('../../services/utils'); | const utils = require('../../services/utils'); | ||||||
| const wrap = require('express-promise-wrap').wrap; | const wrap = require('express-promise-wrap').wrap; | ||||||
|  | const attributes = require('../../services/attributes'); | ||||||
|  |  | ||||||
| router.get('/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { | router.get('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|  |  | ||||||
|     res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId])); |     res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId])); | ||||||
| })); | })); | ||||||
|  |  | ||||||
| router.put('/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { | router.put('/notes/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||||
|     const noteId = req.params.noteId; |     const noteId = req.params.noteId; | ||||||
|     const attributes = req.body; |     const attributes = req.body; | ||||||
|     const now = utils.nowDate(); |     const now = utils.nowDate(); | ||||||
| @@ -45,4 +46,20 @@ router.put('/:noteId/attributes', auth.checkApiAuth, wrap(async (req, res, next) | |||||||
|     res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId])); |     res.send(await sql.getRows("SELECT * FROM attributes WHERE noteId = ? ORDER BY dateCreated", [noteId])); | ||||||
| })); | })); | ||||||
|  |  | ||||||
|  | router.get('/attributes/names', auth.checkApiAuth, wrap(async (req, res, next) => { | ||||||
|  |     const noteId = req.params.noteId; | ||||||
|  |  | ||||||
|  |     const names = await sql.getColumn("SELECT DISTINCT name FROM attributes"); | ||||||
|  |  | ||||||
|  |     for (const attr of attributes.BUILTIN_ATTRIBUTES) { | ||||||
|  |         if (!names.includes(attr)) { | ||||||
|  |             names.push(attr); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     names.sort(); | ||||||
|  |  | ||||||
|  |     res.send(names); | ||||||
|  | })); | ||||||
|  |  | ||||||
| module.exports = router; | module.exports = router; | ||||||
| @@ -40,7 +40,7 @@ function register(app) { | |||||||
|     app.use('/api/notes', notesApiRoute); |     app.use('/api/notes', notesApiRoute); | ||||||
|     app.use('/api/tree', treeChangesApiRoute); |     app.use('/api/tree', treeChangesApiRoute); | ||||||
|     app.use('/api/notes', cloningApiRoute); |     app.use('/api/notes', cloningApiRoute); | ||||||
|     app.use('/api/notes', attributesRoute); |     app.use('/api', attributesRoute); | ||||||
|     app.use('/api/notes-history', noteHistoryApiRoute); |     app.use('/api/notes-history', noteHistoryApiRoute); | ||||||
|     app.use('/api/recent-changes', recentChangesApiRoute); |     app.use('/api/recent-changes', recentChangesApiRoute); | ||||||
|     app.use('/api/settings', settingsApiRoute); |     app.use('/api/settings', settingsApiRoute); | ||||||
|   | |||||||
| @@ -5,6 +5,8 @@ const utils = require('./utils'); | |||||||
| const sync_table = require('./sync_table'); | const sync_table = require('./sync_table'); | ||||||
| const Repository = require('./repository'); | const Repository = require('./repository'); | ||||||
|  |  | ||||||
|  | const BUILTIN_ATTRIBUTES = [ 'run_on_startup', 'disable_versioning' ]; | ||||||
|  |  | ||||||
| async function getNoteAttributeMap(noteId) { | async function getNoteAttributeMap(noteId) { | ||||||
|     return await sql.getMap(`SELECT name, value FROM attributes WHERE noteId = ?`, [noteId]); |     return await sql.getMap(`SELECT name, value FROM attributes WHERE noteId = ?`, [noteId]); | ||||||
| } | } | ||||||
| @@ -64,5 +66,6 @@ module.exports = { | |||||||
|     getNotesWithAttribute, |     getNotesWithAttribute, | ||||||
|     getNoteWithAttribute, |     getNoteWithAttribute, | ||||||
|     getNoteIdsWithAttribute, |     getNoteIdsWithAttribute, | ||||||
|     createAttribute |     createAttribute, | ||||||
|  |     BUILTIN_ATTRIBUTES | ||||||
| }; | }; | ||||||
| @@ -400,7 +400,7 @@ | |||||||
|             <tr> |             <tr> | ||||||
|               <td data-bind="text: attributeId"></td> |               <td data-bind="text: attributeId"></td> | ||||||
|               <td> |               <td> | ||||||
|                 <input type="text" data-bind="value: name, event: { change: $parent.attributeChanged }"/> |                 <input type="text" class="attribute-name" data-bind="value: name, event: { change: $parent.attributeChanged }"/> | ||||||
|  |  | ||||||
|                 <div style="color: red" data-bind="if: $parent.isNotUnique($index())">Attribute name must be unique per note.</div> |                 <div style="color: red" data-bind="if: $parent.isNotUnique($index())">Attribute name must be unique per note.</div> | ||||||
|                 <div style="color: red" data-bind="if: $parent.isEmptyName($index())">Attribute name can't be empty.</div> |                 <div style="color: red" data-bind="if: $parent.isEmptyName($index())">Attribute name can't be empty.</div> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user