erudit 3.0.0-dev.18 → 3.0.0-dev.19

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 (67) hide show
  1. package/app/assets/icons/graduation.svg +3 -0
  2. package/app/components/aside/AsideMinor.vue +41 -17
  3. package/app/components/aside/major/panes/Pages.vue +11 -14
  4. package/app/components/aside/minor/{Contribute.vue → AsideMinorContribute.vue} +35 -5
  5. package/app/components/aside/minor/content/AsideMinorContent.vue +7 -10
  6. package/app/components/aside/minor/contributor/AsideMinorContributor.vue +78 -0
  7. package/app/components/aside/minor/contributor/BookContribution.vue +64 -0
  8. package/app/components/aside/minor/topic/AsideMinorTopic.vue +1 -4
  9. package/app/components/aside/minor/topic/TopicContributors.vue +3 -3
  10. package/app/components/aside/minor/topic/TopicNav.vue +6 -6
  11. package/app/components/aside/minor/topic/TopicToc.vue +1 -2
  12. package/app/components/bitran/BitranContent.vue +15 -16
  13. package/app/components/contributor/ContributorAvatar.vue +3 -3
  14. package/app/components/main/MainBitranContent.vue +41 -0
  15. package/app/components/main/{utils/Breadcrumb.vue → MainBreadcrumb.vue} +12 -19
  16. package/app/components/main/MainDescription.vue +24 -0
  17. package/app/components/main/{utils/ContentTitle.vue → MainTitle.vue} +11 -2
  18. package/app/components/main/content/ContentBreadcrumb.vue +28 -0
  19. package/app/components/main/{utils → content}/ContentSection.vue +2 -2
  20. package/app/components/main/topic/MainTopic.vue +15 -20
  21. package/app/components/main/topic/TopicPartSwitch.vue +9 -3
  22. package/app/components/preview/display/Unique.vue +2 -11
  23. package/app/composables/adsAllowed.ts +1 -1
  24. package/app/composables/majorPane.ts +3 -2
  25. package/app/composables/phrases.ts +21 -9
  26. package/app/pages/book/[...bookId].vue +6 -11
  27. package/app/pages/contributor/[contributorId].vue +225 -0
  28. package/app/pages/contributors.vue +183 -0
  29. package/app/pages/group/[...groupId].vue +11 -19
  30. package/app/scripts/preview/data/unique.ts +18 -21
  31. package/languages/en.ts +12 -3
  32. package/languages/ru.ts +12 -3
  33. package/package.json +5 -5
  34. package/server/api/aside/minor/book/[...bookId].ts +18 -0
  35. package/server/api/aside/minor/contributor/[contributorId].ts +18 -0
  36. package/server/api/aside/minor/group/[...groupId].ts +18 -0
  37. package/server/api/aside/minor/news.ts +2 -2
  38. package/server/api/aside/minor/topic.ts +36 -0
  39. package/server/api/bitran/content/[...location].ts +4 -1
  40. package/server/api/contributor/list.ts +44 -0
  41. package/server/api/contributor/page/[contributorId].ts +14 -0
  42. package/server/api/preview/unique/[...location].ts +10 -23
  43. package/server/plugin/bitran/content.ts +34 -19
  44. package/server/plugin/bitran/location.ts +6 -2
  45. package/server/plugin/build/jobs/contributors.ts +3 -0
  46. package/server/plugin/db/entities/Contributor.ts +9 -0
  47. package/server/plugin/repository/asideMinor.ts +51 -0
  48. package/server/plugin/repository/book.ts +16 -0
  49. package/server/plugin/repository/contributor.ts +90 -0
  50. package/shared/aside/minor.ts +32 -28
  51. package/shared/bitran/content.ts +9 -0
  52. package/shared/breadcrumb.ts +7 -0
  53. package/shared/contributor.ts +28 -0
  54. package/shared/types/language.ts +8 -3
  55. package/app/components/aside/minor/AsideMinorContributor.vue +0 -5
  56. package/app/components/main/utils/ContentDescription.vue +0 -19
  57. package/app/composables/bitranContent.ts +0 -96
  58. package/app/pages/members.vue +0 -5
  59. package/server/api/aside/minor/path.ts +0 -82
  60. package/shared/bitran/stringContent.ts +0 -6
  61. /package/app/components/main/{utils → content}/ContentDecoration.vue +0 -0
  62. /package/app/components/main/{utils → content}/ContentPopover.vue +0 -0
  63. /package/app/components/main/{utils → content}/ContentPopovers.vue +0 -0
  64. /package/app/components/main/{utils → content}/ContentReferences.vue +0 -0
  65. /package/app/components/main/{utils → content}/reference/ReferenceGroup.vue +0 -0
  66. /package/app/components/main/{utils → content}/reference/ReferenceItem.vue +0 -0
  67. /package/app/components/main/{utils → content}/reference/ReferenceSource.vue +0 -0
@@ -0,0 +1,3 @@
1
+ <svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
2
+ <path d="m5.2 18.2q-0.6-0.3-0.9-0.8-0.2-0.5-0.2-1.1v-5l-2.6-1.4q-0.2-0.2-0.4-0.4-0.1-0.2-0.1-0.5 0-0.3 0.1-0.5 0.2-0.3 0.4-0.4l8.9-4.8q0.2-0.2 0.5-0.2 0.2-0.1 0.5-0.1 0.2 0 0.5 0.1 0.2 0 0.5 0.2l9.9 5.4q0.3 0.1 0.4 0.4 0.2 0.2 0.2 0.5v6.7q0 0.4-0.3 0.7-0.3 0.3-0.8 0.3-0.4 0-0.7-0.3-0.3-0.3-0.3-0.7v-6.2l-2.1 1.2v5q0 0.6-0.3 1.1-0.3 0.5-0.8 0.8l-5.2 2.8q-0.3 0.1-0.5 0.2-0.3 0-0.5 0-0.3 0-0.5 0-0.3-0.1-0.5-0.2zm6.2-5.3l7.2-3.9-7.2-3.9-7.2 3.9zm0 6.3l5.3-2.8v-4l-4.2 2.4q-0.3 0.1-0.5 0.2-0.3 0-0.6 0-0.2 0-0.5 0-0.2-0.1-0.5-0.2l-4.2-2.4v4z"/>
3
+ </svg>
@@ -1,5 +1,7 @@
1
1
  <script lang="ts" setup>
2
- import type { AsideMinorData } from '@shared/aside/minor';
2
+ import { isTopicPart } from '@erudit-js/cog/schema';
3
+
4
+ import { type AsideMinorNews, type AsideMinorData } from '@shared/aside/minor';
3
5
  import { trailingSlash } from '@erudit/utils/slash';
4
6
  import { asideMinorKey } from '@app/scripts/aside/minor/state';
5
7
 
@@ -7,6 +9,7 @@ import {
7
9
  LazyAsideMinorNews,
8
10
  LazyAsideMinorTopic,
9
11
  LazyAsideMinorContent,
12
+ LazyAsideMinorContributor,
10
13
  } from '#components';
11
14
 
12
15
  let setupI = 0;
@@ -19,7 +22,9 @@ const AsideMinorPane = shallowRef<Component>();
19
22
 
20
23
  async function setupAsideMinorData() {
21
24
  const currentSetupI = ++setupI;
22
- const dataKey = trailingSlash(route.path, false);
25
+ const path = trailingSlash(route.path, false).substring(1);
26
+ const pathParts = path.split('/');
27
+
23
28
  const payloadKey = 'aside-minor';
24
29
  const asideMinorPayload =
25
30
  (nuxtApp.static.data[payloadKey] ||=
@@ -27,23 +32,40 @@ async function setupAsideMinorData() {
27
32
  {});
28
33
 
29
34
  const data: AsideMinorData = await (async () => {
30
- const payloadKeyValue: AsideMinorData | 'news' = (asideMinorPayload[
31
- dataKey
32
- ] ||= await $fetch(`/api/aside/minor/path`, {
33
- query: { path: dataKey },
34
- }));
35
-
36
- if (payloadKeyValue === 'news') {
37
- // We do not save news in the payload as it will always have the same content.
38
- // So we prerender news once and use the same data for all pages.
39
- nuxtApp.runWithContext(() =>
40
- prerenderRoutes('/api/aside/minor/news'),
41
- );
42
- return await $fetch('/api/aside/minor/news');
43
- } else return payloadKeyValue;
35
+ const firstPathPart = pathParts[0];
36
+
37
+ if (isTopicPart(firstPathPart)) {
38
+ return (asideMinorPayload[path] ||= await $fetch(
39
+ `/api/aside/minor/topic`,
40
+ {
41
+ query: {
42
+ topicPart: firstPathPart,
43
+ topicId: pathParts.slice(1).join('/'),
44
+ },
45
+ },
46
+ ));
47
+ }
48
+
49
+ switch (firstPathPart) {
50
+ case 'book':
51
+ case 'group':
52
+ return (asideMinorPayload[path] ||= await $fetch(
53
+ `/api/aside/minor/${pathParts.join('/')}`,
54
+ ));
55
+ case 'contributor':
56
+ return (asideMinorPayload[path] ||= await $fetch(
57
+ `/api/aside/minor/contributor/${pathParts[1]}`,
58
+ ));
59
+ default:
60
+ prerenderRoutes(['/api/aside/minor/news']);
61
+ return await $fetch<AsideMinorNews>(`/api/aside/minor/news`);
62
+ }
44
63
  })();
45
64
 
46
- if (currentSetupI !== setupI) return; // This data is outdated because new `setupAsideMinorData` was called.
65
+ if (currentSetupI !== setupI) {
66
+ // This data is outdated because new `setupAsideMinorData` was called
67
+ return;
68
+ }
47
69
 
48
70
  asideData.value = data;
49
71
 
@@ -54,6 +76,8 @@ async function setupAsideMinorData() {
54
76
  case 'book':
55
77
  case 'group':
56
78
  return LazyAsideMinorContent;
79
+ case 'contributor':
80
+ return LazyAsideMinorContributor;
57
81
  }
58
82
 
59
83
  return LazyAsideMinorNews;
@@ -3,32 +3,29 @@ import PaneContentScroll from '../PaneContentScroll.vue';
3
3
 
4
4
  const route = useRoute();
5
5
 
6
- const phrase = await usePhrases('main_page', 'members');
6
+ const phrase = await usePhrases('main_page', 'contributors');
7
7
 
8
- const { data: memberCount } = await useAsyncData<number>('members-count', () =>
9
- $fetch('/api/contributor/count'),
8
+ const { data: contributorCount } = await useAsyncData<number>(
9
+ 'contributor-count',
10
+ () => $fetch('/api/contributor/count'),
10
11
  );
11
-
12
- function linkAttrs(link: string) {
13
- return {
14
- link,
15
- active: route.path === link,
16
- };
17
- }
18
12
  </script>
19
13
 
20
14
  <template>
21
15
  <PaneContentScroll>
22
16
  <AsideListItem
23
17
  icon="house"
24
- v-bind="linkAttrs('/')"
18
+ link="/"
19
+ :active="route.path === '/'"
25
20
  :main="phrase.main_page"
26
21
  />
27
22
  <AsideListItem
23
+ v-if="contributorCount"
28
24
  icon="users"
29
- v-bind="linkAttrs('/members')"
30
- :main="phrase.members"
31
- :secondary="memberCount!.toString()"
25
+ link="/contributors"
26
+ :active="route.path.startsWith('/contributor')"
27
+ :main="phrase.contributors"
28
+ :secondary="contributorCount!.toString()"
32
29
  />
33
30
  </PaneContentScroll>
34
31
  </template>
@@ -1,9 +1,14 @@
1
1
  <script lang="ts" setup>
2
2
  import eruditConfig from '#erudit/config';
3
+ import { injectAsideData } from '@erudit/app/scripts/aside/minor/state';
4
+ import {
5
+ type AsideMinorContentBase,
6
+ type AsideMinorTopic,
7
+ } from '@shared/aside/minor';
3
8
 
4
9
  import AsideOverlayPane from '../utils/AsideOverlayPane.vue';
5
10
 
6
- const props = defineProps<{ contentId: string }>();
11
+ const asideData = injectAsideData<AsideMinorContentBase>();
7
12
  const phrase = await usePhrases(
8
13
  'make_contribution',
9
14
  'material_improvement',
@@ -11,6 +16,7 @@ const phrase = await usePhrases(
11
16
  'how_to_improve',
12
17
  'report_problem',
13
18
  );
19
+
14
20
  const paneVisible = defineModel<boolean>('pane');
15
21
 
16
22
  const issueLink = computed(() => {
@@ -22,14 +28,35 @@ const issueLink = computed(() => {
22
28
 
23
29
  const editPageLink = computed(() => {
24
30
  const ghRepository = eruditConfig.repository;
25
- return ghRepository
26
- ? `https://github.com/${ghRepository.name}/tree/${ghRepository.branch}/content/${props.contentId}`
27
- : null;
31
+
32
+ if (!ghRepository) {
33
+ return undefined;
34
+ }
35
+
36
+ let link = `https://github.com/${ghRepository.name}/edit/${ghRepository.branch}/content/${asideData.value.fsContentDirectory}/`;
37
+
38
+ if (asideData.value.type === 'topic') {
39
+ link += `${(<AsideMinorTopic>asideData.value).part}.bi`;
40
+ }
41
+
42
+ return link;
43
+ });
44
+
45
+ const buttonVisible = computed(() => {
46
+ return (
47
+ eruditConfig.content?.howToImproveLink ||
48
+ issueLink.value ||
49
+ editPageLink.value
50
+ );
28
51
  });
29
52
  </script>
30
53
 
31
54
  <template>
32
- <button :class="$style.contribute" @click="paneVisible = true">
55
+ <button
56
+ v-if="buttonVisible"
57
+ :class="$style.contribute"
58
+ @click="paneVisible = true"
59
+ >
33
60
  <div :class="$style.icon"><MyIcon name="draw" /></div>
34
61
  <div :class="$style.label">{{ phrase.make_contribution }}</div>
35
62
  </button>
@@ -37,8 +64,11 @@ const editPageLink = computed(() => {
37
64
  <AsideOverlayPane v-if="paneVisible" stick="bottom">
38
65
  <div :class="$style.paneBottomActions">
39
66
  <AsideListItem
67
+ v-if="eruditConfig.content?.howToImproveLink"
40
68
  icon="circle-help"
41
69
  :main="phrase.how_to_improve"
70
+ :link="eruditConfig.content.howToImproveLink"
71
+ target="_blank"
42
72
  />
43
73
  <AsideListItem
44
74
  v-if="issueLink"
@@ -1,8 +1,8 @@
1
1
  <script lang="ts" setup>
2
- import type { AsideMinorContent } from '@shared/aside/minor';
2
+ import type { AsideMinorContentBase } from '@shared/aside/minor';
3
3
  import { injectAsideData } from '@app/scripts/aside/minor/state';
4
4
 
5
- const contentData = injectAsideData<AsideMinorContent>();
5
+ const contentData = injectAsideData<AsideMinorContentBase>();
6
6
  const phrase = await usePhrases('contributors', 'no_contributors');
7
7
  const contributePaneVisible = ref(false);
8
8
 
@@ -13,8 +13,8 @@ watch(contentData, () => (contributePaneVisible.value = false));
13
13
  <AsideMinorPane :class="$style.asideMinorContent">
14
14
  <section :class="$style.contentNav">
15
15
  <AsideMinorTopLink
16
- :title="contentData.nav?.previous?.title"
17
- :link="contentData.nav?.previous?.link"
16
+ :title="contentData.previousNext?.previous?.title"
17
+ :link="contentData.previousNext?.previous?.link"
18
18
  :icon="'arrow-left'"
19
19
  />
20
20
 
@@ -24,8 +24,8 @@ watch(contentData, () => (contributePaneVisible.value = false));
24
24
  </div>
25
25
 
26
26
  <AsideMinorTopLink
27
- :title="contentData.nav?.next?.title"
28
- :link="contentData.nav?.next?.link"
27
+ :title="contentData.previousNext?.next?.title"
28
+ :link="contentData.previousNext?.next?.link"
29
29
  :icon="'arrow-left'"
30
30
  class="icon-flip-h"
31
31
  />
@@ -39,10 +39,7 @@ watch(contentData, () => (contributePaneVisible.value = false));
39
39
  <section v-else :class="$style.noContributors">
40
40
  {{ phrase.no_contributors }}
41
41
  </section>
42
- <AsideMinorContribute
43
- :contentId="contentData.contentId"
44
- v-model:pane="contributePaneVisible"
45
- />
42
+ <AsideMinorContribute v-model:pane="contributePaneVisible" />
46
43
  </AsideMinorPane>
47
44
  </template>
48
45
 
@@ -0,0 +1,78 @@
1
+ <script lang="ts" setup>
2
+ import { type AsideMinorContributor } from '@shared/aside/minor';
3
+ import { CONTENT_TYPE_ICON } from '@shared/icons';
4
+ import { injectAsideData } from '@erudit/app/scripts/aside/minor/state';
5
+
6
+ import BookContribution from './BookContribution.vue';
7
+
8
+ const contributorData = injectAsideData<AsideMinorContributor>();
9
+ const phrase = await usePhrases('contribution');
10
+
11
+ const bookContributions = (() => {
12
+ const grouped: Record<string, typeof contributorData.value.contributions> =
13
+ {};
14
+
15
+ contributorData.value.contributions.forEach((contribution) => {
16
+ const bookTitle = contribution.bookTitle || '';
17
+ if (!grouped[bookTitle]) {
18
+ grouped[bookTitle] = [];
19
+ }
20
+ grouped[bookTitle].push(contribution);
21
+ });
22
+
23
+ return grouped;
24
+ })();
25
+
26
+ const topLevelContributions = bookContributions[''];
27
+ delete bookContributions[''];
28
+ </script>
29
+
30
+ <template>
31
+ <AsideMinorPane>
32
+ <section :class="$style.contributionsHeader">
33
+ {{ phrase.contribution }}:
34
+ <span>{{ contributorData.contributions.length }}</span>
35
+ </section>
36
+ <div :class="$style.contributions">
37
+ <TreeContainer>
38
+ <template v-for="contribution of topLevelContributions">
39
+ <TreeItem
40
+ :icon="CONTENT_TYPE_ICON[contribution.contentType]"
41
+ :label="contribution.contentTitle"
42
+ :link="contribution.contentLink"
43
+ />
44
+ </template>
45
+ <BookContribution
46
+ v-for="(contributions, bookTitle) in bookContributions"
47
+ :bookTitle
48
+ :contributions
49
+ />
50
+ </TreeContainer>
51
+ </div>
52
+ </AsideMinorPane>
53
+ </template>
54
+
55
+ <style lang="scss" module>
56
+ .contributionsHeader {
57
+ display: flex;
58
+ align-items: center;
59
+ justify-content: center;
60
+
61
+ border-bottom: 1px solid var(--border);
62
+ color: var(--text);
63
+ font-size: 1.1em;
64
+ font-weight: 600;
65
+ height: 62px;
66
+
67
+ span {
68
+ padding-left: var(--gap);
69
+ color: var(--textMuted);
70
+ }
71
+ }
72
+
73
+ .contributions {
74
+ max-height: calc(100% - 62px);
75
+ overflow-y: auto;
76
+ @include scroll;
77
+ }
78
+ </style>
@@ -0,0 +1,64 @@
1
+ <script lang="ts" setup>
2
+ import type { Contribution } from '@shared/contributor';
3
+ import { CONTENT_TYPE_ICON } from '@shared/icons';
4
+
5
+ defineProps<{
6
+ bookTitle: string;
7
+ contributions: Contribution[];
8
+ }>();
9
+
10
+ const expanded = ref(false);
11
+ </script>
12
+
13
+ <template>
14
+ <div :class="[$style.bookContainer, expanded && $style.expanded]">
15
+ <TreeItem
16
+ :icon="CONTENT_TYPE_ICON.book"
17
+ :label="bookTitle"
18
+ :class="$style.bookTitle"
19
+ @click="expanded = !expanded"
20
+ >
21
+ <MyIcon :class="$style.bookExpandIcon" name="plus" />
22
+ </TreeItem>
23
+ <div :class="$style.bookContributions">
24
+ <TreeItem
25
+ v-for="contribution in contributions"
26
+ :icon="CONTENT_TYPE_ICON[contribution.contentType]"
27
+ :label="contribution.contentTitle"
28
+ :link="contribution.contentLink"
29
+ :level="1"
30
+ />
31
+ </div>
32
+ </div>
33
+ </template>
34
+
35
+ <style lang="scss" module>
36
+ .bookContainer {
37
+ .bookTitle {
38
+ @include transition(color, background);
39
+ }
40
+
41
+ &.expanded .bookTitle {
42
+ color: var(--text);
43
+ }
44
+
45
+ .bookExpandIcon {
46
+ @include transition(transform);
47
+ transform: rotate(0deg);
48
+ }
49
+
50
+ &.expanded .bookExpandIcon {
51
+ transform: rotate(45deg);
52
+ }
53
+
54
+ .bookContributions {
55
+ overflow: hidden;
56
+ height: 0;
57
+ @include transition(height);
58
+ }
59
+
60
+ &.expanded .bookContributions {
61
+ height: auto;
62
+ }
63
+ }
64
+ </style>
@@ -17,10 +17,7 @@ watch(topicData, () => (contributePaneVisible.value = false));
17
17
  <TopicNav />
18
18
  <TopicToc />
19
19
  <TopicContributors v-if="topicData.contributors" />
20
- <AsideMinorContribute
21
- :contentId="topicData.topicId"
22
- v-model:pane="contributePaneVisible"
23
- />
20
+ <AsideMinorContribute v-model:pane="contributePaneVisible" />
24
21
  </AsideMinorPane>
25
22
  </template>
26
23
 
@@ -1,9 +1,9 @@
1
1
  <script lang="ts" setup>
2
2
  import { injectAsideData } from '@app/scripts/aside/minor/state';
3
3
  import { type AsideMinorTopic } from '@shared/aside/minor';
4
+ import type { ContentContributor } from '@shared/contributor';
4
5
 
5
6
  import AsideOverlayPane from '../../utils/AsideOverlayPane.vue';
6
- import type { ContentContributor } from '@shared/contributor';
7
7
 
8
8
  const phrase = await usePhrases('contributors');
9
9
  const topicData = injectAsideData<AsideMinorTopic>();
@@ -27,9 +27,9 @@ onMounted(() => {
27
27
  watch(
28
28
  topicData,
29
29
  () => {
30
- if (previousFullId === topicData.value.topicId) return;
30
+ if (previousFullId === topicData.value.fullContentId) return;
31
31
 
32
- previousFullId = topicData.value.topicId;
32
+ previousFullId = topicData.value.fullContentId;
33
33
  showcase.value = [...topicData.value.contributors!]
34
34
  .sort(() => 0.5 - Math.random())
35
35
  .slice(0, counter.value.showcase);
@@ -10,15 +10,15 @@ const phrase = await usePhrases('article', 'summary', 'practice');
10
10
  const topicData = injectAsideData<AsideMinorTopic>();
11
11
 
12
12
  const currentTopicPart = computed(() => {
13
- return topicData.value.location.type as TopicPart;
13
+ return topicData.value.part as TopicPart;
14
14
  });
15
15
  </script>
16
16
 
17
17
  <template>
18
18
  <section :class="$style.topicNav">
19
19
  <AsideMinorTopLink
20
- :title="topicData.nav?.previous?.title"
21
- :link="topicData.nav?.previous?.link"
20
+ :title="topicData.previousNext?.previous?.title"
21
+ :link="topicData.previousNext?.previous?.link"
22
22
  :icon="'arrow-left'"
23
23
  />
24
24
 
@@ -26,13 +26,13 @@ const currentTopicPart = computed(() => {
26
26
  v-for="topicPart of topicParts"
27
27
  :title="phrase[topicPart]"
28
28
  :icon="TOPIC_PART_ICON[topicPart]"
29
- :link="topicData.nav?.[topicPart]"
29
+ :link="topicData.partLinks?.[topicPart]"
30
30
  :active="topicPart === currentTopicPart"
31
31
  />
32
32
 
33
33
  <AsideMinorTopLink
34
- :title="topicData.nav?.next?.title"
35
- :link="topicData.nav?.next?.link"
34
+ :title="topicData.previousNext?.next?.title"
35
+ :link="topicData.previousNext?.next?.link"
36
36
  :icon="'arrow-left'"
37
37
  class="icon-flip-h"
38
38
  />
@@ -1,7 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { headingName } from '@erudit-js/bitran-elements/heading/shared';
3
3
 
4
- import { stringifyBitranLocation } from '@erudit-js/cog/schema';
5
4
  import type { TocItem } from '@erudit/shared/bitran/toc';
6
5
  import { topicLocation } from '@app/scripts/aside/minor/topic';
7
6
  import { injectAsideData } from '@app/scripts/aside/minor/state';
@@ -172,7 +171,7 @@ onMounted(() => {
172
171
  () => {
173
172
  disableLiveToc();
174
173
 
175
- if (!topicData.value.location || !topicLocation.value) return;
174
+ if (!topicData.value.part || !topicLocation.value) return;
176
175
 
177
176
  enableLiveToc();
178
177
  },
@@ -1,20 +1,19 @@
1
1
  <script lang="ts" setup>
2
2
  import { Bitran, type BitranContent } from '@bitran-js/renderer-vue';
3
- import {
4
- setEruditBitranRuntime,
5
- type BitranContext,
6
- } from '@erudit-js/cog/schema';
3
+ import { setEruditBitranRuntime } from '@erudit-js/cog/schema';
7
4
 
8
5
  import eruditConfig from '#erudit/config';
9
-
6
+ import type { RawBitranContent } from '@shared/bitran/content';
10
7
  import RenderWrapper from './RenderWrapper.vue';
11
8
 
12
9
  const props = defineProps<{
13
- content: BitranContent;
14
- context: BitranContext;
10
+ rawContent: RawBitranContent;
15
11
  }>();
16
12
 
17
- /* Remove transpiler from Bitran Vue Component at all? It takes RootNode already... */
13
+ for (const route of props.rawContent.routes) {
14
+ prerenderRoutes([route]);
15
+ }
16
+
18
17
  const bitranTranspiler = await useBitranTranspiler();
19
18
  const bitranRenderers = await useBitranRenderers();
20
19
 
@@ -22,17 +21,17 @@ const bitranRenderers = await useBitranRenderers();
22
21
  setEruditBitranRuntime(item, {
23
22
  eruditConfig,
24
23
  insideInclude: false,
25
- context: props.context,
24
+ context: props.rawContent.context,
26
25
  });
27
26
  });
28
27
 
29
- const formatText = useFormatText();
28
+ const root = await bitranTranspiler.parser.parse(props.rawContent.biCode);
29
+ const bitranContent: BitranContent = {
30
+ root,
31
+ renderDataStorage: props.rawContent.storage,
32
+ };
30
33
 
31
- // onMounted(() => {
32
- // watch(urlElement, () => {
33
- // //console.log('Highlighting product:', urlElement.value);
34
- // }, { immediate: true });
35
- // });
34
+ const formatText = useFormatText();
36
35
 
37
36
  const isDev = import.meta.dev;
38
37
  const isServer = import.meta.server;
@@ -43,7 +42,7 @@ const isServer = import.meta.server;
43
42
  :class="$style.eruditBitranContainer"
44
43
  :transpiler="bitranTranspiler"
45
44
  :renderers="bitranRenderers"
46
- :content
45
+ :content="bitranContent"
47
46
  :editMode="false"
48
47
  :formatText
49
48
  :RenderWrapper
@@ -28,12 +28,12 @@ const hasAvatar = computed(() => !!props.avatar);
28
28
 
29
29
  <style lang="scss" module>
30
30
  .contributorAvatar {
31
- $avatarSize: 40px;
31
+ --_avatarSize: 40px;
32
32
  --_avatarBlendColor: var(--brand);
33
33
 
34
34
  position: relative;
35
- width: #{$avatarSize};
36
- height: #{$avatarSize};
35
+ width: var(--_avatarSize);
36
+ height: var(--_avatarSize);
37
37
  border-radius: 50%;
38
38
  overflow: hidden;
39
39
  background: var(--border);
@@ -0,0 +1,41 @@
1
+ <script lang="ts" setup>
2
+ import {
3
+ stringifyBitranLocation,
4
+ type BitranLocation,
5
+ } from '@erudit-js/cog/schema';
6
+ import type { RawBitranContent } from '@erudit/shared/bitran/content';
7
+
8
+ const props = defineProps<{ location: BitranLocation }>();
9
+
10
+ const nuxtApp = useNuxtApp();
11
+ const stringLocation = stringifyBitranLocation(props.location);
12
+
13
+ const payloadKey = `main-bitran-content`;
14
+ const payloadValue: RawBitranContent =
15
+ (nuxtApp.static.data[payloadKey] ||=
16
+ nuxtApp.payload.data[payloadKey] ||=
17
+ {});
18
+
19
+ const payloadStringLocation = (() => {
20
+ if (!payloadValue?.context?.location) {
21
+ return undefined;
22
+ }
23
+
24
+ return stringifyBitranLocation(payloadValue.context.location);
25
+ })();
26
+
27
+ if (stringLocation !== payloadStringLocation) {
28
+ const rawBitranContent = await $fetch<RawBitranContent>(
29
+ `/api/bitran/content/${stringLocation}`,
30
+ {
31
+ responseType: 'json',
32
+ },
33
+ );
34
+
35
+ Object.assign(payloadValue, rawBitranContent);
36
+ }
37
+ </script>
38
+
39
+ <template>
40
+ <BitranContent :rawContent="payloadValue" />
41
+ </template>
@@ -1,27 +1,20 @@
1
1
  <script lang="ts" setup>
2
- import type { MyIconName } from '#my-icons';
3
- import type { Context } from '@erudit/shared/content/context';
2
+ import type { BreadcrumbItem } from '@erudit/shared/breadcrumb';
4
3
 
5
- defineProps<{ context: Context }>();
6
-
7
- const Link = defineNuxtLink({ prefetch: false });
8
-
9
- function getIcon(contextIcon: string) {
10
- return contextIcon as MyIconName;
11
- }
4
+ defineProps<{ items: BreadcrumbItem[] }>();
12
5
  </script>
13
6
 
14
7
  <template>
15
- <section :class="$style.breadcrumb">
16
- <template
17
- v-for="contextItem of context
18
- .slice(0, -1)
19
- .filter((item) => !item.hidden)"
20
- >
21
- <Link :to="contextItem.href" :class="$style.breadcrumbItem">
22
- <MyIcon :name="getIcon(contextItem.icon)" wrapper="span" />
23
- {{ contextItem.title }}
24
- </Link>
8
+ <section v-if="items.length" :class="$style.breadcrumb">
9
+ <template v-for="item in items">
10
+ <NuxtLink
11
+ :to="item.link"
12
+ :prefetch="false"
13
+ :class="$style.breadcrumbItem"
14
+ >
15
+ <MyIcon :name="item.icon" wrapper="span" />
16
+ {{ item.title }}
17
+ </NuxtLink>
25
18
  <MyIcon :class="$style.sep" name="angle-right" />
26
19
  </template>
27
20
  </section>