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,59 @@
1
+ // TODO: Push this shit into <SiteAside /> or make composable or make something...
2
+
3
+ export enum AsideType {
4
+ Major = 'major',
5
+ Minor = 'minor',
6
+ }
7
+
8
+ export const switchVisible = ref<boolean>(true);
9
+ export const forcedAside = ref<AsideType>();
10
+
11
+ export const clickTargets: {
12
+ major: HTMLElement[];
13
+ minor: HTMLElement[];
14
+ } = {
15
+ major: [],
16
+ minor: [],
17
+ };
18
+
19
+ if (import.meta.client) {
20
+ //
21
+ // Switch visibility logic
22
+ //
23
+
24
+ let lastY = window.scrollY;
25
+ let sumDelta = 0;
26
+ let scrollTimeout: any;
27
+
28
+ window.addEventListener('scroll', () => {
29
+ const currentY = window.scrollY;
30
+ const delta = currentY - lastY;
31
+
32
+ sumDelta += delta;
33
+
34
+ switchVisible.value = sumDelta <= -1;
35
+
36
+ lastY = currentY;
37
+
38
+ clearTimeout(scrollTimeout);
39
+ scrollTimeout = setTimeout(() => {
40
+ sumDelta = 0;
41
+ }, 200);
42
+ });
43
+
44
+ //
45
+ // Unpin forced asides when click outside logic
46
+ //
47
+
48
+ window.addEventListener('click', (e) => {
49
+ if (forcedAside.value) {
50
+ const toIgnoreTargets = clickTargets[forcedAside.value]; // Ignore currently pinned aside elements
51
+
52
+ for (const target of toIgnoreTargets)
53
+ if (target.contains(e.target as Element)) return;
54
+
55
+ forcedAside.value = undefined;
56
+ switchVisible.value = true;
57
+ }
58
+ });
59
+ }
@@ -0,0 +1,26 @@
1
+ import type { InjectionKey, ShallowRef } from 'vue';
2
+ import type { FrontNav } from '@shared/frontNav';
3
+
4
+ export const insideNavBook = ref(false);
5
+ export const navBookId = ref<string>();
6
+
7
+ export const navBookVisible = computed(() => {
8
+ return insideNavBook.value && navBookId.value;
9
+ });
10
+
11
+ type AsideMajorNav = {
12
+ booksIds: string[];
13
+ globalNav: FrontNav;
14
+ };
15
+
16
+ export function getAsideMajorNavPayload() {
17
+ const key = 'aside-major-nav';
18
+ const nuxtApp = useNuxtApp();
19
+ return (nuxtApp.static.data[key] ||= nuxtApp.payload.data[key] ||=
20
+ {}) as AsideMajorNav;
21
+ }
22
+
23
+ export type NavStateValue = ShallowRef<null | 'active' | 'activePart'>;
24
+ export type NavState = { [contentId: string]: NavStateValue };
25
+
26
+ export const navStateKey = Symbol() as InjectionKey<ShallowRef<NavState>>;
@@ -0,0 +1,37 @@
1
+ import type { InjectionKey, ShallowRef } from 'vue';
2
+ import type {
3
+ AsideMinorData,
4
+ AsideMinorNews,
5
+ AsideMinorTopic,
6
+ AsideMinorType,
7
+ } from '@shared/asideMinor';
8
+
9
+ export const asideMinorKey = Symbol() as InjectionKey<
10
+ ShallowRef<AsideMinorData>
11
+ >;
12
+
13
+ export function injectAsideData<T extends AsideMinorData>() {
14
+ return inject(asideMinorKey) as ShallowRef<T>;
15
+ }
16
+
17
+ // export function injectTopicData(): Ref<AsideMinorTopic>
18
+ // {
19
+ // const dataRef = inject(asideMinorKey);
20
+ // return createTypeSafeDataComputed(dataRef, 'topic') as any;
21
+ // }
22
+
23
+ // export function injectNewsData(): Ref<AsideMinorNews>
24
+ // {
25
+ // const dataRef = inject(asideMinorKey);
26
+ // return createTypeSafeDataComputed(dataRef, 'news') as any;
27
+ // }
28
+
29
+ // function createTypeSafeDataComputed(dataRef: Ref<AsideMinorData>, targetType: AsideMinorType)
30
+ // {
31
+ // return computed(() => {
32
+ // if (dataRef.value.type !== targetType)
33
+ // throw new Error(`Aside minor data type mismatch! Expected "${targetType}", got "${dataRef.value.type}"!`);
34
+
35
+ // return dataRef.value;
36
+ // });
37
+ // }
@@ -0,0 +1,3 @@
1
+ import type { Location } from '@shared/location';
2
+
3
+ export const topicLocation = ref<Location>();
@@ -0,0 +1,28 @@
1
+ import type { MyIconName } from '#my-icons';
2
+ import type { ContentFlag } from 'erudit-cog/schema';
3
+
4
+ interface FlagData {
5
+ icon: MyIconName;
6
+ title: EruditPhraseId;
7
+ }
8
+
9
+ export const flagsData: Record<ContentFlag, FlagData> = {
10
+ //
11
+ // Global Content Flags
12
+ //
13
+
14
+ dev: {
15
+ icon: 'construction',
16
+ title: 'flag_dev',
17
+ },
18
+
19
+ advanced: {
20
+ icon: 'asterisk',
21
+ title: 'flag_advanced',
22
+ },
23
+
24
+ secondary: {
25
+ icon: 'puzzle',
26
+ title: 'flag_secondary',
27
+ },
28
+ };
@@ -0,0 +1,27 @@
1
+ import eruditConfig from '#erudit/config';
2
+ import type { ImageData } from '@erudit/shared/image';
3
+
4
+ export const defaultOgImage = eruditConfig.seo?.defaultOgImage || {
5
+ src: eruditAsset('og-default.png'),
6
+ width: 300,
7
+ height: 300,
8
+ };
9
+
10
+ export function createOgImageTags(siteUrl: string, data: ImageData | string) {
11
+ const tags = [];
12
+ const isImage = typeof data !== 'string';
13
+
14
+ tags.push({
15
+ name: 'og:image',
16
+ content: siteUrl + (isImage ? data.src : data),
17
+ });
18
+
19
+ if (isImage) {
20
+ tags.push(
21
+ { name: 'og:image:width', content: data.width },
22
+ { name: 'og:image:height', content: data.height },
23
+ );
24
+ }
25
+
26
+ return tags;
27
+ }
@@ -0,0 +1,84 @@
1
+ import { PreviewDataType, type PreviewData } from './data';
2
+ import { PreviewRequestType, type PreviewRequest } from './request';
3
+
4
+ import { buildGenericLink } from './data/genericLink';
5
+ import { buildPageLink } from './data/pageLink';
6
+ import { buildUnique } from './data/unique';
7
+ import { createPreviewError } from './data/alert';
8
+ import { PreviewThemeName } from './state';
9
+
10
+ const builders = [
11
+ buildGenericLink,
12
+ buildPageLink,
13
+ buildUnique,
14
+ ];
15
+
16
+ export async function buildPreviewData(request: PreviewRequest): Promise<PreviewData>
17
+ {
18
+ if (request.type === PreviewRequestType.Data)
19
+ return request.data;
20
+
21
+ if (request.type === PreviewRequestType.MissingElement)
22
+ {
23
+ if (request.hashMismatch)
24
+ {
25
+ const phrase = await usePhrases(
26
+ 'preview_missing_title',
27
+ 'preview_missing_explain_mismatch',
28
+ 'element_id',
29
+ 'current_page_hash',
30
+ 'expected_page_hash',
31
+ );
32
+
33
+ return createPreviewError({
34
+ title: phrase.preview_missing_title,
35
+ message: phrase.preview_missing_explain_mismatch,
36
+ pre: `${phrase.element_id}: ${request.elementId}\n${phrase.current_page_hash}: ${request.hashMismatch.current}\n${phrase.expected_page_hash}: ${request.hashMismatch.expected}`,
37
+ });
38
+ }
39
+ else
40
+ {
41
+ const phrase = await usePhrases(
42
+ 'preview_missing_title',
43
+ 'preview_missing_explain',
44
+ 'element_id',
45
+ );
46
+
47
+ return createPreviewError({
48
+ title: phrase.preview_missing_title,
49
+ message: phrase.preview_missing_explain,
50
+ pre: `${phrase.element_id}: ${request.elementId}`,
51
+ });
52
+ }
53
+ }
54
+
55
+ if (request.type === PreviewRequestType.HashMismatch)
56
+ {
57
+ const phrase = await usePhrases(
58
+ 'preview_hash_mismatch_title',
59
+ 'preview_hash_mismatch_explain',
60
+ 'current_page_hash',
61
+ 'expected_page_hash',
62
+ );
63
+
64
+ return {
65
+ type: PreviewDataType.Alert,
66
+ theme: PreviewThemeName.Warn,
67
+ title: phrase.preview_hash_mismatch_title,
68
+ message: phrase.preview_hash_mismatch_explain,
69
+ pre: `${phrase.current_page_hash}: ${request.currentHash}\n${phrase.expected_page_hash}: ${request.expectedHash}`,
70
+ };
71
+ }
72
+
73
+ for (const build of builders)
74
+ {
75
+ const result = await build(request);
76
+ if (result)
77
+ return result;
78
+ }
79
+
80
+ throw createPreviewError({
81
+ message: `Unable to build preview data for request!`,
82
+ pre: JSON.stringify(request, null, 4),
83
+ });
84
+ }
@@ -0,0 +1,19 @@
1
+ import { PreviewDataType, type PreviewDataBase } from '../data';
2
+ import { PreviewThemeName } from '../state';
3
+
4
+ export type AlertData = Partial<{
5
+ theme: PreviewThemeName;
6
+ title: string;
7
+ message: string;
8
+ pre: string;
9
+ }>;
10
+
11
+ export type PreviewDataAlert = PreviewDataBase & AlertData;
12
+
13
+ export function createPreviewError(alertData: AlertData): PreviewDataAlert {
14
+ return {
15
+ type: PreviewDataType.Alert,
16
+ theme: PreviewThemeName.Error,
17
+ ...alertData,
18
+ };
19
+ }
@@ -0,0 +1,8 @@
1
+ import type { PreviewDataBase, PreviewDataType } from '../data';
2
+ import type { PreviewFooter } from '../footer';
3
+
4
+ export interface PreviewDataCustom extends PreviewDataBase {
5
+ type: PreviewDataType.Custom;
6
+ message: string;
7
+ footer?: PreviewFooter;
8
+ }
@@ -0,0 +1,24 @@
1
+ import { PreviewDataType, type PreviewDataBase } from '../data';
2
+ import { PreviewRequestType, type PreviewRequest } from '../request';
3
+
4
+ export interface PreviewDataGenericLink extends PreviewDataBase {
5
+ type: PreviewDataType.GenericLink;
6
+ external: boolean;
7
+ href: string;
8
+ }
9
+
10
+ export async function buildGenericLink(
11
+ request: PreviewRequest,
12
+ ): Promise<PreviewDataGenericLink> {
13
+ if (request.type !== PreviewRequestType.Link) return;
14
+
15
+ const { linkTarget } = request;
16
+
17
+ if (!['external', 'absolute'].includes(linkTarget.type)) return;
18
+
19
+ return {
20
+ type: PreviewDataType.GenericLink,
21
+ external: linkTarget.type === 'external',
22
+ href: linkTarget.href,
23
+ };
24
+ }
@@ -0,0 +1,20 @@
1
+ import { PreviewDataType, type PreviewDataBase } from '../data';
2
+ import { PreviewRequestType, type PreviewRequest } from '../request';
3
+ import type { PreviewFooter } from '../footer';
4
+
5
+ export interface PreviewDataPageLink extends PreviewDataBase {
6
+ type: PreviewDataType.PageLink;
7
+ footer: PreviewFooter;
8
+ }
9
+
10
+ export async function buildPageLink(
11
+ request: PreviewRequest,
12
+ ): Promise<PreviewDataPageLink> {
13
+ if (request.type !== PreviewRequestType.Link) return;
14
+
15
+ const { linkTarget } = request;
16
+
17
+ if (linkTarget.type !== 'page') return;
18
+
19
+ return await $fetch(`/api/preview/page${linkTarget._href}`);
20
+ }
@@ -0,0 +1,70 @@
1
+ import type { BitranContent } from 'bitran';
2
+
3
+ import type { BitranContext } from '@erudit/shared/bitran/context';
4
+
5
+ import { PreviewDataType, type PreviewDataBase } from '../data';
6
+ import type { PreviewFooter } from '../footer';
7
+ import { PreviewRequestType, type PreviewRequest } from '../request';
8
+ import { encodeBitranLocation } from '@erudit/shared/bitran/location';
9
+
10
+ export interface PreviewDataUnique extends PreviewDataBase {
11
+ type: PreviewDataType.Unique;
12
+ productName: string;
13
+ bitran: {
14
+ context: BitranContext;
15
+ content: BitranContent;
16
+ };
17
+ footer: PreviewFooter;
18
+ }
19
+
20
+ export async function buildUnique(
21
+ request: PreviewRequest,
22
+ ): Promise<PreviewDataUnique> {
23
+ if (request.type !== PreviewRequestType.Link) return;
24
+
25
+ const { linkTarget } = request;
26
+
27
+ if (linkTarget.type !== 'unique') return;
28
+
29
+ const serverData = (await $fetch(
30
+ `/api/preview/unique/${encodeBitranLocation(linkTarget._absoluteStrLocation!)}`,
31
+ )) as any;
32
+ const productName = serverData.bitran.productName;
33
+ const customTitle = serverData.productTitle;
34
+
35
+ const icon = await useBitranProductIcon(productName);
36
+
37
+ const productPhraseName = await (async () => {
38
+ const productPhrase = await useBitranProductLanguage(productName);
39
+ try {
40
+ return productPhrase('_name');
41
+ } catch {
42
+ return productName;
43
+ }
44
+ })();
45
+
46
+ let secondary = serverData.context
47
+ .filter((i) => !i.hidden)
48
+ .map((i) => i.title)
49
+ .join(' / ');
50
+ secondary += customTitle
51
+ ? (secondary ? ' • ' : '') + productPhraseName
52
+ : '';
53
+
54
+ const primary = customTitle || productPhraseName || productName;
55
+
56
+ return {
57
+ type: PreviewDataType.Unique,
58
+ productName,
59
+ bitran: {
60
+ context: serverData.bitran.context,
61
+ content: serverData.bitran.content as any,
62
+ },
63
+ footer: {
64
+ iconSvg: icon,
65
+ primary,
66
+ secondary,
67
+ href: linkTarget._href,
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,26 @@
1
+ import type { PreviewDataAlert } from './data/alert';
2
+ import type { PreviewDataCustom } from './data/custom';
3
+ import type { PreviewDataGenericLink } from './data/genericLink';
4
+ import type { PreviewDataPageLink } from './data/pageLink';
5
+ import type { PreviewDataUnique } from './data/unique';
6
+
7
+ export enum PreviewDataType
8
+ {
9
+ Alert = 'alert',
10
+ Custom = 'custom',
11
+ GenericLink = 'generic-link',
12
+ PageLink = 'page-link',
13
+ Unique = 'unique',
14
+ }
15
+
16
+ export interface PreviewDataBase
17
+ {
18
+ type: `${PreviewDataType}`;
19
+ }
20
+
21
+ export type PreviewData =
22
+ PreviewDataAlert |
23
+ PreviewDataCustom |
24
+ PreviewDataGenericLink |
25
+ PreviewDataPageLink |
26
+ PreviewDataUnique;
@@ -0,0 +1,39 @@
1
+ import type { Component } from 'vue';
2
+
3
+ import { PreviewDataType, type PreviewData } from './data';
4
+
5
+ import {
6
+ LazyPreviewDisplayAlert,
7
+ LazyPreviewDisplayCustom,
8
+ LazyPreviewDisplayGenericLink,
9
+ LazyPreviewDisplayPageLink,
10
+ LazyPreviewDisplayUnique,
11
+ } from '#components';
12
+ import { createPreviewError } from './data/alert';
13
+
14
+ export interface PreviewDisplayProps<T extends PreviewData>
15
+ {
16
+ data: T;
17
+ }
18
+
19
+ export function getPreviewDisplayComponent(data: PreviewData): Component
20
+ {
21
+ switch (data.type)
22
+ {
23
+ case PreviewDataType.Alert:
24
+ return LazyPreviewDisplayAlert;
25
+ case PreviewDataType.Custom:
26
+ return LazyPreviewDisplayCustom;
27
+ case PreviewDataType.GenericLink:
28
+ return LazyPreviewDisplayGenericLink;
29
+ case PreviewDataType.PageLink:
30
+ return LazyPreviewDisplayPageLink;
31
+ case PreviewDataType.Unique:
32
+ return LazyPreviewDisplayUnique;
33
+ }
34
+
35
+ throw createPreviewError({
36
+ message: `Unable to build preview data for request!`,
37
+ pre: JSON.stringify(data, null, 4),
38
+ });
39
+ }
@@ -0,0 +1,9 @@
1
+ import type { MyIconName } from '#my-icons';
2
+
3
+ export type PreviewFooter = Partial<{
4
+ iconName: MyIconName;
5
+ iconSvg: string;
6
+ secondary: string;
7
+ primary: string;
8
+ href: string;
9
+ }>;
@@ -0,0 +1,51 @@
1
+ import type { LinkData } from '@erudit/shared/bitran/products/link/shared';
2
+ import type { LinkTarget } from '@erudit/shared/bitran/products/link/target';
3
+
4
+ import type { PreviewData } from './data';
5
+
6
+ export enum PreviewRequestType {
7
+ Link = 'link',
8
+ Data = 'data',
9
+ MissingElement = 'missing-element',
10
+ HashMismatch = 'hash-mismatch',
11
+ }
12
+
13
+ export interface PreviewRequestData extends PreviewRequestBase {
14
+ type: PreviewRequestType.Data;
15
+ data: PreviewData;
16
+ }
17
+
18
+ export interface PreviewRequestLink extends PreviewRequestBase {
19
+ type: PreviewRequestType.Link;
20
+ linkData: LinkData;
21
+ linkTarget: LinkTarget;
22
+ }
23
+
24
+ export interface PreviewRequestMissingElement extends PreviewRequestBase {
25
+ type: PreviewRequestType.MissingElement;
26
+ elementId: string;
27
+ hashMismatch?: {
28
+ current: string;
29
+ expected: string;
30
+ };
31
+ }
32
+
33
+ export interface PreviewRequestHashMismatch extends PreviewRequestBase {
34
+ type: PreviewRequestType.HashMismatch;
35
+ currentHash: string;
36
+ expectedHash: string;
37
+ }
38
+
39
+ export type PreviewRequest =
40
+ | PreviewRequestData
41
+ | PreviewRequestLink
42
+ | PreviewRequestMissingElement
43
+ | PreviewRequestHashMismatch;
44
+
45
+ //
46
+ //
47
+ //
48
+
49
+ interface PreviewRequestBase {
50
+ type: `${PreviewRequestType}`;
51
+ }
@@ -0,0 +1,63 @@
1
+ import { objectsEqual } from '@shared/utils/objectsEqual';
2
+ import { PreviewRequestType, type PreviewRequest } from './request';
3
+
4
+ export enum PreviewThemeName {
5
+ Error = 'error',
6
+ Warn = 'warn',
7
+ Success = 'success',
8
+ Brand = 'brand',
9
+ }
10
+
11
+ //
12
+
13
+ const _request = ref<PreviewRequest>();
14
+ const _visible = ref(false);
15
+ const _history = ref<PreviewRequest[]>([]);
16
+ const _theme = ref<PreviewThemeName>();
17
+ const _blinki = ref(0);
18
+
19
+ //
20
+
21
+ export const previewRequest = readonly(_request);
22
+ export const previewVisible = readonly(_visible);
23
+ export const previewTheme = readonly(_theme);
24
+ export const previewBlinkTrigger = readonly(_blinki);
25
+
26
+ export const hasPreviewHistory = computed(() => _history.value.length > 0);
27
+
28
+ export function showPreviousPreview() {
29
+ if (!hasPreviewHistory.value) return;
30
+
31
+ _request.value = _history.value.pop();
32
+ }
33
+
34
+ export function showPreview(request: PreviewRequest) {
35
+ if (objectsEqual(_request.value, request)) {
36
+ if (_visible.value) _blinki.value++;
37
+ else _visible.value = true;
38
+ return;
39
+ }
40
+
41
+ if (_request.value) _history.value.push(_request.value);
42
+
43
+ _request.value = request;
44
+ _visible.value = true;
45
+ }
46
+
47
+ export function closePreview() {
48
+ _visible.value = false;
49
+ _request.value = undefined;
50
+ _history.value.length = 0;
51
+ }
52
+
53
+ export function togglePreview(visible?: boolean) {
54
+ _visible.value = visible === undefined ? !_visible.value : visible;
55
+ }
56
+
57
+ export function setPreviewTheme(newTheme: PreviewThemeName) {
58
+ _theme.value = newTheme;
59
+ }
60
+
61
+ export function unsetPreviewTheme() {
62
+ _theme.value = undefined;
63
+ }
@@ -0,0 +1,3 @@
1
+ /* Instant color theme setup */
2
+ :root { color-scheme: light; }
3
+ :root[data-theme='dark'] { color-scheme: dark; }
@@ -0,0 +1,50 @@
1
+ @mixin transition($properties...)
2
+ {
3
+ transition: all var(--transitionSpeed) var(--transitionFunction);
4
+ transition-property: $properties;
5
+ }
6
+
7
+ @mixin dark
8
+ {
9
+ :root[data-theme='dark'] &
10
+ {
11
+ @content;
12
+ }
13
+ }
14
+
15
+ @mixin hoverLink
16
+ {
17
+ cursor: pointer;
18
+ color: inherit;
19
+ text-decoration: underline;
20
+ text-decoration-color: transparent;
21
+ @include transition(text-decoration-color);
22
+
23
+ &:hover
24
+ {
25
+ text-decoration: underline;
26
+ }
27
+ }
28
+
29
+ @mixin scroll
30
+ {
31
+ -webkit-overflow-scrolling: auto;
32
+
33
+ &::-webkit-scrollbar
34
+ {
35
+ -webkit-appearance: none;
36
+ height: 5px;
37
+ width: 5px;
38
+ }
39
+
40
+ &::-webkit-scrollbar-thumb
41
+ {
42
+ background: var(--scrollbar);
43
+ &:hover { background: color-mix(in srgb, var(--scrollbar), var(--invert) 15%); }
44
+ }
45
+
46
+ &::-webkit-scrollbar-corner
47
+ {
48
+ background: var(--border);
49
+ }
50
+ }