feat: content link states
This commit is contained in:
		| @@ -71,6 +71,7 @@ | ||||
|                 single-line, | ||||
|                 solo | ||||
|                 flat | ||||
|                 rounded | ||||
|                 hide-details, | ||||
|                 prepend-inner-icon='mdi-magnify', | ||||
|                 :loading='searchIsLoading', | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
|           .overline.mr-3.animated.fadeInLeft Current Selection | ||||
|           v-chip.mr-3.primary--text( | ||||
|             v-for='tag of tagsSelected' | ||||
|             :key='`tagSelected-` + tag.tag' | ||||
|             color='white' | ||||
|             close | ||||
|             @click:close='toggleTag(tag.tag)' | ||||
| @@ -38,7 +39,7 @@ | ||||
|         template(v-else) | ||||
|           v-icon.mr-3.animated.fadeInRight mdi-arrow-left | ||||
|           .overline.animated.fadeInRight Select one or more tags | ||||
|       v-toolbar(color='grey lighten-4', flat, height='58') | ||||
|       v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-l5` : `grey lighten-4`', flat, height='58') | ||||
|         v-text-field.tags-search( | ||||
|           label='Search within results...' | ||||
|           solo | ||||
| @@ -50,12 +51,29 @@ | ||||
|           prepend-icon='mdi-file-document-box-search-outline' | ||||
|           append-icon='mdi-arrow-right' | ||||
|         ) | ||||
|         template(v-if='locales.length > 1') | ||||
|           v-divider.mx-3(vertical) | ||||
|           .overline Locale | ||||
|           v-select.ml-2( | ||||
|             :items='locales' | ||||
|             v-model='locale' | ||||
|             :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`' | ||||
|             hide-details | ||||
|             label='Locale' | ||||
|             item-text='name' | ||||
|             item-value='code' | ||||
|             rounded | ||||
|             single-line | ||||
|             dense | ||||
|             height='40' | ||||
|             style='max-width: 170px;' | ||||
|           ) | ||||
|         v-divider.mx-3(vertical) | ||||
|         .overline Order By | ||||
|         v-select.ml-2( | ||||
|           :items='orderByItems' | ||||
|           v-model='orderBy' | ||||
|           background-color='white' | ||||
|           :background-color='$vuetify.theme.dark ? `grey darken-3` : `white`' | ||||
|           hide-details | ||||
|           label='Order By' | ||||
|           rounded | ||||
| @@ -64,12 +82,13 @@ | ||||
|           height='40' | ||||
|           style='max-width: 250px;' | ||||
|         ) | ||||
|         v-divider.mx-3(vertical) | ||||
|         v-btn-toggle(v-model='displayStyle', rounded, mandatory) | ||||
|           v-btn(text, height='40'): v-icon(small) mdi-view-list | ||||
|           v-btn(text, height='40'): v-icon(small) mdi-cards-variant | ||||
|           v-btn(text, height='40'): v-icon(small) mdi-format-align-justify | ||||
|         v-btn-toggle.ml-2(v-model='orderByDirection', rounded, mandatory) | ||||
|           v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-up | ||||
|           v-btn(text, height='40'): v-icon(size='20') mdi-chevron-double-down | ||||
|       v-divider | ||||
|       .text-center.pt-10 | ||||
|         img(src='/svg/icon-price-tag.svg') | ||||
|         .subtitle-2.grey--text Select one or more tags on the left. | ||||
|     nav-footer | ||||
|     notify | ||||
|     search-results | ||||
| @@ -77,16 +96,25 @@ | ||||
|  | ||||
| <script> | ||||
| import { get } from 'vuex-pathify' | ||||
| import VueRouter from 'vue-router' | ||||
| import _ from 'lodash' | ||||
|  | ||||
| import tagsQuery from 'gql/common/common-pages-query-tags.gql' | ||||
|  | ||||
| /* global siteLangs */ | ||||
|  | ||||
| const router = new VueRouter({ | ||||
|   mode: 'history', | ||||
|   base: '/t' | ||||
| }) | ||||
|  | ||||
| export default { | ||||
|   data() { | ||||
|     return { | ||||
|       tags: [], | ||||
|       selection: [], | ||||
|       displayStyle: 0, | ||||
|       locale: 'any', | ||||
|       locales: [], | ||||
|       orderBy: 'TITLE', | ||||
|       orderByItems: [ | ||||
|         { text: 'Creation Date', value: 'CREATED' }, | ||||
| @@ -95,6 +123,7 @@ export default { | ||||
|         { text: 'Path', value: 'PATH' }, | ||||
|         { text: 'Title', value: 'TITLE' } | ||||
|       ], | ||||
|       orderByDirection: 0, | ||||
|       scrollStyle: { | ||||
|         vuescroll: {}, | ||||
|         scrollPanel: { | ||||
| @@ -127,8 +156,27 @@ export default { | ||||
|       return _.filter(this.tags, t => _.includes(this.selection, t.tag)) | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     locale (newValue, oldValue) { | ||||
|       this.rebuildURL() | ||||
|     }, | ||||
|     orderBy (newValue, oldValue) { | ||||
|       this.rebuildURL() | ||||
|     }, | ||||
|     orderByDirection (newValue, oldValue) { | ||||
|       this.rebuildURL() | ||||
|     } | ||||
|   }, | ||||
|   router, | ||||
|   created () { | ||||
|     this.$store.commit('page/SET_MODE', 'tags') | ||||
|  | ||||
|     this.locales = _.concat( | ||||
|       [{name: 'Any', code: 'any'}], | ||||
|       (siteLangs.length > 0 ? siteLangs : []) | ||||
|     ) | ||||
|  | ||||
|     this.selection = _.compact(this.$route.path.split('/')) | ||||
|   }, | ||||
|   methods: { | ||||
|     toggleTag (tag) { | ||||
| @@ -137,9 +185,25 @@ export default { | ||||
|       } else { | ||||
|         this.selection.push(tag) | ||||
|       } | ||||
|       this.rebuildURL() | ||||
|     }, | ||||
|     isSelected (tag) { | ||||
|       return _.includes(this.selection, tag) | ||||
|     }, | ||||
|     rebuildURL () { | ||||
|       let urlObj = { | ||||
|         path: '/' + this.selection.join('/') | ||||
|       } | ||||
|       if (this.locale !== `any`) { | ||||
|         _.set(urlObj, 'query.lang', this.locale) | ||||
|       } | ||||
|       if (this.orderBy !== `TITLE`) { | ||||
|         _.set(urlObj, 'query.sort', this.orderBy.toLowerCase()) | ||||
|       } | ||||
|       if (this.orderByDirection !== 0) { | ||||
|         _.set(urlObj, 'query.dir', this.orderByDirection === 0 ? `asc` : `desc`) | ||||
|       } | ||||
|       this.$router.push(urlObj) | ||||
|     } | ||||
|   }, | ||||
|   apollo: { | ||||
|   | ||||
							
								
								
									
										1
									
								
								client/static/svg/icon-price-tag.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								client/static/svg/icon-price-tag.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" width="256" height="256"><path fill="#f7f7fb" d="M43.5,28L16.9,45.4c-5,2.3-2.3,9.1,0,14.1l17.9,17.9l62.2,23l17-47L43.5,28 M35.3,59.5 c-3.9-1.4-5.9-5.7-4.5-9.6c1.4-3.9,5.7-5.9,9.6-4.5s5.9,5.7,4.5,9.6C43.5,58.9,39.2,61,35.3,59.5"/><path fill="#dfdfe3" d="M30.353,52.448c0-0.846,0.144-1.706,0.446-2.548c1.098-3.057,3.977-4.946,7.048-4.946 c0.847,0,1.709,0.144,2.552,0.446l0,0c3.065,1.1,4.957,3.993,4.946,7.074C45.349,53.317,45.206,54.17,44.9,55l0,0 c-1.091,3.04-3.945,4.986-6.997,4.986c-0.864,0-1.743-0.156-2.603-0.486C32.242,58.402,30.352,55.521,30.353,52.448 M43.5,28 L16.9,45.4c-2.078,0.956-2.826,2.689-2.825,4.746c0,2.894,1.481,6.431,2.825,9.354l17.9,17.9l21.635,8H102.4l11.6-32L43.5,28"/><path fill="#454b54" d="M97,103.5c-0.4,0-0.7-0.1-1-0.2l-62.2-23c-0.4-0.2-0.8-0.4-1.1-0.7L14.8,61.7 c-0.6-0.6-0.9-1.3-0.9-2.1V45.4c0-1,0.5-2,1.4-2.5l26.6-17.5c0.8-0.5,1.8-0.6,2.7-0.3l70.5,25.5c0.7,0.3,1.4,0.8,1.7,1.5 s0.4,1.5,0.1,2.3l-17,47c-0.3,0.8-0.8,1.4-1.6,1.7C97.9,103.4,97.4,103.5,97,103.5z M36.5,74.9l58.7,21.7l15-41.4l-66.3-24L19.9,47 v11.3L36.5,74.9z"/><path fill="#6ec7b0" d="M34.8,27.5L16.9,45.4c-3.9,3.9-3.9,10.2,0,14.1l17.9,17.9h75v-50h-75V27.5z M37.8,60 c-4.1,0-7.5-3.4-7.5-7.5s3.4-7.5,7.5-7.5s7.5,3.4,7.5,7.5S42,60,37.8,60z"/><path fill="#454b54" d="M109.8,80.5h-75c-0.8,0-1.6-0.3-2.1-0.9L14.8,61.7C12.3,59.2,11,56,11,52.5s1.4-6.7,3.8-9.2 l17.9-17.9c0.6-0.6,1.3-0.9,2.1-0.9h75c1.7,0,3,1.3,3,3v50C112.8,79.2,111.5,80.5,109.8,80.5z M36.1,74.5h70.8v-44H36.1L19,47.5 c-1.3,1.3-2,3.1-2,5s0.7,3.6,2,4.9L36.1,74.5z"/><path fill="#fff" d="M89.8 50.5h-25c-1.7 0-3-1.3-3-3s1.3-3 3-3h25c1.7 0 3 1.3 3 3S91.5 50.5 89.8 50.5zM89.8 60.5h-25c-1.7 0-3-1.3-3-3s1.3-3 3-3h25c1.7 0 3 1.3 3 3S91.5 60.5 89.8 60.5z"/><path fill="#454b54" d="M7.8,85.5c-0.8,0-1.5-0.3-2.1-0.9c-1.2-1.2-1.2-3.1,0-4.2l9.1-9.1c1.2-1.2,3.1-1.2,4.2,0 c1.2,1.2,1.2,3.1,0,4.2l-9.1,9.1C9.4,85.2,8.6,85.5,7.8,85.5z"/></svg> | ||||
| After Width: | Height: | Size: 1.9 KiB | 
| @@ -10,11 +10,40 @@ | ||||
|   } | ||||
|  | ||||
|   @at-root .theme--dark & { | ||||
|     // background-color: darken(mc('grey', '900'), 4%); | ||||
|     color: mc('grey', '300'); | ||||
|   } | ||||
|  | ||||
|   // --------------------------------- | ||||
|   // LINKS | ||||
|   // --------------------------------- | ||||
|  | ||||
|   a { | ||||
|       color: mc('blue', '100'); | ||||
|     color: mc('blue', '700'); | ||||
|  | ||||
|     &.is-internal-link.is-invalid-page { | ||||
|       color: mc('red', '700'); | ||||
|  | ||||
|       @at-root .theme--dark & { | ||||
|         color: mc('red', '200'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     &.is-external-link { | ||||
|       padding-right: 3px; | ||||
|  | ||||
|       &::after { | ||||
|         font-family: 'Material Design Icons'; | ||||
|         font-size: 24px/1; | ||||
|         padding-left: 3px; | ||||
|         display: inline-block; | ||||
|         content: '\F3CC'; | ||||
|         color: mc('grey', '500'); | ||||
|         text-decoration: none; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     @at-root .theme--dark & { | ||||
|       color: mc('blue', '200'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -22,7 +22,7 @@ module.exports = { | ||||
|         name: _.get(c, 'name', 'Anonymous') || '', | ||||
|         profile: _.get(c, 'profile', ''), | ||||
|         tier: _.toLower(_.get(c, 'tier', 'backers')), | ||||
|         totalDonated: _.get(c, 'totalAmountDonated', 0), | ||||
|         totalDonated: Math.ceil(_.get(c, 'totalAmountDonated', 0)), | ||||
|         twitter: _.get(c, 'twitter', '') || '', | ||||
|         website: _.get(c, 'website', '') || '' | ||||
|       })) | ||||
|   | ||||
| @@ -8,6 +8,8 @@ module.exports = async (pageId) => { | ||||
|  | ||||
|   try { | ||||
|     WIKI.models = require('../core/db').init() | ||||
|     await WIKI.configSvc.loadFromDb() | ||||
|     await WIKI.configSvc.applyFlags() | ||||
|  | ||||
|     const page = await WIKI.models.pages.getPageFromDb(pageId) | ||||
|     if (!page) { | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| const _ = require('lodash') | ||||
| const cheerio = require('cheerio') | ||||
|  | ||||
| /* global WIKI */ | ||||
|  | ||||
| module.exports = { | ||||
|   async render() { | ||||
|     const $ = cheerio.load(this.input) | ||||
| @@ -14,6 +16,98 @@ module.exports = { | ||||
|       renderer.init($, child.config) | ||||
|     } | ||||
|  | ||||
|     // -------------------------------- | ||||
|     // Detect internal / external links | ||||
|     // -------------------------------- | ||||
|  | ||||
|     let internalRefs = [] | ||||
|     const reservedPrefixes = /^\/[a-z]\//gi | ||||
|  | ||||
|     const isHostSet = WIKI.config.host.length > 7 && WIKI.config.host !== 'http://' | ||||
|     if (!isHostSet) { | ||||
|       WIKI.logger.warn('Host is not set. You must set the Site Host under General in the Administration Area!') | ||||
|     } | ||||
|  | ||||
|     $('a').each((i, elm) => { | ||||
|       let href = $(elm).attr('href') | ||||
|  | ||||
|       // -> Ignore empty links | ||||
|       if (!href || href.length < 1) { | ||||
|         return | ||||
|       } | ||||
|  | ||||
|       // -> Strip host from local links | ||||
|       if (isHostSet && href.indexOf(WIKI.config.site.host) === 0) { | ||||
|         href = href.replace(WIKI.config.site.host, '') | ||||
|       } | ||||
|  | ||||
|       // -> Assign local / external tag | ||||
|       if (href.indexOf('://') < 0) { | ||||
|         // -> Remove trailing slash | ||||
|         if (_.endsWith('/')) { | ||||
|           href = href.slice(0, -1) | ||||
|         } | ||||
|  | ||||
|         // -> Check for system prefix | ||||
|         if (!reservedPrefixes.test(href)) { | ||||
|           $(elm).addClass(`is-internal-link`) | ||||
|  | ||||
|           // -> Reformat paths | ||||
|           if (href.indexOf('/') !== 0) { | ||||
|             href = `/${this.page.localeCode}/${this.page.path}/${href}` | ||||
|           } else if (href.charAt(3) !== '/') { | ||||
|             href = `/${this.page.localeCode}${href}` | ||||
|           } | ||||
|  | ||||
|           // -> Save internal references | ||||
|           internalRefs.push({ | ||||
|             localeCode: href.substring(1, 3), | ||||
|             path: _.head(href.substring(4).split('#')) | ||||
|           }) | ||||
|         } else { | ||||
|           $(elm).addClass(`is-system-link`) | ||||
|         } | ||||
|       } else { | ||||
|         $(elm).addClass(`is-external-link`) | ||||
|       } | ||||
|  | ||||
|       // -> Update element | ||||
|       $(elm).attr('href', href) | ||||
|     }) | ||||
|  | ||||
|     // -------------------------------- | ||||
|     // Detect internal link states | ||||
|     // -------------------------------- | ||||
|  | ||||
|     if (internalRefs.length > 0) { | ||||
|       // -> Find matching pages | ||||
|       const results = await WIKI.models.pages.query().column('path', 'localeCode').where(builder => { | ||||
|         internalRefs.forEach((ref, idx) => { | ||||
|           if (idx < 1) { | ||||
|             builder.where(ref) | ||||
|           } else { | ||||
|             builder.orWhere(ref) | ||||
|           } | ||||
|         }) | ||||
|       }) | ||||
|  | ||||
|       // -> Apply tag to internal links for found pages | ||||
|       $('a.is-internal-link').each((i, elm) => { | ||||
|         const href = $(elm).attr('href') | ||||
|         const hrefObj = { | ||||
|           localeCode: href.substring(1, 3), | ||||
|           path: _.head(href.substring(4).split('#')) | ||||
|         } | ||||
|         if (_.some(results, r => { | ||||
|           return r.localeCode === hrefObj.localeCode && r.path === hrefObj.path | ||||
|         })) { | ||||
|           $(elm).addClass(`is-valid-page`) | ||||
|         } else { | ||||
|           $(elm).addClass(`is-invalid-page`) | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|  | ||||
|     return $.html('body').replace('<body>', '').replace('</body>', '') | ||||
|   } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user