mirror of
https://github.com/jellyfin/jellyfin-vue.git
synced 2024-11-23 14:09:51 +00:00
feat(ui): switch to Radix and Inter, cleanups (#2250)
* Inter is going to be one of the brand Jellyfin fonts, as discussed in Matrix's UI/UX channels. Check this: https://matrix.to/#/!xrSDQsdjElWFYUAMoG:matrix.org/$_ZCxjEgHmaYdFo6aiCfqdXSnhEvg8UAksk0NG5PUhZg?via=bonifacelabs.ca&via=t2bot.io&via=matrix.org (Previous messages are also relevant) * Add radix-vue to use their components as base. There is only one modification I would like to have to their components (the ability to pass arbitrary props to Primitive) but it's something that's likely to be accepted upstream * Minor cleanup in Carousel styles * Use the client font as Jassub's font * Install UnoCSS and it's resets (not enabled yet due to Vuetify inconsistencies) Signed-off-by: Fernando Fernández <ferferga@hotmail.com>
This commit is contained in:
parent
75bca13018
commit
a379e25b01
@ -18,8 +18,8 @@
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fontsource/roboto": "5.0.8",
|
||||
"@jellyfin/sdk": "0.8.2",
|
||||
"@unocss/reset": "0.58.5",
|
||||
"@vueuse/components": "10.9.0",
|
||||
"@vueuse/core": "10.9.0",
|
||||
"audiomotion-analyzer": "4.4.0",
|
||||
@ -31,9 +31,11 @@
|
||||
"dompurify": "3.0.9",
|
||||
"fast-equals": "5.0.1",
|
||||
"hls.js": "1.5.7",
|
||||
"inter-ui": "4.0.2",
|
||||
"jassub": "1.7.15",
|
||||
"lodash-es": "4.17.21",
|
||||
"marked": "12.0.0",
|
||||
"radix-vue": "1.4.9",
|
||||
"sortablejs": "1.15.2",
|
||||
"swiper": "11.0.7",
|
||||
"uuid": "9.0.1",
|
||||
@ -80,6 +82,7 @@
|
||||
"sass": "1.71.1",
|
||||
"typescript": "5.3.3",
|
||||
"typescript-eslint-parser-for-extra-files": "0.6.0",
|
||||
"unocss": "0.58.5",
|
||||
"unplugin-icons": "0.18.5",
|
||||
"unplugin-vue-components": "0.26.0",
|
||||
"unplugin-vue-router": "0.8.4",
|
||||
|
@ -1,16 +1,3 @@
|
||||
.default-icon {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.swiperContainer {
|
||||
min-width: 100%;
|
||||
min-height: 100%;
|
||||
|
@ -1,6 +1,5 @@
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-family: "InterVariable", system-ui !important;
|
||||
cursor: var(--j-client-cursor);
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,10 @@
|
||||
class="elevation-2">
|
||||
<div
|
||||
class="absolute-cover card-content d-flex justify-center align-center">
|
||||
<JSlot class="card-image">
|
||||
<RSlot class="card-image">
|
||||
<slot
|
||||
name="image" />
|
||||
</JSlot>
|
||||
</RSlot>
|
||||
</div>
|
||||
<div
|
||||
class="absolute-cover card-overlay d-flex justify-center align-center"
|
||||
|
@ -11,7 +11,6 @@
|
||||
<div
|
||||
:class="useResponsiveClasses('slide-backdrop')"
|
||||
data-swiper-parallax="-100">
|
||||
<div class="default-icon" />
|
||||
<BlurhashImage
|
||||
:key="`${item.Id}-image`"
|
||||
:item="getRelatedItem(item)"
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<JSlot
|
||||
<RSlot
|
||||
@mouseenter="isHovering = true"
|
||||
@mouseleave="isHovering = false">
|
||||
<slot
|
||||
:is-hovering="isHovering" />
|
||||
</JSlot>
|
||||
</RSlot>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
|
||||
import type { Arrayable } from '@vueuse/core';
|
||||
import { cloneVNode, mergeProps, Fragment, type VNode } from 'vue';
|
||||
|
||||
/**
|
||||
* Render multiple child nodes from a slot, including nested fragments.
|
||||
*/
|
||||
function renderSlotFragments(children?: VNode[]): VNode[] {
|
||||
if (!children) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return children.flatMap((child) => {
|
||||
if (child.type === Fragment) {
|
||||
return renderSlotFragments(child.children as VNode[]);
|
||||
}
|
||||
|
||||
return [child];
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
inheritAttrs: false,
|
||||
setup(_, { attrs, slots }) {
|
||||
return (): Arrayable<VNode> | undefined => {
|
||||
|
||||
if (!slots.default) {
|
||||
return;
|
||||
}
|
||||
|
||||
const childrens = renderSlotFragments(slots.default());
|
||||
|
||||
const [firstChildren, ...otherChildren] = childrens;
|
||||
|
||||
if (Object.keys(attrs).length > 0) {
|
||||
// Remove props ref from being inferred
|
||||
delete firstChildren.props?.ref;
|
||||
|
||||
const mergedProps = mergeProps(attrs, firstChildren.props ?? {});
|
||||
|
||||
// Remove class to prevent duplicated
|
||||
if (attrs.class && firstChildren.props?.class) {
|
||||
delete firstChildren.props.class;
|
||||
}
|
||||
|
||||
const cloned = cloneVNode(firstChildren, mergedProps);
|
||||
|
||||
/*
|
||||
* Explicitly override props starting with `on`.
|
||||
* It seems cloneVNode from Vue doesn't like overriding `onXXX` props. So
|
||||
* we have to do it manually.
|
||||
*/
|
||||
|
||||
for (const prop in mergedProps) {
|
||||
if (prop.startsWith('on')) {
|
||||
cloned.props ||= {};
|
||||
cloned.props[prop] = mergedProps[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return childrens.length === 1 ? cloned : [cloned, ...otherChildren];
|
||||
}
|
||||
|
||||
return childrens;
|
||||
};
|
||||
}
|
||||
};
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
|
||||
</script>
|
@ -16,8 +16,16 @@ import { vuetify } from '@/plugins/vuetify';
|
||||
/**
|
||||
* - GLOBAL STYLES -
|
||||
*/
|
||||
import 'inter-ui/inter-variable.css';
|
||||
/**
|
||||
* TODO: Re-enable once Vuetify is gone
|
||||
*/
|
||||
/*
|
||||
* import 'uno.css';
|
||||
* import 'virtual:unocss-devtools';
|
||||
*/
|
||||
import '@unocss/reset/tailwind.css';
|
||||
import '@/assets/styles/global.scss';
|
||||
import '@fontsource/roboto';
|
||||
|
||||
/**
|
||||
* - VUE PLUGINS, STORE AND DIRECTIVE -
|
||||
|
@ -5,7 +5,6 @@
|
||||
* an agnostic way, regardless of where the media is being played (remotely or locally)
|
||||
*/
|
||||
import JASSUB from 'jassub';
|
||||
import jassubDefaultFont from 'jassub/dist/default.woff2?url';
|
||||
import jassubWorker from 'jassub/dist/jassub-worker.js?url';
|
||||
import jassubWasmUrl from 'jassub/dist/jassub-worker.wasm?url';
|
||||
import { nextTick, reactive, watch } from 'vue';
|
||||
@ -98,10 +97,12 @@ class PlayerElementStore {
|
||||
fonts: attachedFonts,
|
||||
workerUrl: jassubWorker,
|
||||
wasmUrl: jassubWasmUrl,
|
||||
availableFonts: { 'liberation sans': jassubDefaultFont },
|
||||
fallbackFont: 'InterVariable',
|
||||
// Both parameters needed for subs to work on iOS
|
||||
prescaleFactor: 0.8,
|
||||
onDemandRender: false
|
||||
onDemandRender: false,
|
||||
// OffscreenCanvas doesn't work perfectly on Workers: https://github.com/ThaUnknown/jassub/issues/33
|
||||
offscreenRender: false
|
||||
});
|
||||
} else if (jassub) {
|
||||
if (isArray(attachedFonts)) {
|
||||
|
2
frontend/types/global/components.d.ts
vendored
2
frontend/types/global/components.d.ts
vendored
@ -98,7 +98,6 @@ declare module 'vue' {
|
||||
ItemsCarouselTitle: typeof import('./../../src/components/Layout/Carousel/Item/ItemsCarouselTitle.vue')['default']
|
||||
JHover: typeof import('./../../src/components/lib/JHover.vue')['default']
|
||||
JImg: typeof import('./../../src/components/lib/JImg.vue')['default']
|
||||
JSlot: typeof import('./../../src/components/lib/JSlot.vue')['default']
|
||||
LikeButton: typeof import('./../../src/components/Buttons/LikeButton.vue')['default']
|
||||
LoadingIndicator: typeof import('./../../src/components/System/LoadingIndicator.vue')['default']
|
||||
LocaleSwitcher: typeof import('./../../src/components/System/LocaleSwitcher.vue')['default']
|
||||
@ -129,6 +128,7 @@ declare module 'vue' {
|
||||
RepeatButton: typeof import('./../../src/components/Buttons/Playback/RepeatButton.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
RSlot: typeof import('radix-vue')['Slot']
|
||||
ScrollToTopButton: typeof import('./../../src/components/Buttons/ScrollToTopButton.vue')['default']
|
||||
SearchField: typeof import('./../../src/components/Layout/AppBar/SearchField.vue')['default']
|
||||
SeasonTabs: typeof import('./../../src/components/Item/SeasonTabs.vue')['default']
|
||||
|
@ -12,6 +12,9 @@ import {
|
||||
VueUseDirectiveResolver
|
||||
} from 'unplugin-vue-components/resolvers';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import RadixVueResolver from 'radix-vue/resolver';
|
||||
import UnoCSS from 'unocss/vite';
|
||||
import { presetUno } from 'unocss';
|
||||
import VueRouter from 'unplugin-vue-router/vite';
|
||||
import { defineConfig, type UserConfig } from 'vite';
|
||||
import { entrypoints, localeFilesFolder, srcRoot } from './scripts/paths';
|
||||
@ -41,7 +44,10 @@ export default defineConfig(({ mode }): UserConfig => {
|
||||
IconsResolver(),
|
||||
VueUseComponentsResolver(),
|
||||
Vuetify3Resolver(),
|
||||
VueUseDirectiveResolver()
|
||||
VueUseDirectiveResolver(),
|
||||
RadixVueResolver({
|
||||
prefix: 'R'
|
||||
})
|
||||
]
|
||||
}),
|
||||
/**
|
||||
@ -57,6 +63,11 @@ export default defineConfig(({ mode }): UserConfig => {
|
||||
fullInstall: false,
|
||||
forceStringify: true,
|
||||
include: localeFilesFolder
|
||||
}),
|
||||
UnoCSS({
|
||||
presets: [
|
||||
presetUno()
|
||||
]
|
||||
})
|
||||
],
|
||||
build: {
|
||||
|
1178
package-lock.json
generated
1178
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user