erudit 2.0.0-dev.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. package/.nuxtrc +1 -0
  2. package/app/app.vue +172 -0
  3. package/app/assets/icons/alert.svg +3 -0
  4. package/app/assets/icons/angle-right.svg +3 -0
  5. package/app/assets/icons/arrow-in-text.svg +3 -0
  6. package/app/assets/icons/arrow-left.svg +3 -0
  7. package/app/assets/icons/arrow-up-to-right.svg +3 -0
  8. package/app/assets/icons/aside-open.svg +3 -0
  9. package/app/assets/icons/asterisk.svg +3 -0
  10. package/app/assets/icons/book-question.svg +3 -0
  11. package/app/assets/icons/book.svg +3 -0
  12. package/app/assets/icons/bug.svg +3 -0
  13. package/app/assets/icons/chip.svg +3 -0
  14. package/app/assets/icons/circle-help.svg +3 -0
  15. package/app/assets/icons/construction.svg +3 -0
  16. package/app/assets/icons/cross.svg +3 -0
  17. package/app/assets/icons/draw.svg +3 -0
  18. package/app/assets/icons/ellipsis-vertical.svg +3 -0
  19. package/app/assets/icons/file-check.svg +3 -0
  20. package/app/assets/icons/file-lines.svg +3 -0
  21. package/app/assets/icons/file-star.svg +3 -0
  22. package/app/assets/icons/folder-open.svg +3 -0
  23. package/app/assets/icons/folder.svg +3 -0
  24. package/app/assets/icons/globe.svg +3 -0
  25. package/app/assets/icons/house.svg +3 -0
  26. package/app/assets/icons/link-external.svg +3 -0
  27. package/app/assets/icons/link.svg +3 -0
  28. package/app/assets/icons/moon.svg +3 -0
  29. package/app/assets/icons/outline/book.svg +3 -0
  30. package/app/assets/icons/outline/file-lines.svg +3 -0
  31. package/app/assets/icons/pirate.svg +3 -0
  32. package/app/assets/icons/plus-circle.svg +3 -0
  33. package/app/assets/icons/plus.svg +3 -0
  34. package/app/assets/icons/puzzle.svg +3 -0
  35. package/app/assets/icons/search.svg +3 -0
  36. package/app/assets/icons/shuffle.svg +3 -0
  37. package/app/assets/icons/star.svg +3 -0
  38. package/app/assets/icons/sun-moon.svg +3 -0
  39. package/app/assets/icons/sun.svg +3 -0
  40. package/app/assets/icons/user.svg +3 -0
  41. package/app/assets/icons/users.svg +3 -0
  42. package/app/components/Loading.vue +23 -0
  43. package/app/components/SiteAside.vue +382 -0
  44. package/app/components/SiteMain.vue +35 -0
  45. package/app/components/ads/BannerTemplate.vue +51 -0
  46. package/app/components/ads/BottomBanner.vue +45 -0
  47. package/app/components/ads/LeftBanner.vue +50 -0
  48. package/app/components/aside/AsideListItem.vue +74 -0
  49. package/app/components/aside/AsideMajor.vue +56 -0
  50. package/app/components/aside/AsideMinor.vue +71 -0
  51. package/app/components/aside/major/PaneContentScroll.vue +23 -0
  52. package/app/components/aside/major/PaneSwitch.vue +54 -0
  53. package/app/components/aside/major/PaneSwitchButton.vue +63 -0
  54. package/app/components/aside/major/SiteInfo.vue +85 -0
  55. package/app/components/aside/major/panes/Language.vue +79 -0
  56. package/app/components/aside/major/panes/Pages.vue +34 -0
  57. package/app/components/aside/major/panes/Search.vue +3 -0
  58. package/app/components/aside/major/panes/nav/Nav.vue +91 -0
  59. package/app/components/aside/major/panes/nav/NavBook.vue +86 -0
  60. package/app/components/aside/major/panes/nav/NavBookLoading.vue +24 -0
  61. package/app/components/aside/major/panes/nav/NavGlobal.vue +16 -0
  62. package/app/components/aside/major/panes/nav/fnav/FNav.vue +105 -0
  63. package/app/components/aside/major/panes/nav/fnav/FNavBook.vue +32 -0
  64. package/app/components/aside/major/panes/nav/fnav/FNavFlags.vue +40 -0
  65. package/app/components/aside/major/panes/nav/fnav/FNavFolder.vue +60 -0
  66. package/app/components/aside/major/panes/nav/fnav/FNavItem.vue +34 -0
  67. package/app/components/aside/major/panes/nav/fnav/FNavSeparator.vue +80 -0
  68. package/app/components/aside/major/panes/nav/fnav/FNavTopic.vue +24 -0
  69. package/app/components/aside/major/panes/other/ItemContent.vue +29 -0
  70. package/app/components/aside/major/panes/other/ItemGenerator.vue +15 -0
  71. package/app/components/aside/major/panes/other/ItemTheme.vue +54 -0
  72. package/app/components/aside/major/panes/other/Other.vue +16 -0
  73. package/app/components/aside/minor/AsideMinorContributor.vue +5 -0
  74. package/app/components/aside/minor/AsideMinorNews.vue +11 -0
  75. package/app/components/aside/minor/AsideMinorPane.vue +16 -0
  76. package/app/components/aside/minor/AsideMinorTopLink.vue +67 -0
  77. package/app/components/aside/minor/Contribute.vue +145 -0
  78. package/app/components/aside/minor/content/AsideMinorContent.vue +92 -0
  79. package/app/components/aside/minor/topic/AsideMinorTopic.vue +32 -0
  80. package/app/components/aside/minor/topic/TopicContributors.vue +177 -0
  81. package/app/components/aside/minor/topic/TopicNav.vue +49 -0
  82. package/app/components/aside/minor/topic/TopicToc.vue +202 -0
  83. package/app/components/aside/minor/topic/TopicTocItem.vue +31 -0
  84. package/app/components/aside/utils/AsideOverlayPane.vue +40 -0
  85. package/app/components/bitran/BitranContent.vue +64 -0
  86. package/app/components/bitran/RenderWrapper.vue +12 -0
  87. package/app/components/contributor/ContributorAvatar.vue +43 -0
  88. package/app/components/contributor/ContributorListItem.vue +35 -0
  89. package/app/components/main/topic/MainTopic.vue +79 -0
  90. package/app/components/main/topic/TopicPartSwitch.vue +118 -0
  91. package/app/components/main/utils/Breadcrumb.vue +75 -0
  92. package/app/components/main/utils/ContentDecoration.vue +29 -0
  93. package/app/components/main/utils/ContentDescription.vue +20 -0
  94. package/app/components/main/utils/ContentFlag.vue +16 -0
  95. package/app/components/main/utils/ContentPopover.vue +176 -0
  96. package/app/components/main/utils/ContentPopovers.vue +105 -0
  97. package/app/components/main/utils/ContentReferences.vue +76 -0
  98. package/app/components/main/utils/ContentSection.vue +42 -0
  99. package/app/components/main/utils/ContentTitle.vue +37 -0
  100. package/app/components/main/utils/reference/ReferenceGroup.vue +41 -0
  101. package/app/components/main/utils/reference/ReferenceItem.vue +64 -0
  102. package/app/components/main/utils/reference/ReferenceSource.vue +110 -0
  103. package/app/components/preview/Preview.vue +177 -0
  104. package/app/components/preview/PreviewDisplay.vue +139 -0
  105. package/app/components/preview/PreviewFooterAction.vue +73 -0
  106. package/app/components/preview/PreviewLoading.vue +15 -0
  107. package/app/components/preview/PreviewScreen.vue +99 -0
  108. package/app/components/preview/display/Alert.vue +50 -0
  109. package/app/components/preview/display/Custom.vue +18 -0
  110. package/app/components/preview/display/GenericLink.vue +48 -0
  111. package/app/components/preview/display/PageLink.vue +20 -0
  112. package/app/components/preview/display/Unique.vue +49 -0
  113. package/app/components/transition/Fade.vue +22 -0
  114. package/app/components/tree/TreeContainer.vue +12 -0
  115. package/app/components/tree/TreeItem.vue +89 -0
  116. package/app/composables/bitran.ts +132 -0
  117. package/app/composables/bitranContent.ts +36 -0
  118. package/app/composables/bitranLocation.ts +7 -0
  119. package/app/composables/contentData.ts +36 -0
  120. package/app/composables/contentPage.ts +156 -0
  121. package/app/composables/contentRoute.ts +45 -0
  122. package/app/composables/darkMagic.ts +24 -0
  123. package/app/composables/externalApi.ts +63 -0
  124. package/app/composables/favicon.ts +8 -0
  125. package/app/composables/formatText.ts +86 -0
  126. package/app/composables/majorPane.ts +60 -0
  127. package/app/composables/phrases.ts +80 -0
  128. package/app/composables/theme.ts +29 -0
  129. package/app/composables/url.ts +33 -0
  130. package/app/pages/_test/preview.vue +110 -0
  131. package/app/pages/article/[...articleId].vue +3 -0
  132. package/app/pages/book/[...bookId].vue +47 -0
  133. package/app/pages/group/[...groupId].vue +64 -0
  134. package/app/pages/index.vue +32 -0
  135. package/app/pages/members.vue +7 -0
  136. package/app/pages/practice/[...practice].vue +3 -0
  137. package/app/pages/summary/[...summaryId].vue +3 -0
  138. package/app/public/favicon/article.svg +10 -0
  139. package/app/public/favicon/default.svg +10 -0
  140. package/app/public/favicon/practice.svg +10 -0
  141. package/app/public/favicon/summary.svg +10 -0
  142. package/app/public/logotype.svg +17 -0
  143. package/app/public/og-default.png +0 -0
  144. package/app/public/user.svg +10 -0
  145. package/app/scripts/_immediate.js +4 -0
  146. package/app/scripts/aside/index.ts +59 -0
  147. package/app/scripts/aside/major/nav.ts +26 -0
  148. package/app/scripts/aside/minor/state.ts +37 -0
  149. package/app/scripts/aside/minor/topic.ts +3 -0
  150. package/app/scripts/flag.ts +28 -0
  151. package/app/scripts/og.ts +27 -0
  152. package/app/scripts/preview/build.ts +84 -0
  153. package/app/scripts/preview/data/alert.ts +19 -0
  154. package/app/scripts/preview/data/custom.ts +8 -0
  155. package/app/scripts/preview/data/genericLink.ts +24 -0
  156. package/app/scripts/preview/data/pageLink.ts +20 -0
  157. package/app/scripts/preview/data/unique.ts +70 -0
  158. package/app/scripts/preview/data.ts +26 -0
  159. package/app/scripts/preview/display.ts +39 -0
  160. package/app/scripts/preview/footer.ts +9 -0
  161. package/app/scripts/preview/request.ts +51 -0
  162. package/app/scripts/preview/state.ts +63 -0
  163. package/app/styles/_immediate.css +3 -0
  164. package/app/styles/_util.scss +50 -0
  165. package/app/styles/_utils.scss +44 -0
  166. package/app/styles/app.scss +91 -0
  167. package/app/styles/def/_bp.scss +24 -0
  168. package/app/styles/def/_size.scss +7 -0
  169. package/app/styles/def/_z.scss +5 -0
  170. package/app/styles/default.scss +85 -0
  171. package/app/styles/normalize.scss +63 -0
  172. package/app/styles/partials/_darkMagic.scss +7 -0
  173. package/app/styles/partials/_fnav.scss +18 -0
  174. package/app/styles/partials/_preview.scss +7 -0
  175. package/globalPath.ts +24 -0
  176. package/globals/bitran.ts +39 -0
  177. package/globals/content.ts +22 -0
  178. package/globals/contributor.ts +5 -0
  179. package/globals/erudit.ts +5 -0
  180. package/globals/register.ts +18 -0
  181. package/languages/en.ts +95 -0
  182. package/languages/ru.ts +99 -0
  183. package/module/bitran.ts +34 -0
  184. package/module/config.ts +34 -0
  185. package/module/imports.ts +46 -0
  186. package/module/index.ts +35 -0
  187. package/module/logger.ts +10 -0
  188. package/module/paths.ts +22 -0
  189. package/module/restart.ts +61 -0
  190. package/nuxt.config.ts +96 -0
  191. package/package.json +32 -0
  192. package/server/api/aside/major/nav/bookIds.ts +5 -0
  193. package/server/api/aside/major/nav/bookNav/[...bookId].ts +20 -0
  194. package/server/api/aside/major/nav/global.ts +7 -0
  195. package/server/api/aside/minor/news.ts +7 -0
  196. package/server/api/aside/minor/path.ts +78 -0
  197. package/server/api/bitran/content/[location].ts +7 -0
  198. package/server/api/bitran/toc/[location].ts +7 -0
  199. package/server/api/content/data.ts +72 -0
  200. package/server/api/contributor/count.ts +6 -0
  201. package/server/api/fake/content.ts +11 -0
  202. package/server/api/fake/shared/languages.ts +12 -0
  203. package/server/api/language/functions.ts +12 -0
  204. package/server/api/language/phrase/[phraseId].ts +19 -0
  205. package/server/api/language/phraseIds.ts +8 -0
  206. package/server/api/preview/page/[...parts].ts +51 -0
  207. package/server/api/preview/unique/[location].ts +55 -0
  208. package/server/plugin/bitran/content.ts +176 -0
  209. package/server/plugin/bitran/core.ts +51 -0
  210. package/server/plugin/bitran/location.ts +25 -0
  211. package/server/plugin/bitran/products/include.ts +229 -0
  212. package/server/plugin/bitran/products/link.ts +114 -0
  213. package/server/plugin/bitran/setup.ts +10 -0
  214. package/server/plugin/bitran/toc.ts +82 -0
  215. package/server/plugin/build/close.ts +10 -0
  216. package/server/plugin/build/jobs/content/builderArgs.ts +8 -0
  217. package/server/plugin/build/jobs/content/generic.ts +176 -0
  218. package/server/plugin/build/jobs/content/parse.ts +90 -0
  219. package/server/plugin/build/jobs/content/path.ts +6 -0
  220. package/server/plugin/build/jobs/content/type/book.ts +9 -0
  221. package/server/plugin/build/jobs/content/type/group.ts +37 -0
  222. package/server/plugin/build/jobs/content/type/topic.ts +36 -0
  223. package/server/plugin/build/jobs/contributors.ts +66 -0
  224. package/server/plugin/build/jobs/language.ts +36 -0
  225. package/server/plugin/build/jobs/nav.ts +209 -0
  226. package/server/plugin/build/process.ts +25 -0
  227. package/server/plugin/build/rebuild.ts +55 -0
  228. package/server/plugin/build/setup.ts +21 -0
  229. package/server/plugin/content/absoluteId.ts +94 -0
  230. package/server/plugin/content/context.ts +112 -0
  231. package/server/plugin/db/entities/Book.ts +7 -0
  232. package/server/plugin/db/entities/Content.ts +49 -0
  233. package/server/plugin/db/entities/Contribution.ts +10 -0
  234. package/server/plugin/db/entities/Contributor.ts +16 -0
  235. package/server/plugin/db/entities/Group.ts +14 -0
  236. package/server/plugin/db/entities/Hash.ts +15 -0
  237. package/server/plugin/db/entities/Topic.ts +20 -0
  238. package/server/plugin/db/entities/Unique.ts +21 -0
  239. package/server/plugin/db/setup.ts +34 -0
  240. package/server/plugin/global.ts +18 -0
  241. package/server/plugin/importer.ts +12 -0
  242. package/server/plugin/index.ts +9 -0
  243. package/server/plugin/logger.ts +23 -0
  244. package/server/plugin/nav/node.ts +26 -0
  245. package/server/plugin/nav/utils.ts +129 -0
  246. package/server/plugin/repository/book.ts +21 -0
  247. package/server/plugin/repository/content.ts +238 -0
  248. package/server/plugin/repository/contributor.ts +8 -0
  249. package/server/plugin/repository/frontNav.ts +148 -0
  250. package/server/plugin/repository/topic.ts +32 -0
  251. package/server/tsconfig.json +7 -0
  252. package/shared/aside/minor.ts +50 -0
  253. package/shared/asset.ts +15 -0
  254. package/shared/bitran/alias.ts +17 -0
  255. package/shared/bitran/context.ts +7 -0
  256. package/shared/bitran/location.ts +166 -0
  257. package/shared/bitran/products/alias/core/factory.ts +46 -0
  258. package/shared/bitran/products/alias/core/index.ts +13 -0
  259. package/shared/bitran/products/alias/render/Alias.vue +10 -0
  260. package/shared/bitran/products/alias/render/icon.svg +3 -0
  261. package/shared/bitran/products/alias/render/index.ts +17 -0
  262. package/shared/bitran/products/alias/render/languages/en.ts +5 -0
  263. package/shared/bitran/products/alias/render/languages/ru.ts +5 -0
  264. package/shared/bitran/products/alias/shared.ts +11 -0
  265. package/shared/bitran/products/heading/core/factory.ts +53 -0
  266. package/shared/bitran/products/heading/core/index.ts +19 -0
  267. package/shared/bitran/products/heading/render/Heading.vue +47 -0
  268. package/shared/bitran/products/heading/render/icon.svg +3 -0
  269. package/shared/bitran/products/heading/render/index.ts +17 -0
  270. package/shared/bitran/products/heading/render/languages/en.ts +5 -0
  271. package/shared/bitran/products/heading/render/languages/ru.ts +5 -0
  272. package/shared/bitran/products/heading/shared.ts +13 -0
  273. package/shared/bitran/products/include/core/factory.ts +61 -0
  274. package/shared/bitran/products/include/core/index.ts +13 -0
  275. package/shared/bitran/products/include/render/Include.vue +13 -0
  276. package/shared/bitran/products/include/render/icon.svg +3 -0
  277. package/shared/bitran/products/include/render/index.ts +18 -0
  278. package/shared/bitran/products/include/render/languages/en.ts +5 -0
  279. package/shared/bitran/products/include/render/languages/ru.ts +5 -0
  280. package/shared/bitran/products/include/shared.ts +15 -0
  281. package/shared/bitran/products/link/core/factory.ts +20 -0
  282. package/shared/bitran/products/link/core/index.ts +17 -0
  283. package/shared/bitran/products/link/render/Link.vue +174 -0
  284. package/shared/bitran/products/link/render/icon.svg +3 -0
  285. package/shared/bitran/products/link/render/index.ts +17 -0
  286. package/shared/bitran/products/link/render/languages/en.ts +5 -0
  287. package/shared/bitran/products/link/render/languages/ru.ts +5 -0
  288. package/shared/bitran/products/link/shared.ts +15 -0
  289. package/shared/bitran/products/link/target.ts +134 -0
  290. package/shared/bitran/toc.ts +8 -0
  291. package/shared/content/context.ts +9 -0
  292. package/shared/content/data/base.ts +32 -0
  293. package/shared/content/data/index.ts +5 -0
  294. package/shared/content/data/type/book.ts +5 -0
  295. package/shared/content/data/type/group.ts +6 -0
  296. package/shared/content/data/type/topic.ts +11 -0
  297. package/shared/content/previousNext.ts +9 -0
  298. package/shared/contributor.ts +5 -0
  299. package/shared/frontNav.ts +41 -0
  300. package/shared/icons.ts +38 -0
  301. package/shared/image.ts +5 -0
  302. package/shared/link.ts +25 -0
  303. package/shared/types/language.ts +75 -0
  304. package/shared/utils/objectsEqual.ts +4 -0
  305. package/shared/utils/stringColor.ts +9 -0
  306. package/test/bitran/alias.test.ts +44 -0
  307. package/test/bitran/location.test.ts +143 -0
  308. package/test/bitran/products/alias.test.ts +83 -0
  309. package/test/bitran/products/heading.test.ts +119 -0
  310. package/test/bitran/products/include.test.ts +77 -0
  311. package/test/bitran/products/link/factory.test.ts +30 -0
  312. package/test/bitran/products/link/target.test.ts +138 -0
  313. package/tsconfig.json +8 -0
  314. package/utils/stress.ts +9 -0
@@ -0,0 +1,13 @@
1
+ <script lang="ts" setup>
2
+ import { Render, useParseData, type ProductProps } from 'bitran/render';
3
+
4
+ import type { IncludeType } from '../shared';
5
+
6
+ const { node } = defineProps<ProductProps<IncludeType>>();
7
+ const { blocks } = useParseData(node);
8
+ </script>
9
+
10
+ <template>
11
+ <Render v-if="blocks" v-for="node of blocks.children" :node />
12
+ <div v-else>Блоков нема</div>
13
+ </template>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 24 24">
2
+ <path d="M0,13.3l8-8,1.9,1.9-4.8,4.8h12.9c.9,0,1.7-.3,2.4-1,.6-.6,1-1.4,1-2.4s-.3-1.7-1-2.4-1.4-1-2.4-1h-.7v-2.7h.7c1.7,0,3.1.6,4.2,1.7s1.7,2.6,1.7,4.2-.6,3.1-1.7,4.2-2.6,1.7-4.2,1.7H5.1s4.8,4.8,4.8,4.8l-1.9,1.9L0,13.3Z"/>
3
+ </svg>
@@ -0,0 +1,18 @@
1
+ import {
2
+ defineIcon,
3
+ defineLanguages,
4
+ defineProductComponent,
5
+ defineProductRender,
6
+ RenderMode,
7
+ } from 'bitran/render';
8
+
9
+ export const include = defineProductRender({
10
+ mode: RenderMode.Server,
11
+ component: defineProductComponent(() => import('./Include.vue')),
12
+ icon: defineIcon(() => import('./icon.svg?raw')),
13
+ customLayout: true,
14
+ languages: defineLanguages({
15
+ en: () => import('./languages/en'),
16
+ ru: () => import('./languages/ru'),
17
+ }),
18
+ });
@@ -0,0 +1,5 @@
1
+ import { defineLanguage } from 'bitran/render';
2
+
3
+ export default defineLanguage({
4
+ _name: 'Include',
5
+ });
@@ -0,0 +1,5 @@
1
+ import { defineLanguage } from 'bitran/render';
2
+
3
+ export default defineLanguage({
4
+ _name: 'Вставка',
5
+ });
@@ -0,0 +1,15 @@
1
+ import type { DefineProductType } from 'bitran';
2
+ import type { BlocksNode } from 'bitran/default';
3
+
4
+ export interface IncludeData {
5
+ location: string;
6
+ resolved?: boolean;
7
+ blocks?: BlocksNode;
8
+ error?: any;
9
+ }
10
+
11
+ export type IncludeType = DefineProductType<{
12
+ ParseData: IncludeData;
13
+ }>;
14
+
15
+ export const includeName = '__include';
@@ -0,0 +1,20 @@
1
+ import { ProductStrFactory, RegexpInlinerParseFactory } from 'bitran/process';
2
+
3
+ import type { LinkData, LinkType } from '../shared';
4
+
5
+ export class LinkParser extends RegexpInlinerParseFactory<LinkType> {
6
+ regexp = /\[(.+?)\]\((.+?)\)/gm;
7
+
8
+ async parseDataFromRegexp(match: RegExpExecArray) {
9
+ return {
10
+ label: match[1]!,
11
+ target: match[2]!,
12
+ };
13
+ }
14
+ }
15
+
16
+ export class LinkStr extends ProductStrFactory<LinkType> {
17
+ stringifyData(parseData: LinkData) {
18
+ return `[${parseData.label}](${parseData.target})`;
19
+ }
20
+ }
@@ -0,0 +1,17 @@
1
+ import { InlinerNode } from 'bitran/dom';
2
+ import { defineProductCore } from 'bitran/core';
3
+
4
+ import type { LinkType } from '../shared';
5
+ import { LinkParser, LinkStr } from './factory';
6
+
7
+ export class LinkNode extends InlinerNode<LinkType> {}
8
+
9
+ export const link = defineProductCore<LinkType>({
10
+ Node: LinkNode,
11
+ Parser: LinkParser,
12
+ Stringifier: LinkStr,
13
+ renderDataSide: 'pre',
14
+ buildRenderData: async () => {
15
+ throw Error('Cache for Links must be built only on server!');
16
+ },
17
+ });
@@ -0,0 +1,174 @@
1
+ <script lang="ts" setup>
2
+ import { useParseData, useRenderData, type ProductProps } from 'bitran/render';
3
+ import { isContentType, isTopicPart } from 'erudit-cog/schema';
4
+
5
+ import { CONTENT_TYPE_ICON, ICON, TOPIC_PART_ICON } from '@erudit/shared/icons';
6
+ import type { LinkType } from '../shared';
7
+ import { MyIcon, MyRuntimeIcon } from '#components';
8
+ import { showPreview, togglePreview } from '@erudit/app/scripts/preview/state';
9
+ import { PreviewRequestType } from '@erudit/app/scripts/preview/request';
10
+ import { encodeBitranLocation } from '@erudit/shared/bitran/location';
11
+
12
+ const { node } = defineProps<ProductProps<LinkType>>();
13
+ const { label } = useParseData(node);
14
+ const linkTarget = useRenderData(node);
15
+
16
+ const baseUrlPath = useBaseUrlPath();
17
+ const formatText = useFormatText();
18
+
19
+ const Icon = await (async () => {
20
+ const type = linkTarget.type;
21
+
22
+ if (type === 'unique') {
23
+ return h(MyRuntimeIcon, {
24
+ name: 'link-icon',
25
+ svg: await useBitranProductIcon(linkTarget._productName!),
26
+ });
27
+ } else if (type === 'absolute') {
28
+ return h(MyIcon, { name: 'link' });
29
+ } else if (type === 'external') {
30
+ return h(MyIcon, { name: 'link-external' });
31
+ } else if (type === 'page') {
32
+ const pageType = linkTarget.pageType;
33
+
34
+ if (isTopicPart(pageType))
35
+ return h(MyIcon, { name: TOPIC_PART_ICON[pageType] });
36
+ if (isContentType(pageType))
37
+ return h(MyIcon, { name: CONTENT_TYPE_ICON[pageType] });
38
+
39
+ switch (pageType) {
40
+ case 'contributor':
41
+ return h(MyIcon, { name: ICON.contributor });
42
+ }
43
+
44
+ return h(MyIcon, { name: 'link' });
45
+ }
46
+ })();
47
+
48
+ const href = (() => {
49
+ switch (linkTarget.type) {
50
+ case 'absolute':
51
+ case 'external':
52
+ return linkTarget.href;
53
+ case 'page':
54
+ case 'unique':
55
+ return linkTarget._href!;
56
+ }
57
+ })();
58
+
59
+ // Prerendering data for previews to work after site build
60
+ switch (linkTarget.type) {
61
+ case 'page':
62
+ prerenderRoutes(`/api/preview/page${linkTarget._href}`);
63
+ break;
64
+ case 'unique':
65
+ prerenderRoutes(
66
+ `/api/preview/unique/${encodeBitranLocation(linkTarget._absoluteStrLocation!)}`,
67
+ );
68
+ break;
69
+ }
70
+
71
+ const doubleClick = {
72
+ timeout: null as any,
73
+ startTimeout() {
74
+ this.timeout = setTimeout(() => {
75
+ this.reset();
76
+ }, 400);
77
+ },
78
+ reset() {
79
+ clearTimeout(this.timeout);
80
+ this.timeout = null;
81
+ },
82
+ };
83
+
84
+ function linkClick(e: Event) {
85
+ // Direct link traversal is disabled, use double click instead!
86
+
87
+ e.stopPropagation();
88
+ e.preventDefault();
89
+
90
+ if (doubleClick.timeout) {
91
+ doubleClick.reset();
92
+ togglePreview(false);
93
+ window.open(baseUrlPath(href), '_blank');
94
+ return false;
95
+ }
96
+
97
+ showPreview({
98
+ type: PreviewRequestType.Link,
99
+ linkData: useParseData(node),
100
+ linkTarget,
101
+ });
102
+
103
+ doubleClick.startTimeout();
104
+
105
+ return false;
106
+ }
107
+ </script>
108
+
109
+ <template>
110
+ <a
111
+ :href="baseUrlPath(href)"
112
+ :class="$style.link"
113
+ @click="linkClick"
114
+ :external="true"
115
+ >
116
+ <Icon :class="$style.icon" wrapper="span" />
117
+ <span :class="$style.label">{{ formatText(label) }}</span>
118
+ </a>
119
+ </template>
120
+
121
+ <style lang="scss" module>
122
+ .link {
123
+ --linkColor: var(--brand); //light-dark(#3e66a1, #77a0db);
124
+
125
+ display: inline-flex;
126
+ align-items: center;
127
+ gap: 3px;
128
+
129
+ position: relative;
130
+ top: 1px;
131
+
132
+ padding: 0 3px;
133
+ margin: 0 -2px;
134
+
135
+ color: var(--linkColor);
136
+ text-decoration-thickness: 2px;
137
+ text-decoration-style: solid;
138
+ text-decoration-color: color-mix(
139
+ in srgb,
140
+ var(--linkColor),
141
+ transparent 75%
142
+ );
143
+
144
+ border-radius: 5px;
145
+
146
+ @include transition(background);
147
+
148
+ &:hover,
149
+ &:active {
150
+ background: color-mix(in srgb, var(--linkColor), transparent 87.5%);
151
+ }
152
+
153
+ .icon {
154
+ flex-shrink: 0;
155
+ position: relative;
156
+ top: 1px;
157
+ font-size: 0.86em;
158
+
159
+ text-decoration: none;
160
+ color: color-mix(in srgb, var(--linkColor), transparent 10%);
161
+
162
+ // &,
163
+ // & svg {
164
+ // width: #{$iconSize};
165
+ // height: #{$iconSize};
166
+ // fill: currentColor;
167
+ // }
168
+ }
169
+
170
+ .label {
171
+ font-weight: 450;
172
+ }
173
+ }
174
+ </style>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
2
+ <path d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z" />
3
+ </svg>
@@ -0,0 +1,17 @@
1
+ import {
2
+ defineIcon,
3
+ defineLanguages,
4
+ defineProductComponent,
5
+ defineProductRender,
6
+ RenderMode,
7
+ } from 'bitran/render';
8
+
9
+ export const link = defineProductRender({
10
+ mode: RenderMode.Server,
11
+ component: defineProductComponent(() => import('./Link.vue')),
12
+ icon: defineIcon(() => import('./icon.svg?raw')),
13
+ languages: defineLanguages({
14
+ en: () => import('./languages/en'),
15
+ ru: () => import('./languages/ru'),
16
+ }),
17
+ });
@@ -0,0 +1,5 @@
1
+ import { defineLanguage } from 'bitran/render';
2
+
3
+ export default defineLanguage({
4
+ _name: 'Link',
5
+ });
@@ -0,0 +1,5 @@
1
+ import { defineLanguage } from 'bitran/render';
2
+
3
+ export default defineLanguage({
4
+ _name: 'Ссылка',
5
+ });
@@ -0,0 +1,15 @@
1
+ import type { DefineProductType } from 'bitran';
2
+
3
+ import type { LinkTarget } from './target';
4
+
5
+ export interface LinkData {
6
+ target: string;
7
+ label: string;
8
+ }
9
+
10
+ export type LinkType = DefineProductType<{
11
+ ParseData: LinkData;
12
+ RenderData: LinkTarget;
13
+ }>;
14
+
15
+ export const linkName = '__link';
@@ -0,0 +1,134 @@
1
+ import { isTopicPart } from 'erudit-cog/schema';
2
+
3
+ import type { BitranContext } from '@shared/bitran/context';
4
+ import { tryReplaceAlias } from '@shared/bitran/alias';
5
+ import {
6
+ bitranLocationTypes,
7
+ parsePartialBitranLocation,
8
+ stringifyBitranLocation,
9
+ } from '@shared/bitran/location';
10
+
11
+ export type LinkTargetType = 'unique' | 'page' | 'absolute' | 'external';
12
+
13
+ interface LinkTargetBase {
14
+ type: LinkTargetType;
15
+ }
16
+
17
+ export interface UniqueLinkTarget extends LinkTargetBase {
18
+ type: 'unique';
19
+ strlocation: string;
20
+ _productName?: string;
21
+ _absoluteStrLocation?: string;
22
+ _href?: string;
23
+ }
24
+
25
+ export const linkTargetPageTypes = {
26
+ ...bitranLocationTypes,
27
+ // Custom page types, that does not have Bitran content
28
+ book: true,
29
+ };
30
+
31
+ export type LinkTargetPageType = keyof typeof linkTargetPageTypes;
32
+
33
+ export function isLinkTargetPageType(
34
+ pageType: any,
35
+ ): pageType is LinkTargetPageType {
36
+ return pageType in linkTargetPageTypes;
37
+ }
38
+
39
+ function pageTypeRequiresPath(type: LinkTargetPageType) {
40
+ return linkTargetPageTypes[type];
41
+ }
42
+
43
+ export interface PageLinkTarget extends LinkTargetBase {
44
+ type: 'page';
45
+ pageType: LinkTargetPageType;
46
+ path?: string;
47
+ _href?: string;
48
+ }
49
+
50
+ export interface AbsoluteLinkTarget extends LinkTargetBase {
51
+ type: 'absolute';
52
+ href: string;
53
+ }
54
+
55
+ export interface ExternalLinkTarget extends LinkTargetBase {
56
+ type: 'external';
57
+ href: string;
58
+ }
59
+
60
+ export type LinkTarget =
61
+ | UniqueLinkTarget
62
+ | PageLinkTarget
63
+ | AbsoluteLinkTarget
64
+ | ExternalLinkTarget;
65
+
66
+ export function createLinkTarget(
67
+ target: string,
68
+ context: BitranContext,
69
+ ): LinkTarget {
70
+ target = tryReplaceAlias(target, context.aliases);
71
+
72
+ if (target.startsWith('/'))
73
+ return {
74
+ type: 'absolute',
75
+ href: target,
76
+ };
77
+
78
+ try {
79
+ new URL(target);
80
+ return {
81
+ type: 'external',
82
+ href: target,
83
+ };
84
+ } catch {}
85
+
86
+ if (target.startsWith('page|')) {
87
+ let [, pageType, path] = target.split('|');
88
+
89
+ if (!isLinkTargetPageType(pageType))
90
+ throw new Error(
91
+ `Unknown page type "${pageType}" in link "${target}"!`,
92
+ );
93
+
94
+ if (!path) {
95
+ if (isTopicPart(pageType)) {
96
+ if (!isTopicPart(context.location.type))
97
+ throw new Error(
98
+ `Page link "${target}" is referencing topic part "${pageType}" without path in non-topic context!`,
99
+ );
100
+
101
+ path = context.location.path;
102
+ } else {
103
+ if (pageTypeRequiresPath(pageType))
104
+ throw new Error(
105
+ `Page link "${target}" does not have a path!`,
106
+ );
107
+ }
108
+ }
109
+
110
+ return {
111
+ type: 'page',
112
+ pageType,
113
+ path,
114
+ };
115
+ }
116
+
117
+ const location = parsePartialBitranLocation(target, context.location);
118
+
119
+ if (
120
+ !location.unique &&
121
+ isLinkTargetPageType(location.type) &&
122
+ !isTopicPart(location.type)
123
+ )
124
+ return {
125
+ type: 'page',
126
+ pageType: location.type,
127
+ path: location.path,
128
+ };
129
+
130
+ return {
131
+ type: 'unique',
132
+ strlocation: stringifyBitranLocation(location),
133
+ };
134
+ }
@@ -0,0 +1,8 @@
1
+ export interface TocItem {
2
+ id: string;
3
+ level: number;
4
+ productName: string;
5
+ title?: string;
6
+ }
7
+
8
+ export type Toc = TocItem[];
@@ -0,0 +1,9 @@
1
+ export interface ContextItem {
2
+ type: string;
3
+ icon: string;
4
+ title: string;
5
+ href: string;
6
+ hidden: boolean;
7
+ }
8
+
9
+ export type Context = ContextItem[];
@@ -0,0 +1,32 @@
1
+ import type {
2
+ ContentFlag,
3
+ ContentReferences,
4
+ ContentSeo,
5
+ ContentType,
6
+ TopicPart,
7
+ } from 'erudit-cog/schema';
8
+
9
+ import type { Context } from '@shared/content/context';
10
+ import type { PreviousNext } from '@shared/content/previousNext';
11
+ import type { ImageData } from '@shared/image';
12
+
13
+ export interface ContentGenericData {
14
+ contentId: string;
15
+ title?: string;
16
+ description?: string;
17
+ decoration?: string;
18
+ type: ContentType;
19
+ context: Context;
20
+ flags: Record<ContentFlag, boolean>;
21
+ topicPart?: TopicPart;
22
+ seo?: Partial<ContentSeo>;
23
+ ogImage?: ImageData;
24
+ previousNext: PreviousNext;
25
+ dependencies?: Record<string, string>;
26
+ references?: ContentReferences;
27
+ }
28
+
29
+ export interface ContentBaseData {
30
+ type: ContentType;
31
+ generic: ContentGenericData;
32
+ }
@@ -0,0 +1,5 @@
1
+ import type { ContentTopicData } from './type/topic';
2
+ import type { ContentGroupData } from './type/group';
3
+ import type { ContentBookData } from './type/book';
4
+
5
+ export type ContentData = ContentTopicData | ContentGroupData | ContentBookData;
@@ -0,0 +1,5 @@
1
+ import type { ContentBaseData } from '../base';
2
+
3
+ export interface ContentBookData extends ContentBaseData {
4
+ type: 'book';
5
+ }
@@ -0,0 +1,6 @@
1
+ import type { ContentBaseData } from '../base';
2
+
3
+ export interface ContentGroupData extends ContentBaseData {
4
+ type: 'group';
5
+ bookTitle?: string;
6
+ }
@@ -0,0 +1,11 @@
1
+ import type { TopicPart } from 'erudit-cog/schema';
2
+
3
+ import type { ContentBaseData } from '../base';
4
+
5
+ export type TopicPartLinks = Partial<Record<TopicPart, string>>;
6
+
7
+ export interface ContentTopicData extends ContentBaseData {
8
+ type: 'topic';
9
+ topicPartLinks: TopicPartLinks;
10
+ bookTitle?: string;
11
+ }
@@ -0,0 +1,9 @@
1
+ export interface PreviousNext {
2
+ next?: PreviousNextItem;
3
+ previous?: PreviousNextItem;
4
+ }
5
+
6
+ export interface PreviousNextItem {
7
+ title: string;
8
+ link: string;
9
+ }
@@ -0,0 +1,5 @@
1
+ export interface ContentContributor {
2
+ contributorId: string;
3
+ displayName?: string;
4
+ avatar?: string;
5
+ }
@@ -0,0 +1,41 @@
1
+ import type { ContentFlag, TopicPart } from 'erudit-cog/schema';
2
+
3
+ export type FrontNavType = 'book' | 'topic' | 'separator' | 'folder';
4
+
5
+ export interface FrontNavBase {
6
+ type: FrontNavType;
7
+ id: string;
8
+ fullId: string;
9
+ label: string;
10
+ level: number;
11
+ flags?: Record<ContentFlag, boolean>;
12
+ }
13
+
14
+ export interface FrontNavTopic extends FrontNavBase {
15
+ type: 'topic';
16
+ part: TopicPart;
17
+ }
18
+
19
+ export interface FrontNavContainer extends FrontNavBase {
20
+ children?: FrontNavItem[];
21
+ }
22
+
23
+ export interface FrontNavBook extends FrontNavContainer {
24
+ type: 'book';
25
+ }
26
+
27
+ export interface FrontNavSeparator extends FrontNavContainer {
28
+ type: 'separator';
29
+ }
30
+
31
+ export interface FrontNavFolder extends FrontNavContainer {
32
+ type: 'folder';
33
+ }
34
+
35
+ export type FrontNavItem =
36
+ | FrontNavTopic
37
+ | FrontNavBook
38
+ | FrontNavSeparator
39
+ | FrontNavFolder;
40
+
41
+ export type FrontNav = FrontNavItem[];
@@ -0,0 +1,38 @@
1
+ import {
2
+ isContentType,
3
+ isTopicPart,
4
+ type ContentType,
5
+ type TopicPart,
6
+ } from 'erudit-cog/schema';
7
+
8
+ import type { MyIconName } from '#my-icons';
9
+ import type { BitranLocation } from './bitran/location';
10
+
11
+ export const TOPIC_PART_ICON = {
12
+ article: 'outline/file-lines',
13
+ summary: 'file-star',
14
+ practice: 'file-check',
15
+ } satisfies Record<TopicPart, MyIconName>;
16
+
17
+ export const CONTENT_TYPE_ICON = {
18
+ book: 'outline/book',
19
+ group: 'folder',
20
+ topic: 'outline/file-lines',
21
+ } satisfies Record<ContentType, MyIconName>;
22
+
23
+ export const ICON = {
24
+ contributor: 'user',
25
+ } satisfies Record<string, MyIconName>;
26
+
27
+ export function locationIcon(location: BitranLocation): MyIconName {
28
+ if (isTopicPart(location.type)) return TOPIC_PART_ICON[location.type];
29
+
30
+ if (isContentType(location.type)) return CONTENT_TYPE_ICON[location.type];
31
+
32
+ switch (location.type) {
33
+ case 'contributor':
34
+ return ICON['contributor'];
35
+ }
36
+
37
+ return '__missing';
38
+ }
@@ -0,0 +1,5 @@
1
+ export interface ImageData {
2
+ src: string;
3
+ width: number;
4
+ height: number;
5
+ }