|
|
@@ -1,12 +1,115 @@
|
|
|
-document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" component="AppLink">'+stylFns['app/shared/AppLink/index.styl']+'</style>')
|
|
|
+# Загрузка стилей компонента
|
|
|
+document.head.insertAdjacentHTML('beforeend','<style type="text/tailwindcss" page="AppLink">'+stylFns['app/shared/AppLink/index.styl']+'</style>')
|
|
|
+
|
|
|
module.exports =
|
|
|
- default:
|
|
|
- render: (new Function '_ctx', '_cache', renderFns['app/shared/AppLink/index.pug'])()
|
|
|
- name: 'AppLink'
|
|
|
- props:
|
|
|
- to:
|
|
|
- type: [String, Object]
|
|
|
- required: true
|
|
|
- computed:
|
|
|
- isExternal: ()->
|
|
|
- return @.to.startsWith('http')
|
|
|
+ name: 'AppLink'
|
|
|
+ render: (new Function '_ctx', '_cache', renderFns['app/shared/AppLink/index.pug'])()
|
|
|
+
|
|
|
+ props:
|
|
|
+ to:
|
|
|
+ type: [String, Object]
|
|
|
+ required: true
|
|
|
+ href:
|
|
|
+ type: String
|
|
|
+ default: ''
|
|
|
+ target:
|
|
|
+ type: String
|
|
|
+ default: '_self'
|
|
|
+ rel:
|
|
|
+ type: String
|
|
|
+ default: ''
|
|
|
+ class:
|
|
|
+ type: [String, Object, Array]
|
|
|
+ default: ''
|
|
|
+ activeClass:
|
|
|
+ type: String
|
|
|
+ default: 'app-link--active'
|
|
|
+ exact:
|
|
|
+ type: Boolean
|
|
|
+ default: false
|
|
|
+
|
|
|
+ computed:
|
|
|
+ # Определяем тип ссылки: внутренняя (router) или внешняя
|
|
|
+ isExternalLink: ->
|
|
|
+ if typeof @to is 'string'
|
|
|
+ return @isExternalUrl(@to) or @to.startsWith('http') or @to.startsWith('//') or @to.startsWith('mailto:') or @to.startsWith('tel:')
|
|
|
+ return false
|
|
|
+
|
|
|
+ # Определяем является ли ссылка маршрутом Vue Router
|
|
|
+ isRouterLink: ->
|
|
|
+ if typeof @to is 'object'
|
|
|
+ return true
|
|
|
+ if typeof @to is 'string' and not @isExternalLink
|
|
|
+ return @to.startsWith('/') and not @to.startsWith('//')
|
|
|
+ return false
|
|
|
+
|
|
|
+ # Нормализованный URL для внешних ссылок
|
|
|
+ normalizedHref: ->
|
|
|
+ if @href
|
|
|
+ return @href
|
|
|
+ if typeof @to is 'string' and @isExternalLink
|
|
|
+ return @to
|
|
|
+ return ''
|
|
|
+
|
|
|
+ # Нормализованный to для router-link
|
|
|
+ normalizedTo: ->
|
|
|
+ if typeof @to is 'object'
|
|
|
+ return @to
|
|
|
+ if typeof @to is 'string' and not @isExternalLink
|
|
|
+ return @to
|
|
|
+ return ''
|
|
|
+
|
|
|
+ # Классы для компонента
|
|
|
+ linkClasses: ->
|
|
|
+ baseClasses = 'app-link transition-colors duration-200'
|
|
|
+
|
|
|
+ if typeof @class is 'string'
|
|
|
+ return baseClasses + ' ' + @class
|
|
|
+ else if Array.isArray(@class)
|
|
|
+ return [baseClasses, ...@class]
|
|
|
+ else if typeof @class is 'object'
|
|
|
+ return Object.assign({}, {[baseClasses]: true}, @class)
|
|
|
+ else
|
|
|
+ return baseClasses
|
|
|
+
|
|
|
+ methods:
|
|
|
+ # Проверка является ли URL внешним
|
|
|
+ isExternalUrl: (url) ->
|
|
|
+ if not url or typeof url isnt 'string'
|
|
|
+ return false
|
|
|
+
|
|
|
+ # Проверяем различные признаки внешних ссылок
|
|
|
+ externalPatterns = [
|
|
|
+ /^https?:\/\//i
|
|
|
+ /^\/\//i
|
|
|
+ /^mailto:/i
|
|
|
+ /^tel:/i
|
|
|
+ /^ftp:/i
|
|
|
+ /^#/i # якорные ссылки считаем внешними для простоты
|
|
|
+ ]
|
|
|
+
|
|
|
+ for pattern in externalPatterns
|
|
|
+ if pattern.test(url)
|
|
|
+ return true
|
|
|
+
|
|
|
+ return false
|
|
|
+
|
|
|
+ # Обработчик клика для внешних ссылок
|
|
|
+ handleExternalClick: (event) ->
|
|
|
+ # Если target="_blank", добавляем noopener noreferrer для безопасности
|
|
|
+ if @target is '_blank' and not @rel
|
|
|
+ @$el.rel = 'noopener noreferrer'
|
|
|
+
|
|
|
+ # Можно добавить аналитику или другие обработчики здесь
|
|
|
+ EventBus.emit('external_link_clicked', {
|
|
|
+ url: @normalizedHref
|
|
|
+ target: @target
|
|
|
+ })
|
|
|
+
|
|
|
+ # Обработчик клика для внутренних ссылок
|
|
|
+ handleInternalClick: (event) ->
|
|
|
+ # Можно добавить аналитику или другие обработчики здесь
|
|
|
+ EventBus.emit('internal_link_clicked', {
|
|
|
+ to: @normalizedTo
|
|
|
+ route: @$route
|
|
|
+ })
|