UNPKG

@nextcloud/vue

Version:
1 lines 15.7 kB
{"version":3,"file":"NcSelectTags.mjs","sources":["../../src/components/NcSelectTags/api.js","../../src/components/NcSelectTags/NcSelectTags.vue"],"sourcesContent":["/**\n * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net>\n *\n * @author Julius Härtl <jus@bitgrid.net>\n *\n * @license AGPL-3.0-or-later\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU Affero General Public License as\n * published by the Free Software Foundation, either version 3 of the\n * License, or (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n * GNU Affero General Public License for more details.\n *\n * You should have received a copy of the GNU Affero General Public License\n * along with this program. If not, see <http://www.gnu.org/licenses/>.\n *\n */\n\nimport axios from '@nextcloud/axios'\nimport { generateRemoteUrl } from '@nextcloud/router'\n\nconst xmlToJson = (xml) => {\n\tlet obj = {}\n\n\tif (xml.nodeType === 1) {\n\t\tif (xml.attributes.length > 0) {\n\t\t\tobj['@attributes'] = {}\n\t\t\tfor (let j = 0; j < xml.attributes.length; j++) {\n\t\t\t\tconst attribute = xml.attributes.item(j)\n\t\t\t\tobj['@attributes'][attribute.nodeName] = attribute.nodeValue\n\t\t\t}\n\t\t}\n\t} else if (xml.nodeType === 3) {\n\t\tobj = xml.nodeValue\n\t}\n\n\tif (xml.hasChildNodes()) {\n\t\tfor (let i = 0; i < xml.childNodes.length; i++) {\n\t\t\tconst item = xml.childNodes.item(i)\n\t\t\tconst nodeName = item.nodeName\n\t\t\tif (typeof (obj[nodeName]) === 'undefined') {\n\t\t\t\tobj[nodeName] = xmlToJson(item)\n\t\t\t} else {\n\t\t\t\tif (typeof obj[nodeName].push === 'undefined') {\n\t\t\t\t\tconst old = obj[nodeName]\n\t\t\t\t\tobj[nodeName] = []\n\t\t\t\t\tobj[nodeName].push(old)\n\t\t\t\t}\n\t\t\t\tobj[nodeName].push(xmlToJson(item))\n\t\t\t}\n\t\t}\n\t}\n\treturn obj\n}\n\nconst parseXml = (xml) => {\n\tlet dom = null\n\ttry {\n\t\tdom = (new DOMParser()).parseFromString(xml, 'text/xml')\n\t} catch (e) {\n\t\tconsole.error('Failed to parse xml document', e)\n\t}\n\treturn dom\n}\n\nconst xmlToTagList = (xml) => {\n\tconst json = xmlToJson(parseXml(xml))\n\tconst list = json['d:multistatus']['d:response']\n\tconst result = []\n\tfor (const index in list) {\n\t\tconst tag = list[index]['d:propstat']\n\n\t\tif (tag['d:status']['#text'] !== 'HTTP/1.1 200 OK') {\n\t\t\tcontinue\n\t\t}\n\t\tresult.push({\n\t\t\tid: parseInt(tag['d:prop']['oc:id']['#text']),\n\t\t\tdisplayName: tag['d:prop']['oc:display-name']['#text'],\n\t\t\tcanAssign: tag['d:prop']['oc:can-assign']['#text'] === 'true',\n\t\t\tuserAssignable: tag['d:prop']['oc:user-assignable']['#text'] === 'true',\n\t\t\tuserVisible: tag['d:prop']['oc:user-visible']['#text'] === 'true',\n\t\t})\n\t}\n\treturn result\n}\n\nconst searchTags = async function() {\n\tif (window.NextcloudVueDocs) {\n\t\treturn Promise.resolve(xmlToTagList(window.NextcloudVueDocs.tags))\n\t}\n\n\tconst result = await axios({\n\t\tmethod: 'PROPFIND',\n\t\turl: generateRemoteUrl('dav') + '/systemtags/',\n\t\tdata: `<?xml version=\"1.0\"?>\n\t\t\t\t\t<d:propfind xmlns:d=\"DAV:\" xmlns:oc=\"http://owncloud.org/ns\">\n\t\t\t\t\t <d:prop>\n\t\t\t\t\t\t<oc:id />\n\t\t\t\t\t\t<oc:display-name />\n\t\t\t\t\t\t<oc:user-visible />\n\t\t\t\t\t\t<oc:user-assignable />\n\t\t\t\t\t\t<oc:can-assign />\n\t\t\t\t\t </d:prop>\n\t\t\t\t\t</d:propfind>`,\n\t})\n\treturn xmlToTagList(result.data)\n}\n\nexport {\n\tsearchTags,\n}\n","<!--\n - @copyright 2022 Christopher Ng <chrng8@gmail.com>\n -\n - @author Christopher Ng <chrng8@gmail.com>\n - @author Julius Härtl <jus@bitgrid.net>\n -\n - @license AGPL-3.0-or-later\n -\n - This program is free software: you can redistribute it and/or modify\n - it under the terms of the GNU Affero General Public License as\n - published by the Free Software Foundation, either version 3 of the\n - License, or (at your option) any later version.\n -\n - This program is distributed in the hope that it will be useful,\n - but WITHOUT ANY WARRANTY; without even the implied warranty of\n - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n - GNU Affero General Public License for more details.\n -\n - You should have received a copy of the GNU Affero General Public License\n - along with this program. If not, see <http://www.gnu.org/licenses/>.\n -\n-->\n\n<docs>\n### Single tag selector\n\n```vue\n<template>\n\t<div class=\"wrapper\">\n\t\t<NcSelectTags v-model=\"value\" :multiple=\"false\" />\n\t\t{{ value }}\n\t</div>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tvalue: 1,\n\t\t}\n\t}\n}\n</script>\n```\n\n### Multiple tag selector\n```vue\n<template>\n\t<div class=\"wrapper\">\n\t\t<NcSelectTags v-model=\"value\" :multiple=\"true\" />\n\t\t{{ value }}\n\t</div>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tvalue: [\n\t\t\t\t1,\n\t\t\t\t2,\n\t\t\t],\n\t\t}\n\t}\n}\n</script>\n```\n\n### Custom filter\nBecause of compatibility reasons only 5 tag entries are shown by default. If you want to show all available tags set the `limit` prop to `null`:\n```vue\n<template>\n\t<div class=\"wrapper\">\n\t\t<NcSelectTags v-model=\"value\" :limit=\"null\" />\n\t\t{{ value }}\n\t</div>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tvalue: [\n\t\t\t\t1,\n\t\t\t\t2,\n\t\t\t],\n\t\t}\n\t}\n}\n</script>\n```\n\nIt's also possible to apply any custom filter logic by setting the `optionsFilter` function-prop to any custom function receiving the tag element and the index:\n```vue\n<template>\n\t<div class=\"wrapper\">\n\t\t<NcSelectTags v-model=\"value\" :options-filter=\"customFilter\" />\n\t\t{{ value }}\n\t</div>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\tvalue: [\n\t\t\t\t3,\n\t\t\t],\n\t\t}\n\t},\n\tmethods: {\n\t\t/**\n\t\t* Implement your custom filter logic to filter tags delivered\n\t\t* by the server\n\t\t*\n\t\t* @param {object} the tag object\n\t\t* @param {int} the index of the object inside the collection\n\t\t*/\n\t\tcustomFilter(element, index) {\n\t\t\t/*\n\t\t\t* Tag objects returned by the server will have the\n\t\t\t* following properties (see also api.js):\n\t\t\t* id, displayName, canAssign, userAssignable, userVisible\n\t\t\t*/\n\t\t\treturn element.id >= 2 && element.displayName !== '' && element.canAssign && element.userAssignable && element.userVisible\n\t\t}\n\t}\n}\n</script>\n```\n</docs>\n\n<template>\n\t<NcSelect v-bind=\"propsToForward\"\n\t\t:options=\"availableOptions\"\n\t\t:close-on-select=\"!multiple\"\n\t\t:value=\"passthru ? value : localValue\"\n\t\t@search=\"searchString => search = searchString\"\n\t\tv-on=\"{\n\t\t\t...$listeners,\n\t\t\tinput: passthru ? $listeners.input : handleInput,\n\t\t}\">\n\t\t<template #option=\"option\">\n\t\t\t<NcEllipsisedOption :name=\"getOptionLabel(option)\"\n\t\t\t\t:search=\"search\" />\n\t\t</template>\n\t\t<template #selected-option=\"selectedOption\">\n\t\t\t<NcEllipsisedOption :name=\"getOptionLabel(selectedOption)\"\n\t\t\t\t:search=\"search\" />\n\t\t</template>\n\t\t<template v-for=\"(_, name) in $scopedSlots\" #[name]=\"data\">\n\t\t\t<!-- @slot Any combination of slots from https://vue-select.org/api/slots.html -->\n\t\t\t<slot :name=\"name\" v-bind=\"data\" />\n\t\t</template>\n\t</NcSelect>\n</template>\n\n<script>\nimport NcEllipsisedOption from '../NcEllipsisedOption/index.js'\nimport NcSelect from '../NcSelect/index.js'\n\nimport { searchTags } from './api.js'\nimport { t } from '../../l10n.js'\n\nexport default {\n\tname: 'NcSelectTags',\n\n\tcomponents: {\n\t\tNcEllipsisedOption,\n\t\tNcSelect,\n\t},\n\n\tprops: {\n\t\t// Add NcSelect prop defaults and populate $props\n\t\t...NcSelect.props,\n\n\t\t/**\n\t\t * Enable automatic fetching of tags\n\t\t *\n\t\t * If `false`, available tags must be passed using the `options` prop\n\t\t */\n\t\tfetchTags: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Callback to generate the label text\n\t\t *\n\t\t * @see https://vue-select.org/api/props.html#getoptionlabel\n\t\t */\n\t\tgetOptionLabel: {\n\t\t\ttype: Function,\n\t\t\tdefault: (option) => {\n\t\t\t\tconst { displayName, userVisible, userAssignable } = option\n\t\t\t\tif (userVisible === false) {\n\t\t\t\t\treturn t('{tag} (invisible)', { tag: displayName })\n\t\t\t\t}\n\t\t\t\tif (userAssignable === false) {\n\t\t\t\t\treturn t('{tag} (restricted)', { tag: displayName })\n\t\t\t\t}\n\t\t\t\treturn displayName\n\t\t\t},\n\t\t},\n\n\t\t/**\n\t\t * Sets the maximum number of tags to display in the dropdown list\n\t\t *\n\t\t * Because of compatibility reasons only 5 tag entries are shown by\n\t\t * default\n\t\t */\n\t\tlimit: {\n\t\t\ttype: Number,\n\t\t\tdefault: 5,\n\t\t},\n\n\t\t/**\n\t\t * Allow selection of multiple options\n\t\t *\n\t\t * This prop automatically sets the internal `closeOnSelect` prop to\n\t\t * its boolean opposite\n\t\t *\n\t\t * @see https://vue-select.org/api/props.html#multiple\n\t\t */\n\t\tmultiple: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: true,\n\t\t},\n\n\t\t/**\n\t\t * Callback to filter available options\n\t\t */\n\t\toptionsFilter: {\n\t\t\ttype: Function,\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Enable passing of `value` prop and emitted `input` events as-is\n\t\t * i.e. for usage with `v-model`\n\t\t *\n\t\t * If `true`, custom internal `value` and `input` handling is disabled\n\t\t */\n\t\tpassthru: {\n\t\t\ttype: Boolean,\n\t\t\tdefault: false,\n\t\t},\n\n\t\t/**\n\t\t * Placeholder text\n\t\t *\n\t\t * @see https://vue-select.org/api/props.html#placeholder\n\t\t */\n\t\tplaceholder: {\n\t\t\ttype: String,\n\t\t\tdefault: t('Select a tag'),\n\t\t},\n\n\t\t/**\n\t\t * Currently selected value\n\t\t */\n\t\tvalue: {\n\t\t\ttype: [Number, Array],\n\t\t\tdefault: null,\n\t\t},\n\n\t\t/**\n\t\t * Any available prop\n\t\t *\n\t\t * @see https://vue-select.org/api/props.html\n\t\t */\n\t\t// Not an actual prop but needed to show in vue-styleguidist docs\n\t\t// eslint-disable-next-line\n\t\t' ': {},\n\t},\n\n\temits: [\n\t\t'input',\n\t\t/**\n\t\t * All events from https://vue-select.org/api/events.html\n\t\t */\n\t\t// Not an actual event but needed to show in vue-styleguidist docs\n\t\t' ',\n\t],\n\n\tdata() {\n\t\treturn {\n\t\t\tsearch: '',\n\t\t\tavailableTags: [],\n\t\t}\n\t},\n\n\tcomputed: {\n\t\tavailableOptions() {\n\t\t\tif (this.optionsFilter) {\n\t\t\t\treturn this.tags.filter(this.optionsFilter)\n\t\t\t}\n\t\t\treturn this.tags\n\t\t},\n\n\t\tlocalValue() {\n\t\t\tif (this.tags.length === 0) {\n\t\t\t\treturn []\n\t\t\t}\n\t\t\tif (this.multiple) {\n\t\t\t\treturn this.value\n\t\t\t\t\t.filter(tag => tag !== '')\n\t\t\t\t\t.map(id => this.tags.find(tag2 => tag2.id === id))\n\t\t\t} else {\n\t\t\t\treturn this.tags.find(tag => tag.id === this.value)\n\t\t\t}\n\t\t},\n\n\t\tpropsToForward() {\n\t\t\tconst {\n\t\t\t\t// Props handled by this component\n\t\t\t\tfetchTags,\n\t\t\t\toptionsFilter,\n\t\t\t\tpassthru,\n\t\t\t\t// Props to forward\n\t\t\t\t...propsToForward\n\t\t\t} = this.$props\n\n\t\t\treturn propsToForward\n\t\t},\n\n\t\ttags() {\n\t\t\tif (!this.fetchTags) {\n\t\t\t\treturn this.options\n\t\t\t}\n\t\t\treturn this.availableTags\n\t\t},\n\t},\n\n\tasync created() {\n\t\tif (!this.fetchTags) {\n\t\t\treturn\n\t\t}\n\t\ttry {\n\t\t\tconst result = await searchTags()\n\t\t\tthis.availableTags = result\n\t\t} catch (error) {\n\t\t\tconsole.error('Loading systemtags failed', error)\n\t\t}\n\t},\n\n\tmethods: {\n\t\thandleInput(value) {\n\t\t\tif (this.multiple) {\n\t\t\t\t/**\n\t\t\t\t * Emitted on input events of the multiselect field\n\t\t\t\t *\n\t\t\t\t * @type {number|number[]}\n\t\t\t\t */\n\t\t\t\tthis.$emit('input', value.map(element => element.id))\n\t\t\t} else {\n\t\t\t\tif (value === null) {\n\t\t\t\t\tthis.$emit('input', null)\n\t\t\t\t} else {\n\t\t\t\t\tthis.$emit('input', value.id)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t},\n}\n</script>\n"],"names":["xmlToJson","xml","obj","j","attribute","i","item","nodeName","old","parseXml","dom","e","xmlToTagList","list","result","index","tag","searchTags","axios","generateRemoteUrl","_sfc_main","NcEllipsisedOption","NcSelect","option","displayName","userVisible","userAssignable","t","id","tag2","fetchTags","optionsFilter","passthru","propsToForward","error","value","element"],"mappings":"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"}