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.
- package/app/assets/icons/graduation.svg +3 -0
- package/app/components/aside/AsideMinor.vue +41 -17
- package/app/components/aside/major/panes/Pages.vue +11 -14
- package/app/components/aside/minor/{Contribute.vue → AsideMinorContribute.vue} +35 -5
- package/app/components/aside/minor/content/AsideMinorContent.vue +7 -10
- package/app/components/aside/minor/contributor/AsideMinorContributor.vue +78 -0
- package/app/components/aside/minor/contributor/BookContribution.vue +64 -0
- package/app/components/aside/minor/topic/AsideMinorTopic.vue +1 -4
- package/app/components/aside/minor/topic/TopicContributors.vue +3 -3
- package/app/components/aside/minor/topic/TopicNav.vue +6 -6
- package/app/components/aside/minor/topic/TopicToc.vue +1 -2
- package/app/components/bitran/BitranContent.vue +15 -16
- package/app/components/contributor/ContributorAvatar.vue +3 -3
- package/app/components/main/MainBitranContent.vue +41 -0
- package/app/components/main/{utils/Breadcrumb.vue → MainBreadcrumb.vue} +12 -19
- package/app/components/main/MainDescription.vue +24 -0
- package/app/components/main/{utils/ContentTitle.vue → MainTitle.vue} +11 -2
- package/app/components/main/content/ContentBreadcrumb.vue +28 -0
- package/app/components/main/{utils → content}/ContentSection.vue +2 -2
- package/app/components/main/topic/MainTopic.vue +15 -20
- package/app/components/main/topic/TopicPartSwitch.vue +9 -3
- package/app/components/preview/display/Unique.vue +2 -11
- package/app/composables/adsAllowed.ts +1 -1
- package/app/composables/majorPane.ts +3 -2
- package/app/composables/phrases.ts +21 -9
- package/app/pages/book/[...bookId].vue +6 -11
- package/app/pages/contributor/[contributorId].vue +225 -0
- package/app/pages/contributors.vue +183 -0
- package/app/pages/group/[...groupId].vue +11 -19
- package/app/scripts/preview/data/unique.ts +18 -21
- package/languages/en.ts +12 -3
- package/languages/ru.ts +12 -3
- package/package.json +5 -5
- package/server/api/aside/minor/book/[...bookId].ts +18 -0
- package/server/api/aside/minor/contributor/[contributorId].ts +18 -0
- package/server/api/aside/minor/group/[...groupId].ts +18 -0
- package/server/api/aside/minor/news.ts +2 -2
- package/server/api/aside/minor/topic.ts +36 -0
- package/server/api/bitran/content/[...location].ts +4 -1
- package/server/api/contributor/list.ts +44 -0
- package/server/api/contributor/page/[contributorId].ts +14 -0
- package/server/api/preview/unique/[...location].ts +10 -23
- package/server/plugin/bitran/content.ts +34 -19
- package/server/plugin/bitran/location.ts +6 -2
- package/server/plugin/build/jobs/contributors.ts +3 -0
- package/server/plugin/db/entities/Contributor.ts +9 -0
- package/server/plugin/repository/asideMinor.ts +51 -0
- package/server/plugin/repository/book.ts +16 -0
- package/server/plugin/repository/contributor.ts +90 -0
- package/shared/aside/minor.ts +32 -28
- package/shared/bitran/content.ts +9 -0
- package/shared/breadcrumb.ts +7 -0
- package/shared/contributor.ts +28 -0
- package/shared/types/language.ts +8 -3
- package/app/components/aside/minor/AsideMinorContributor.vue +0 -5
- package/app/components/main/utils/ContentDescription.vue +0 -19
- package/app/composables/bitranContent.ts +0 -96
- package/app/pages/members.vue +0 -5
- package/server/api/aside/minor/path.ts +0 -82
- package/shared/bitran/stringContent.ts +0 -6
- /package/app/components/main/{utils → content}/ContentDecoration.vue +0 -0
- /package/app/components/main/{utils → content}/ContentPopover.vue +0 -0
- /package/app/components/main/{utils → content}/ContentPopovers.vue +0 -0
- /package/app/components/main/{utils → content}/ContentReferences.vue +0 -0
- /package/app/components/main/{utils → content}/reference/ReferenceGroup.vue +0 -0
- /package/app/components/main/{utils → content}/reference/ReferenceItem.vue +0 -0
- /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
|
|
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
|
|
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
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
|
|
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)
|
|
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', '
|
|
6
|
+
const phrase = await usePhrases('main_page', 'contributors');
|
|
7
7
|
|
|
8
|
-
const { data:
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
30
|
-
:
|
|
31
|
-
:
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
|
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 {
|
|
2
|
+
import type { AsideMinorContentBase } from '@shared/aside/minor';
|
|
3
3
|
import { injectAsideData } from '@app/scripts/aside/minor/state';
|
|
4
4
|
|
|
5
|
-
const contentData = injectAsideData<
|
|
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.
|
|
17
|
-
:link="contentData.
|
|
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.
|
|
28
|
-
:link="contentData.
|
|
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.
|
|
30
|
+
if (previousFullId === topicData.value.fullContentId) return;
|
|
31
31
|
|
|
32
|
-
previousFullId = topicData.value.
|
|
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.
|
|
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.
|
|
21
|
-
:link="topicData.
|
|
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.
|
|
29
|
+
:link="topicData.partLinks?.[topicPart]"
|
|
30
30
|
:active="topicPart === currentTopicPart"
|
|
31
31
|
/>
|
|
32
32
|
|
|
33
33
|
<AsideMinorTopLink
|
|
34
|
-
:title="topicData.
|
|
35
|
-
:link="topicData.
|
|
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.
|
|
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
|
-
|
|
14
|
-
context: BitranContext;
|
|
10
|
+
rawContent: RawBitranContent;
|
|
15
11
|
}>();
|
|
16
12
|
|
|
17
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
31
|
+
--_avatarSize: 40px;
|
|
32
32
|
--_avatarBlendColor: var(--brand);
|
|
33
33
|
|
|
34
34
|
position: relative;
|
|
35
|
-
width:
|
|
36
|
-
height:
|
|
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 {
|
|
3
|
-
import type { Context } from '@erudit/shared/content/context';
|
|
2
|
+
import type { BreadcrumbItem } from '@erudit/shared/breadcrumb';
|
|
4
3
|
|
|
5
|
-
defineProps<{
|
|
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
|
-
|
|
18
|
-
.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<MyIcon :name="
|
|
23
|
-
{{
|
|
24
|
-
</
|
|
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>
|