Skip to content

Commit bf7c69d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into fix-a11y-extract-btn-component
2 parents 917d2d4 + 2acb298 commit bf7c69d

File tree

21 files changed

+3886
-76
lines changed

21 files changed

+3886
-76
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"i18n-ally.localesPaths": ["./i18n/locales"],
3-
"i18n-ally.keystyle": "nested"
3+
"i18n-ally.keystyle": "nested",
4+
"typescript.tsdk": "node_modules/typescript/lib"
45
}

app/assets/main.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
box-sizing: border-box;
66
}
77

8+
:root:not([data-theme='light']),
89
:root[data-theme='dark'] {
910
/* background colors */
1011
--bg: var(--bg-color, oklch(0.145 0 0));

app/components/Modal.client.vue

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const props = defineProps<{
55
66
const dialogRef = useTemplateRef('dialogRef')
77
8+
const emit = defineEmits<{
9+
(e: 'transitioned'): void
10+
}>()
11+
812
const modalTitleId = computed(() => {
913
const id = getCurrentInstance()?.attrs.id
1014
return id ? `${id}-title` : undefined
@@ -14,6 +18,20 @@ function handleModalClose() {
1418
dialogRef.value?.close()
1519
}
1620
21+
/**
22+
* Emits `transitioned` once the dialog has finished its open opacity transition.
23+
* This is used by consumers that need to run layout-sensitive logic (for example
24+
* dispatching a resize) only after the modal is fully displayed.
25+
*/
26+
function onDialogTransitionEnd(event: TransitionEvent) {
27+
const el = dialogRef.value
28+
if (!el) return
29+
if (!el.open) return
30+
if (event.target !== el) return
31+
if (event.propertyName !== 'opacity') return
32+
emit('transitioned')
33+
}
34+
1735
defineExpose({
1836
showModal: () => dialogRef.value?.showModal(),
1937
close: () => dialogRef.value?.close(),
@@ -28,6 +46,7 @@ defineExpose({
2846
class="w-full bg-bg border border-border rounded-lg shadow-xl max-h-[90vh] overflow-y-auto overscroll-contain m-0 m-auto p-6 text-fg focus-visible:outline focus-visible:outline-accent/70"
2947
:aria-labelledby="modalTitleId"
3048
v-bind="$attrs"
49+
@transitionend="onDialogTransitionEnd"
3150
>
3251
<!-- Modal top header section -->
3352
<div class="flex items-center justify-between mb-6">

app/components/Package/WeeklyDownloadStats.vue

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,27 @@ const props = defineProps<{
99
}>()
1010
1111
const chartModal = useModal('chart-modal')
12-
12+
const hasChartModalTransitioned = shallowRef(false)
1313
const isChartModalOpen = shallowRef(false)
14+
1415
async function openChartModal() {
1516
isChartModalOpen.value = true
17+
hasChartModalTransitioned.value = false
1618
// ensure the component renders before opening the dialog
1719
await nextTick()
1820
await nextTick()
1921
chartModal.open()
2022
}
2123
24+
function handleModalClose() {
25+
isChartModalOpen.value = false
26+
hasChartModalTransitioned.value = false
27+
}
28+
29+
function handleModalTransitioned() {
30+
hasChartModalTransitioned.value = true
31+
}
32+
2233
const { fetchPackageDownloadEvolution } = useCharts()
2334
2435
const { accentColors, selectedAccentColor } = useAccentColor()
@@ -249,16 +260,45 @@ const config = computed(() => {
249260
</CollapsibleSection>
250261
</div>
251262

252-
<PackageChartModal v-if="isChartModalOpen" @close="isChartModalOpen = false">
253-
<PackageDownloadAnalytics
254-
:weeklyDownloads="weeklyDownloads"
255-
:inModal="true"
256-
:packageName="props.packageName"
257-
:createdIso="createdIso"
263+
<PackageChartModal @close="handleModalClose" @transitioned="handleModalTransitioned">
264+
<!-- The Chart is mounted after the dialog has transitioned -->
265+
<!-- This avoids flaky behavior that hides the chart's minimap half of the time -->
266+
<Transition name="opacity" mode="out-in">
267+
<PackageDownloadAnalytics
268+
v-if="hasChartModalTransitioned"
269+
:weeklyDownloads="weeklyDownloads"
270+
:inModal="true"
271+
:packageName="props.packageName"
272+
:createdIso="createdIso"
273+
/>
274+
</Transition>
275+
276+
<!-- This placeholder bears the same dimensions as the PackageDownloadAnalytics component -->
277+
<!-- Avoids CLS when the dialog has transitioned -->
278+
<div
279+
v-if="!hasChartModalTransitioned"
280+
class="w-full aspect-[390/634.5] sm:aspect-[718/622.797]"
258281
/>
259282
</PackageChartModal>
260283
</template>
261284

285+
<style scoped>
286+
.opacity-enter-active,
287+
.opacity-leave-active {
288+
transition: opacity 200ms ease;
289+
}
290+
291+
.opacity-enter-from,
292+
.opacity-leave-to {
293+
opacity: 0;
294+
}
295+
296+
.opacity-enter-to,
297+
.opacity-leave-from {
298+
opacity: 1;
299+
}
300+
</style>
301+
262302
<style>
263303
/** Overrides */
264304
.vue-ui-sparkline-title span {

app/components/SearchSuggestionCard.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ defineProps<{
1414
<template>
1515
<BaseCard :isExactMatch="isExactMatch">
1616
<NuxtLink
17-
:to="type === 'user' ? `/~${name}` : `/@${name}`"
17+
:to="
18+
type === 'user'
19+
? { name: '~username', params: { username: name } }
20+
: { name: 'org', params: { org: name } }
21+
"
1822
:data-suggestion-index="index"
1923
class="flex items-center gap-4 focus-visible:outline-none after:content-[''] after:absolute after:inset-0"
2024
>

app/pages/index.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ defineOgImageComponent('Default', {
5656
</h1>
5757

5858
<p
59-
class="text-fg-muted text-lg sm:text-xl max-w-md mb-12 lg:mb-14 motion-safe:animate-slide-up motion-safe:animate-fill-both"
59+
class="text-fg-muted text-lg sm:text-xl max-w-xl mb-12 lg:mb-14 motion-safe:animate-slide-up motion-safe:animate-fill-both"
6060
style="animation-delay: 0.1s"
6161
>
6262
{{ $t('tagline') }}

app/utils/input.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function isEditableElement(target: EventTarget | null): boolean {
1919
*/
2020
export function isKeyWithoutModifiers(event: KeyboardEvent, key: string): boolean {
2121
return (
22-
event.key.toLowerCase() === key.toLowerCase() &&
22+
event.key?.toLowerCase() === key.toLowerCase() &&
2323
!event.altKey &&
2424
!event.ctrlKey &&
2525
!event.metaKey &&

app/utils/package-name.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import validatePackageName from 'validate-npm-package-name'
2+
import { encodePackageName } from '#shared/utils/npm'
23

34
/**
45
* Normalize a package name for comparison by removing common variations.
@@ -77,11 +78,7 @@ export async function checkPackageExists(
7778
options: Parameters<typeof $fetch>[1] = {},
7879
): Promise<boolean> {
7980
try {
80-
const encodedName = name.startsWith('@')
81-
? `@${encodeURIComponent(name.slice(1))}`
82-
: encodeURIComponent(name)
83-
84-
await $fetch(`${NPM_REGISTRY}/${encodedName}`, {
81+
await $fetch(`${NPM_REGISTRY}/${encodePackageName(name)}`, {
8582
...options,
8683
method: 'HEAD',
8784
})

config/i18n.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ const locales: (LocaleObjectData | (Omit<LocaleObjectData, 'code'> & { code: str
156156
file: 'hi-IN.json',
157157
name: 'हिंदी',
158158
},
159+
{
160+
code: 'te-IN',
161+
file: 'te-IN.json',
162+
name: 'తెలుగు',
163+
},
159164
{
160165
code: 'mr-IN',
161166
file: 'mr-IN.json',
@@ -322,6 +327,11 @@ const locales: (LocaleObjectData | (Omit<LocaleObjectData, 'code'> & { code: str
322327
file: 'cy.json',
323328
name: 'Cymraeg',
324329
},*/
330+
{
331+
code: 'no-NO',
332+
file: 'no-NO.json',
333+
name: 'Norsk',
334+
},
325335
]
326336

327337
const lunariaJSONFiles: Record<string, string> = {}

i18n/locales/de-DE.json

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
"seo": {
33
"home": {
44
"title": "npmx - Paket-Browser für die npm Registry",
5-
"description": "Ein besserer Browser für die npm Registry. Pakete suchen, durchstöbern und erkunden mit einer modernen Oberfläche."
5+
"description": "Ein schnellerer, modernerer Browser für die npm Registry. Pakete suchen, durchstöbern und erkunden mit einer modernen Oberfläche."
66
}
77
},
88
"version": "Version",
99
"built_at": "erstellt {0}",
1010
"alt_logo": "npmx Logo",
11-
"tagline": "ein besserer Browser für die npm Registry",
11+
"tagline": "ein schnellerer, modernerer Browser für die npm Registry",
1212
"non_affiliation_disclaimer": "nicht verbunden mit npm, Inc.",
1313
"trademark_disclaimer": "npm ist eine eingetragene Marke von npm, Inc. Diese Seite ist nicht mit npm, Inc. verbunden.",
1414
"footer": {
@@ -319,7 +319,7 @@
319319
},
320320
"playgrounds": {
321321
"title": "Ausprobieren",
322-
"choose": "Playground wählen"
322+
"choose": "Testumgebung wählen"
323323
},
324324
"metrics": {
325325
"esm": "ES-Module unterstützt",
@@ -450,26 +450,26 @@
450450
},
451451
"operations": {
452452
"queue": {
453-
"title": "Operations-Warteschlange",
453+
"title": "Aktions-Warteschlange",
454454
"clear_all": "Alle löschen",
455-
"refresh": "Operationen aktualisieren",
456-
"empty": "Keine Operationen in der Warteschlange",
457-
"empty_hint": "Füge Operationen von Paket- oder Org-Seiten hinzu",
458-
"active_label": "Aktive Operationen",
455+
"refresh": "Aktionen aktualisieren",
456+
"empty": "Keine Aktionen in der Warteschlange",
457+
"empty_hint": "Füge Aktionen von Paket- oder Org-Seiten hinzu",
458+
"active_label": "Aktive Aktionen",
459459
"otp_required": "OTP erforderlich",
460460
"otp_prompt": "OTP eingeben, um fortzufahren",
461461
"otp_placeholder": "OTP-Code eingeben...",
462462
"otp_label": "Einmalpasswort",
463463
"retry_otp": "Mit OTP wiederholen",
464464
"retrying": "Wird wiederholt...",
465-
"approve_operation": "Operation genehmigen",
466-
"remove_operation": "Operation entfernen",
465+
"approve_operation": "Aktion genehmigen",
466+
"remove_operation": "Aktion entfernen",
467467
"approve_all": "Alle genehmigen",
468468
"execute": "Ausführen",
469469
"executing": "Wird ausgeführt...",
470-
"log": "Log",
471-
"log_label": "Log abgeschlossener Operationen",
472-
"remove_from_log": "Aus Log entfernen"
470+
"log": "Protokoll",
471+
"log_label": "Protokoll abgeschlossener Aktionen",
472+
"remove_from_log": "Aus Protokoll entfernen"
473473
}
474474
},
475475
"org": {
@@ -764,14 +764,14 @@
764764
"title": "Was wir sind",
765765
"better_ux_dx": "Bessere UX/DX",
766766
"admin_ui": "Admin-UI",
767-
"description": "npmx ist eine {betterUxDx} für die npm-Paket-Registry und Tooling. Wir bieten eine schnelle, moderne Oberfläche zum Erkunden von Paketen mit Features wie Dark Mode, Tastaturnavigation, Code-Browsing und Verbindungen zu alternativen Registries wie {jsr}.",
767+
"description": "npmx ist eine {betterUxDx} für die npm-Paket-Registry und Tooling. Wir bieten eine schnelle, moderne Oberfläche zum Erkunden von Paketen mit Funktionen wie Dark Mode, Tastaturnavigation, Code-Browsing und Verbindungen zu alternativen Registries wie {jsr}.",
768768
"admin_description": "Wir wollen auch eine bessere {adminUi} zum Verwalten deiner Pakete, Teams und Organisationen bieten — alles im Browser, angetrieben von deiner lokalen npm CLI."
769769
},
770770
"what_we_are_not": {
771771
"title": "Was wir nicht sind",
772772
"not_package_manager": "Kein Paketmanager.",
773773
"not_registry": "Keine Registry.",
774-
"registry_description": "Wir hosten keine Pakete. Wir sind nur ein besserer Weg, sie zu durchstöbern.",
774+
"registry_description": "Wir hosten keine Pakete. Wir sind nur ein schnellerer, modernerer Weg, sie zu durchstöbern.",
775775
"package_managers_exist": "{already} {people} {building} {really} {cool} {package} {managers}.",
776776
"words": {
777777
"already": "Es gibt",

0 commit comments

Comments
 (0)