erudit 4.0.0-dev.1 → 4.0.0-dev.2

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 (62) hide show
  1. package/app/app.vue +1 -2
  2. package/app/components/FancyBold.vue +0 -1
  3. package/app/components/FancyCard.vue +1 -2
  4. package/app/components/ads/AdsBannerAside.vue +1 -2
  5. package/app/components/ads/AdsReplacer.vue +2 -2
  6. package/app/components/aside/AsideListItem.vue +1 -1
  7. package/app/components/aside/AsideSwitch.vue +18 -8
  8. package/app/components/aside/major/PaneSwitcher.vue +1 -1
  9. package/app/components/aside/minor/AsideMinorPlainHeader.vue +2 -3
  10. package/app/components/aside/minor/content/AsideMinorContentTopic.vue +1 -4
  11. package/app/components/aside/minor/content/ButtonPaneContributions.vue +2 -4
  12. package/app/components/aside/minor/content/ButtonPaneImprove.vue +2 -3
  13. package/app/components/aside/minor/content/Contribution.vue +1 -1
  14. package/app/components/aside/minor/content/TocItem.vue +35 -21
  15. package/app/components/aside/minor/news/AsideMinorNews.vue +1 -2
  16. package/app/components/aside/minor/news/NewsItem.vue +2 -2
  17. package/app/components/aside/minor/news/elements/Ref.vue +1 -1
  18. package/app/components/main/MainContentChild.vue +2 -3
  19. package/app/components/main/MainDescription.vue +1 -1
  20. package/app/components/main/MainQuickLink.vue +20 -5
  21. package/app/components/main/MainQuickLinks.vue +1 -3
  22. package/app/components/main/MainQuote.vue +3 -6
  23. package/app/components/main/MainSection.vue +6 -21
  24. package/app/components/main/MainTitle.vue +1 -2
  25. package/app/components/main/MainTopicPartSwitch.vue +1 -1
  26. package/app/components/main/connections/Deps.vue +1 -1
  27. package/app/components/main/connections/Externals.vue +92 -34
  28. package/app/components/main/connections/MainConnections.vue +61 -8
  29. package/app/components/main/connections/MainConnectionsButton.vue +3 -2
  30. package/app/components/main/connections/ScrollPane.vue +2 -3
  31. package/app/components/main/contentStats/Item.vue +1 -2
  32. package/app/components/main/contentStats/MainContentStats.vue +6 -3
  33. package/app/components/preview/Preview.vue +1 -2
  34. package/app/components/preview/PreviewScreen.vue +1 -2
  35. package/app/components/site/SiteAside.vue +2 -4
  36. package/app/components/site/SiteMain.vue +1 -4
  37. package/app/components/tree/TreeItem.vue +1 -1
  38. package/app/composables/og.ts +19 -2
  39. package/app/pages/contributor/[contributorId].vue +1 -2
  40. package/app/pages/index.vue +1 -4
  41. package/app/styles/main.css +0 -1
  42. package/package.json +4 -4
  43. package/server/erudit/cameos/build.ts +77 -27
  44. package/server/erudit/content/global/build.ts +27 -1
  45. package/server/erudit/content/nav/build.ts +36 -4
  46. package/server/erudit/content/repository/elementSnippets.ts +51 -11
  47. package/server/erudit/content/repository/externals.ts +38 -9
  48. package/server/erudit/content/resolve/index.ts +172 -21
  49. package/server/erudit/content/resolve/topic.ts +93 -32
  50. package/server/erudit/content/resolve/utils/insertContentResolved.ts +48 -9
  51. package/server/erudit/content/search.ts +31 -3
  52. package/server/erudit/contributors/build.ts +106 -51
  53. package/server/erudit/db/repository/pushFile.ts +7 -4
  54. package/server/erudit/db/schema/content.ts +2 -2
  55. package/server/erudit/db/schema/contentSnippets.ts +3 -4
  56. package/server/erudit/language/list/en.ts +2 -0
  57. package/server/erudit/language/list/ru.ts +2 -0
  58. package/server/erudit/news/build.ts +85 -48
  59. package/server/erudit/sponsors/build.ts +77 -26
  60. package/shared/types/contentConnections.ts +2 -2
  61. package/shared/types/elementSnippet.ts +9 -3
  62. package/shared/types/language.ts +2 -0
package/app/app.vue CHANGED
@@ -22,8 +22,7 @@ initAsideMajorPaneWatcher();
22
22
  class="relative m-auto min-h-dvh max-w-(--w-max-content) overflow-clip
23
23
  border-x
24
24
  border-[light-dark(var(--color-neutral-300),var(--color-neutral-800))]
25
- shadow-[0_0_3px_0px_light-dark(rgba(0,0,0,0.05),rgba(0,0,0,0.18))]
26
- transition-[border]"
25
+ shadow-[0_0_3px_0px_light-dark(rgba(0,0,0,0.05),rgba(0,0,0,0.18))]"
27
26
  >
28
27
  <SiteMain>
29
28
  <NuxtPage></NuxtPage>
@@ -9,7 +9,6 @@ const { color = 'var(--color-brand)' } = defineProps<{
9
9
  <span
10
10
  :style="{ '--titleColor': color }"
11
11
  class="text-text-deep leading-tight font-extrabold
12
- transition-[color,text-shadow]
13
12
  text-shadow-[2px_2px_color-mix(in_srgb,var(--titleColor),transparent_65%)]"
14
13
  >
15
14
  {{ formatText(text) }}
@@ -31,8 +31,7 @@ defineProps<{
31
31
  `p-small micro:p-normal not-last:mb-normal gap-small text-main-sm
32
32
  relative flex w-full break-inside-avoid-column flex-col items-center
33
33
  rounded border border-(--colorBorder) bg-linear-to-tr
34
- from-(--colorBg)/30 to-(--colorBg)
35
- transition-[background,border,box-shadow]`,
34
+ from-(--colorBg)/30 to-(--colorBg) transition-shadow`,
36
35
  link && 'hocus:ring-(--colorBorder) ring-2 ring-transparent',
37
36
  ]"
38
37
  >
@@ -33,8 +33,7 @@ onMounted(() => {
33
33
  <template>
34
34
  <section
35
35
  v-if="adsData"
36
- class="p-small border-border border-t transition-[border]
37
- [@media(max-height:500px)]:hidden"
36
+ class="p-small border-border border-t [@media(max-height:500px)]:hidden"
38
37
  >
39
38
  <div
40
39
  class="relative max-h-[300px] overflow-hidden
@@ -11,8 +11,8 @@ const phrase = await usePhrases('ads_replacer');
11
11
  h-full w-full flex-row items-center justify-center
12
12
  bg-neutral-200
13
13
  shadow-[inset_0px_0px_5px_0px_light-dark(rgba(0,0,0,0.12),rgba(255,255,255,0.08))]
14
- transition-[background,color] @max-[550px]:flex-col
15
- @max-[550px]:text-center dark:bg-neutral-800"
14
+ @max-[550px]:flex-col @max-[550px]:text-center
15
+ dark:bg-neutral-800"
16
16
  >
17
17
  <MyIcon
18
18
  name="handshake"
@@ -19,7 +19,7 @@ const isExternalLink = computed(() => {
19
19
  <EruditLink
20
20
  :class="[
21
21
  `border-border block min-h-(--h-min-aside-item) border-b
22
- bg-transparent text-sm transition-[background,color,border]`,
22
+ bg-transparent text-sm transition-[background,color]`,
23
23
  hoverable && 'hocus:bg-bg-accent cursor-pointer',
24
24
  active
25
25
  ? 'text-brand'
@@ -41,17 +41,27 @@ if (import.meta.client) {
41
41
  `micro:[--_switchSize:110px] [--_switchGap:var(--spacing-main)]
42
42
  [--_switchSize:80px]`,
43
43
  /* */
44
- `absolute bottom-0 flex size-(--_switchSize) items-center
44
+ `fixed bottom-0 flex size-(--_switchSize) items-center
45
45
  justify-center pb-(--_switchGap) pl-(--_switchGap)
46
46
  transition-[left,right,opacity]`,
47
47
  {
48
- '-scale-x-100': isMinor,
49
- [`aside1:right-0 aside1:opacity-0 -right-(--_switchSize)
50
- opacity-100`]: canShowSwitches && isMajor,
51
- [`aside2:left-0 aside2:opacity-0 -left-(--_switchSize)
52
- opacity-100`]: canShowSwitches && isMinor,
53
- 'right-0 opacity-0': !canShowSwitches && isMajor,
54
- 'left-0 opacity-0': !canShowSwitches && isMinor,
48
+ //
49
+ // Major Global
50
+ //
51
+ 'aside1:opacity-0': isMajor,
52
+ // Major Can Show
53
+ 'left-0 opacity-100': isMajor && canShowSwitches,
54
+ // Major Cannot Show
55
+ '-left-(--_switchSize) opacity-0': isMajor && !canShowSwitches,
56
+
57
+ //
58
+ // Minor Global
59
+ //
60
+ 'aside2:opacity-0 -scale-x-100': isMinor,
61
+ // Minor Can Show
62
+ 'right-0 opacity-100': isMinor && canShowSwitches,
63
+ // Minor Cannot Show
64
+ '-right-(--_switchSize) opacity-0': isMinor && !canShowSwitches,
55
65
  },
56
66
  ]"
57
67
  >
@@ -22,7 +22,7 @@ const phrase = await usePhrases(
22
22
  </script>
23
23
 
24
24
  <template>
25
- <section class="border-border z-1 border-b transition-[border]">
25
+ <section class="border-border z-1 border-b">
26
26
  <div
27
27
  style="--_button-size: 56px; --_underline-p: 6px"
28
28
  class="relative mx-auto flex w-max"
@@ -6,15 +6,14 @@ defineProps<{ icon: MyIconName; title: string; count?: number }>();
6
6
  <template>
7
7
  <section
8
8
  class="gap-normal p-normal border-border bg-bg-aside flex items-center
9
- justify-center border-b transition-[border,background]"
9
+ justify-center border-b"
10
10
  >
11
11
  <MyIcon :name="icon" class="text-text-muted text-[1.3em]" />
12
12
  <span class="font-semibold">{{ formatText(title) }}</span>
13
13
  <span
14
14
  v-if="count"
15
15
  class="text-invert rounded-full bg-neutral-600 px-[8px] py-[2.5px]
16
- text-xs font-semibold transition-[color,background]
17
- dark:bg-neutral-300"
16
+ text-xs font-semibold dark:bg-neutral-300"
18
17
  >{{ count }}</span
19
18
  >
20
19
  </section>
@@ -40,10 +40,7 @@ const contentRelativePath = computed(() => {
40
40
  <template>
41
41
  <AsideMinorPane>
42
42
  <div class="flex h-full w-full flex-col">
43
- <div
44
- class="border-border gap-normal flex justify-center border-b
45
- transition-[border]"
46
- >
43
+ <div class="border-border gap-normal flex justify-center border-b">
47
44
  <TopicPartButton
48
45
  v-for="part of topicParts"
49
46
  :part
@@ -37,8 +37,7 @@ const phrase = await usePhrases('contribution');
37
37
  class="absolute top-0 left-0 z-10 h-full w-full"
38
38
  >
39
39
  <div
40
- class="bg-bg-aside flex h-full w-full flex-col justify-end
41
- transition-[background]"
40
+ class="bg-bg-aside flex h-full w-full flex-col justify-end"
42
41
  >
43
42
  <div
44
43
  class="nice-scrollbars *:border-border overflow-auto
@@ -53,7 +52,7 @@ const phrase = await usePhrases('contribution');
53
52
  </div>
54
53
  <div
55
54
  class="border-border flex items-center border-t py-1
56
- pr-0 transition-[border]"
55
+ pr-0"
57
56
  >
58
57
  <div
59
58
  class="pl-normal gap-small flex flex-1 items-center
@@ -63,7 +62,6 @@ const phrase = await usePhrases('contribution');
63
62
  <span
64
63
  class="text-invert rounded-full bg-neutral-600
65
64
  px-[8px] py-[2.5px] text-xs font-semibold
66
- transition-[color,background]
67
65
  dark:bg-neutral-300"
68
66
  >{{ contributions.length }}</span
69
67
  >
@@ -55,8 +55,7 @@ const phrase = await usePhrases(
55
55
  class="absolute top-0 left-0 z-10 h-full w-full"
56
56
  >
57
57
  <div
58
- class="bg-bg-aside flex h-full w-full flex-col justify-end
59
- transition-[background]"
58
+ class="bg-bg-aside flex h-full w-full flex-col justify-end"
60
59
  >
61
60
  <div
62
61
  class="nice-scrollbars overflow-auto *:border-t
@@ -83,7 +82,7 @@ const phrase = await usePhrases(
83
82
  </div>
84
83
  <div
85
84
  class="border-border flex items-center border-t py-1
86
- pr-0 transition-[border]"
85
+ pr-0"
87
86
  >
88
87
  <div class="pl-normal flex-1 text-sm font-semibold">
89
88
  {{ formatText(phrase.improve_material) }}
@@ -8,7 +8,7 @@ defineProps<{ contribution: ContentContribution }>();
8
8
  <EruditLink
9
9
  :to="PAGES.contributor(contribution.contributorId)"
10
10
  class="p-normal hocus:bg-bg-accent block bg-transparent
11
- transition-[border,background]"
11
+ transition-[background]"
12
12
  >
13
13
  <div class="gap-normal flex items-center">
14
14
  <SmartMedia
@@ -51,27 +51,41 @@ onMounted(() => {
51
51
  </script>
52
52
 
53
53
  <template>
54
- <TreeItem
55
- :level
56
- :icon="elementIcon"
57
- :main="formatText(item.title)"
58
- :state="active ? 'active' : undefined"
59
- :to="'#' + item.elementId"
60
- />
61
- <div v-if="item.type === 'heading'" class="relative overflow-hidden">
62
- <div
63
- :style="{ '--level': level ? +level : 0 }"
64
- :class="[
65
- `absolute top-0
66
- left-[calc(var(--spacing-normal)*(var(--level)+1)+8px)] h-full
67
- border-l opacity-40 transition-[border]`,
68
- [active ? 'border-brand' : 'border-text-dimmed'],
69
- ]"
70
- ></div>
71
- <TocItem
72
- v-for="child of item.children"
73
- :item="child"
74
- :level="item.level"
54
+ <div :class="[$style.item, active && $style.activeItem]">
55
+ <TreeItem
56
+ :level
57
+ :icon="elementIcon"
58
+ :main="formatText(item.title)"
59
+ :state="active ? 'active' : undefined"
60
+ :to="'#' + item.elementId"
75
61
  />
62
+ <div
63
+ v-if="item.type === 'heading'"
64
+ class="group/children relative overflow-hidden"
65
+ >
66
+ <div
67
+ :style="{ '--level': level ? +level : 0 }"
68
+ :class="[
69
+ `border-text-dimmed absolute top-0
70
+ left-[calc(var(--spacing-normal)*(var(--level)+1)+6.5px)]
71
+ h-full border-l opacity-40 transition-[border]`,
72
+ $style.leftLine,
73
+ ]"
74
+ ></div>
75
+ <TocItem
76
+ v-for="child of item.children"
77
+ :item="child"
78
+ :level="item.level"
79
+ />
80
+ </div>
76
81
  </div>
77
82
  </template>
83
+
84
+ <style module>
85
+ .item.activeItem,
86
+ .item:has(.activeItem) {
87
+ .leftLine {
88
+ border-color: var(--color-brand);
89
+ }
90
+ }
91
+ </style>
@@ -120,8 +120,7 @@ const phrase = await usePhrases('news', 'no_news', 'show_more');
120
120
  class="my-big px-normal py-small bg-bg-accent
121
121
  hocus:border-text-disabled gap-normal m-auto flex
122
122
  w-auto cursor-pointer items-center rounded border-2
123
- border-transparent text-sm
124
- transition-[border,background,color]"
123
+ border-transparent text-sm transition-[border]"
125
124
  >
126
125
  <MyRuntimeIcon v-if="newsLoading" :svg="loadingSvg" />
127
126
  <span>{{ phrase.show_more }}</span>
@@ -37,9 +37,9 @@ await walkElements(item.content.proseElement, async (element) => {
37
37
  </script>
38
38
 
39
39
  <template>
40
- <div class="p-normal border-border border-b text-sm transition-[border]">
40
+ <div class="p-normal border-border border-b text-sm">
41
41
  <div
42
- class="mb-small gap-small flex items-center transition-[color]"
42
+ class="mb-small gap-small flex items-center"
43
43
  :class="
44
44
  isNew
45
45
  ? 'font-semibold text-orange-700 dark:text-orange-300'
@@ -51,7 +51,7 @@ function linkClick() {
51
51
  -mt-(--tGap) -mb-(--bGap) rounded-sm bg-transparent px-(--xGap)
52
52
  pt-(--tGap) pb-(--bGap) text-(--linkColor) underline
53
53
  decoration-[color-mix(in_srgb,var(--linkColor)30%,transparent)]
54
- decoration-2 underline-offset-2 transition-[color,background]"
54
+ decoration-2 underline-offset-2 transition-[background]"
55
55
  >
56
56
  {{ formatText(element.data.label) }}
57
57
  </EruditLink>
@@ -8,7 +8,7 @@ const hasExtra = child.stats || child.quickLinks;
8
8
  <div
9
9
  class="border-border hocus:ring-brand/25 hocus:border-brand group
10
10
  dark:bg-bg-aside rounded border bg-neutral-100 ring-2
11
- ring-transparent transition-[border,box-shadow,background]"
11
+ ring-transparent transition-[border,box-shadow]"
12
12
  >
13
13
  <EruditLink :to="child.link" class="p-normal gap-small flex flex-col">
14
14
  <div class="gap-small micro:gap-normal flex items-center">
@@ -31,8 +31,7 @@ const hasExtra = child.stats || child.quickLinks;
31
31
  </EruditLink>
32
32
  <div
33
33
  v-if="hasExtra"
34
- class="border-t-border p-normal gap-normal flex flex-col border-t
35
- transition-[border]"
34
+ class="border-t-border p-normal gap-normal flex flex-col border-t"
36
35
  >
37
36
  <div v-if="child.quickLinks" class="relative top-[1px]">
38
37
  <MainQuickLinks
@@ -6,7 +6,7 @@ defineProps<{ description: string | undefined }>();
6
6
  <section
7
7
  v-if="description"
8
8
  class="text-main-lg max-micro:text-center px-main py-main-half
9
- font-semibold transition-[color]"
9
+ font-semibold"
10
10
  >
11
11
  {{ formatText(description) }}
12
12
  </section>
@@ -40,6 +40,22 @@ onUnmounted(() => {
40
40
  });
41
41
 
42
42
  const elementIcon = await getElementIcon(quickLink.schemaName);
43
+
44
+ const title = computed(() => {
45
+ if (quickLink.quick?.title) {
46
+ return quickLink.quick.title;
47
+ } else {
48
+ return quickLink.title;
49
+ }
50
+ });
51
+
52
+ const description = computed(() => {
53
+ if (quickLink.quick?.description) {
54
+ return quickLink.quick.description;
55
+ } else {
56
+ return quickLink.description;
57
+ }
58
+ });
43
59
  </script>
44
60
 
45
61
  <template>
@@ -50,15 +66,14 @@ const elementIcon = await getElementIcon(quickLink.schemaName);
50
66
  class="gap-small border-border px-small text-text-muted text-main-sm
51
67
  hocus:text-brand hocus:border-brand hocus:ring-brand/25 flex
52
68
  items-center rounded border bg-(--quickBg) py-1 ring-2
53
- ring-transparent
54
- transition-[background,color,border,box-shadow]"
69
+ ring-transparent transition-[color,border,box-shadow]"
55
70
  >
56
71
  <MaybeMyIcon :name="elementIcon" class="-mr-0.5 text-[1.2em]" />
57
- <span>{{ formatText(quickLink.title) }}</span>
72
+ <span>{{ formatText(title) }}</span>
58
73
  </EruditLink>
59
74
  <TransitionFade>
60
75
  <div
61
- v-if="quickLink.description && popupVisible"
76
+ v-if="description && popupVisible"
62
77
  :style="floatingStyles"
63
78
  ref="popup"
64
79
  class="z-10 max-w-[300px] p-2"
@@ -68,7 +83,7 @@ const elementIcon = await getElementIcon(quickLink.schemaName);
68
83
  text-white shadow-lg/30 dark:bg-neutral-400
69
84
  dark:text-black dark:shadow-neutral-500"
70
85
  >
71
- {{ formatText(quickLink.description) }}
86
+ {{ formatText(description) }}
72
87
  </div>
73
88
  </div>
74
89
  </TransitionFade>
@@ -9,9 +9,7 @@ const quickLinks = (() => {
9
9
  return;
10
10
  }
11
11
 
12
- const filtered = elementSnippets.filter(
13
- (snippet) => snippet.quick === true,
14
- );
12
+ const filtered = elementSnippets.filter((snippet) => !!snippet.quick);
15
13
 
16
14
  return filtered.length > 0 ? filtered : undefined;
17
15
  })();
@@ -74,28 +74,25 @@ const phrase = await usePhrases('add_quote', 'next_quote');
74
74
  :url="avatarUrl"
75
75
  class="border-bg-main micro:size-[60px] size-[40px]
76
76
  rounded-full border-2
77
- [box-shadow:0_0_16px_0_var(--color)] transition-[border]
77
+ [box-shadow:0_0_16px_0_var(--color)]
78
78
  [--mediaColor:var(--color)]"
79
79
  />
80
80
  </EruditLink>
81
81
  </div>
82
82
  <div
83
83
  class="p-small micro:p-normal relative flex-1 rounded
84
- rounded-tl-none border-2 border-(--colorBorder) bg-(--colorBg)
85
- transition-[background,border]"
84
+ rounded-tl-none border-2 border-(--colorBorder) bg-(--colorBg)"
86
85
  >
87
86
  <div
88
87
  class="micro:[--arrowSize:22px] absolute -top-[2px]
89
88
  -left-(--arrowSize) h-(--arrowSize) w-(--arrowSize)
90
- bg-(--colorBorder) transition-[background]
91
- [--arrowSize:14px]
89
+ bg-(--colorBorder) [--arrowSize:14px]
92
90
  [--arrowSizeSmall:calc(var(--arrowSize)-2px)]
93
91
  [clip-path:polygon(110%_0,110%_110%,0_0)]"
94
92
  >
95
93
  <div
96
94
  class="absolute top-[2px] -right-[3px] h-(--arrowSizeSmall)
97
95
  w-(--arrowSizeSmall) bg-(--colorBg)
98
- transition-[background]
99
96
  [clip-path:polygon(100%_0,100%_100%,0_0)]"
100
97
  ></div>
101
98
  </div>
@@ -1,27 +1,12 @@
1
1
  <template>
2
- <section :class="[$style.section, 'bg-bg-main transition-[background]']">
2
+ <section :class="[$style.section, 'bg-bg-main']">
3
3
  <!-- Section Header -->
4
- <div
5
- :class="[
6
- $style.header,
7
- 'border-border relative border-b-2 transition-[border]',
8
- ]"
9
- >
4
+ <div :class="[$style.header, 'border-border relative border-b-2']">
10
5
  <!-- Header Shade -->
11
- <div class="absolute bottom-0 h-[70px] w-full">
12
- <!-- Light mode gradient -->
13
- <div
14
- class="absolute inset-0 bg-linear-to-t from-[#f7f7f7]
15
- to-transparent opacity-100 transition-opacity
16
- dark:opacity-0"
17
- ></div>
18
- <!-- Dark mode gradient -->
19
- <div
20
- class="absolute inset-0 bg-linear-to-t from-[#1b1b1b]
21
- to-transparent opacity-0 transition-opacity
22
- dark:opacity-100"
23
- ></div>
24
- </div>
6
+ <div
7
+ class="absolute bottom-0 h-[70px] w-full bg-linear-to-t
8
+ from-[#f7f7f7] to-transparent dark:from-[#1b1b1b]"
9
+ ></div>
25
10
 
26
11
  <!-- Header Content -->
27
12
  <div :class="[$style.headerContent, 'relative z-1']">
@@ -22,8 +22,7 @@ const { color = 'var(--color-brand)' } = defineProps<{
22
22
  <MaybeMyIcon
23
23
  :name="icon"
24
24
  class="max-micro:text-white micro:text-[38px] text-[30px]
25
- text-[color-mix(in_srgb,var(--titleColor),var(--color-text)_70%)]
26
- transition-[color]"
25
+ text-[color-mix(in_srgb,var(--titleColor),var(--color-text)_70%)]"
27
26
  />
28
27
  </div>
29
28
  <h1 class="text-size-h1 max-micro:text-center">
@@ -45,7 +45,7 @@ const data: Record<TopicPart, TopicPartSwitchData> = {
45
45
  `micro:[--switchHeight:50px] micro:gap-small px-small
46
46
  micro:px-normal border-border relative -bottom-[2px] flex
47
47
  h-(--switchHeight) items-center gap-1 rounded rounded-b-none
48
- border-2 transition-[border,color,background]`,
48
+ border-2 transition-[color]`,
49
49
  partData.state === 'missing' &&
50
50
  'text-text-disabled/75 border-b-border cursor-not-allowed',
51
51
  partData.state === 'active' &&
@@ -5,7 +5,7 @@ defineProps<{ deps: ContentDep[] }>();
5
5
  </script>
6
6
 
7
7
  <template>
8
- <ScrollPane>
8
+ <ScrollPane class="py-normal gap-main-half flex max-h-[500px] flex-col">
9
9
  <div v-for="dep of deps" class="gap-small flex">
10
10
  <MyIcon
11
11
  :name="ICONS[dep.contentType]"
@@ -1,44 +1,102 @@
1
1
  <script setup lang="ts">
2
- import type { ContentExternal } from '@erudit-js/core/content/externals';
2
+ import type { ContentExternals } from '@erudit-js/core/content/externals';
3
3
  import ScrollPane from './ScrollPane.vue';
4
4
 
5
- defineProps<{ externals: ContentExternal[] }>();
5
+ defineProps<{ externals: ContentExternals }>();
6
+
7
+ const phrase = await usePhrases('externals_own', 'externals_from');
6
8
  </script>
7
9
 
8
10
  <template>
9
- <ScrollPane>
10
- <div v-for="external of externals" class="gap-small flex">
11
- <MyIcon
12
- :name="external.type === 'physical' ? 'book' : 'globe'"
13
- class="relative top-1 shrink-0"
14
- />
15
- <div class="flex flex-col gap-0.5">
16
- <div>
17
- <template v-if="external.type === 'web'">
18
- <EruditLink
19
- external
20
- target="_blank"
21
- :to="external.link"
22
- :class="[external.link && 'text-hover-underline']"
11
+ <ScrollPane class="max-h-[800px]">
12
+ <div v-for="group of externals">
13
+ <div
14
+ :class="[
15
+ `border-border gap-small pl-small py-small bg-bg-main
16
+ relative sticky top-0 z-10 flex w-full items-center border-b
17
+ border-dashed`,
18
+ group.type === 'own'
19
+ ? 'font-semibold text-amber-600 dark:text-amber-400'
20
+ : 'text-text-muted',
21
+ ]"
22
+ >
23
+ <div
24
+ :class="[
25
+ `absolute top-0 left-0 h-full w-full bg-linear-to-t
26
+ to-transparent`,
27
+ group.type === 'own'
28
+ ? 'from-amber-400/10'
29
+ : 'from-bg-aside/50',
30
+ ]"
31
+ ></div>
32
+ <MyIcon
33
+ :name="
34
+ group.type === 'own'
35
+ ? 'arrow/left'
36
+ : 'arrow/up-to-right'
37
+ "
38
+ :class="[
39
+ 'relative',
40
+ group.type === 'own' && '-scale-x-100',
41
+ ]"
42
+ />
43
+ <span>
44
+ {{
45
+ group.type === 'own'
46
+ ? phrase.externals_own
47
+ : phrase.externals_from
48
+ }}
49
+ </span>
50
+ <span
51
+ v-if="group.type === 'parent'"
52
+ class="relative font-semibold"
53
+ >
54
+ {{ formatText(`"${group.title}"`) }}
55
+ </span>
56
+ </div>
57
+ <div>
58
+ <div
59
+ v-for="external of group.items"
60
+ class="gap-small py-normal flex"
61
+ >
62
+ <MyIcon
63
+ :name="external.type === 'physical' ? 'book' : 'globe'"
64
+ class="relative top-1 shrink-0"
65
+ />
66
+ <div class="flex flex-col gap-0.5">
67
+ <div>
68
+ <template v-if="external.link">
69
+ <EruditLink
70
+ external
71
+ target="_blank"
72
+ :to="external.link"
73
+ :class="[
74
+ external.link && 'text-hover-underline',
75
+ ]"
76
+ >
77
+ {{ formatText(external.title) }}
78
+ <MyIcon
79
+ v-if="external.link"
80
+ name="arrow/outward"
81
+ class="text-text-disabled text-main-sm
82
+ relative -top-1 inline"
83
+ />
84
+ </EruditLink>
85
+ </template>
86
+ <template v-else>
87
+ <span>{{ formatText(external.title) }}</span>
88
+ </template>
89
+ </div>
90
+ <div
91
+ v-if="external.info"
92
+ class="text-text-muted text-main-sm"
23
93
  >
24
- {{ formatText(external.title) }}
25
- <MyIcon
26
- v-if="external.link"
27
- name="arrow/outward"
28
- class="text-text-disabled text-main-sm relative
29
- -top-1 inline"
30
- />
31
- </EruditLink>
32
- </template>
33
- <template v-else>
34
- <span>{{ formatText(external.title) }}</span>
35
- </template>
36
- </div>
37
- <div v-if="external.info" class="text-text-muted text-main-sm">
38
- {{ formatText(external.info) }}
39
- </div>
40
- <div class="text-text-muted text-main-sm italic">
41
- {{ formatText(external.reason) }}
94
+ {{ formatText(external.info) }}
95
+ </div>
96
+ <div class="text-text-muted text-main-sm italic">
97
+ {{ formatText(external.reason) }}
98
+ </div>
99
+ </div>
42
100
  </div>
43
101
  </div>
44
102
  </div>