big ui refractor

This commit is contained in:
Rene Kievits
2025-12-23 02:23:44 +01:00
parent 4428a2b7be
commit eaed23a678
62 changed files with 2662 additions and 815 deletions

View File

@@ -1,5 +1,5 @@
<template lang="pug">
v-card.pa-4.widget-card.rounded-xl.border-subtle.d-flex.flex-column(
v-card.pa-4.widget-card.rounded-xl.d-flex.flex-column(
color="#1e1e24"
flat
)
@@ -38,5 +38,3 @@ const props = defineProps({
// eslint-disable-next-line no-unused-vars
const hasHeader = computed(() => !!props.title || !!props.icon || !!props.subtitle);
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -35,5 +35,3 @@ const accuracyPercent = computed(() => {
return Math.round((props.accuracy.correct / props.accuracy.total) * 100);
});
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -59,5 +59,3 @@ const toRoman = (num) => {
return lookup[num] || num;
};
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -29,5 +29,3 @@ const props = defineProps({
const hasUpcoming = computed(() => props.forecast && props.forecast.some((c) => c > 0));
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -35,5 +35,3 @@ const props = defineProps({
ghosts: { type: Array, default: () => [] },
});
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -39,5 +39,3 @@ const masteryPercent = computed(
() => (totalItems.value === 0 ? 0 : Math.round((masteredCount.value / totalItems.value) * 100)),
);
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -78,5 +78,3 @@ const getHeatmapClass = (count) => {
const formatDate = (dateStr) => new Date(dateStr).toLocaleDateString(locale.value, { month: 'short', day: 'numeric' });
const isToday = (dateStr) => dateStr === new Date().toISOString().split('T')[0];
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -58,5 +58,3 @@ const getDayLabel = (dateStr) => {
return new Date(dateStr).toLocaleDateString(locale.value, { weekday: 'short' });
};
</script>
<style lang="scss" src="@/styles/components/_widgets.scss" scoped></style>

View File

@@ -1,53 +1,72 @@
<template lang="pug">
.grid-area-full.text-center.mb-4
.text-h2.font-weight-bold.mb-2 {{ $t('hero.welcome') }}
.text-h5.text-grey.mb-8 {{ $t('hero.subtitle') }}
.grid-area-full.text-center.mb-8.zen-wrapper
.text-h3.font-weight-bold.mb-2.text-white.letter-spacing-small {{ $t('hero.welcome') }}
.text-subtitle-1.text-grey-lighten-1.mb-10(
style="max-width: 600px; margin-inline: auto;"
) {{ $t('hero.subtitle') }}
.d-flex.justify-center.align-center.flex-column.gap-4
v-btn.text-h5.font-weight-bold.text-black.glow-btn.welcome-btn(
.d-flex.justify-center.align-stretch.flex-wrap.ga-6.mb-8
v-btn.zen-btn.lesson-btn(
v-if="lessonCount > 0"
to="/lesson"
rounded="xl"
color="purple-accent-2"
class="mb-3"
variant="tonal"
size="x-large"
height="80"
)
v-icon(size="32" start) mdi-school
| {{ $t('hero.lessons') }}
v-chip.ml-3.font-weight-bold(
color="#1e1e24"
variant="flat"
size="default"
style="color: white !important;"
) {{ lessonCount }}
v-btn.text-h5.font-weight-bold.text-black.glow-btn.welcome-btn(
.d-flex.align-center.w-100.px-2.zen-btn-content
v-icon.mr-5(size="36" icon="mdi-school-outline")
.d-flex.flex-column.align-start.mr-auto
.text-purple-lighten-1.text-h6.font-weight-bold.line-height-tight
| {{ $t('hero.lessons') }}
.text-purple-accent-1.text-caption.font-weight-regular.opacity-80
| {{ $t('hero.newContent') }}
.zen-counter.bg-purple-accent-2.text-black.ml-4
| {{ lessonCount }}
v-btn.zen-btn.review-btn(
@click="$emit('start', 'shuffled')"
rounded="xl"
color="#00cec9"
color="cyan-accent-2"
variant="tonal"
size="x-large"
height="80"
:disabled="queueLength === 0"
:class="{ 'zen-disabled': queueLength === 0 }"
)
v-icon(size="32" start) mdi-brush
| {{ queueLength > 0 ? $t('hero.start') : $t('hero.noReviews') }}
.d-flex.align-center.w-100.px-2.zen-btn-content
v-icon.mr-5(size="36" icon="mdi-brush-variant")
v-chip.ml-3.font-weight-bold(
v-if="queueLength > 0"
color="#1e1e24"
variant="flat"
size="default"
style="color: white !important;"
) {{ queueLength }}
.d-flex.flex-column.align-start.mr-auto
.text-cyan-lighten-1.text-h6.font-weight-bold.line-height-tight
| {{ queueLength > 0 ? $t('hero.start') : $t('hero.noReviews') }}
.text-cyan-accent-1.text-caption.font-weight-regular.opacity-80
| {{ queueLength > 0 ? $t('hero.readyToReview') : $t('hero.allCaughtUp') }}
v-fade-transition
.zen-counter.bg-cyan-accent-2.text-black.ml-4(v-if="queueLength > 0")
| {{ queueLength }}
.d-flex.justify-center.align-center.flex-column.gap-1
v-fade-transition
v-btn.mt-4.text-caption.font-weight-bold(
v-btn.text-caption.font-weight-bold.text-uppercase.letter-spacing-1.text-grey(
v-if="hasLowerLevels"
@click="$emit('start', 'priority')"
variant="plain"
color="grey-lighten-1"
variant="text"
prepend-icon="mdi-sort-ascending"
:ripple="false"
) {{ $t('hero.prioritize', { count: lowerLevelCount }) }}
.text-caption.text-grey.mt-2(v-if="queueLength === 0")
| {{ $t('hero.nextIn') }} {{ nextReviewTime }}
v-fade-transition
.text-caption.text-grey-darken-1.font-weight-medium.d-flex.align-center.mt-2(
v-if="queueLength === 0 && !hasLowerLevels && nextReviewTime"
)
v-icon.mr-2(size="small" icon="mdi-clock-outline")
span {{ $t('hero.nextIn') }} {{ nextReviewTime }}
</template>
<script setup>
@@ -67,9 +86,11 @@ defineEmits(['start']);
const { t } = useI18n();
const nextReviewTime = computed(() => {
if (!props.forecast) return 'a while';
const idx = props.forecast.findIndex((c) => c > 0);
if (idx === -1) return 'a while';
return idx === 0 ? t('hero.now') : t('stats.inHours', { n: idx }, idx);
if (!props.forecast || Object.keys(props.forecast).length === 0) return null;
try {
const idx = props.forecast.findIndex((c) => c > 0);
if (idx === -1) return null;
return idx === 0 ? t('hero.now') : t('stats.inHours', { n: idx }, idx);
} catch (e) { return null; }
});
</script>

View File

@@ -121,6 +121,7 @@ watch(() => props.char, (newChar) => {
watch(() => props.autoHint, (shouldHint) => {
if (!controller) return;
controller.setAutoHint(shouldHint);
if (shouldHint) controller.showHint();
});

View File

@@ -58,6 +58,7 @@ const props = defineProps({
default: 'animate',
validator: (v) => ['hero', 'animate'].includes(v),
},
svgContent: { type: String, default: '' },
});
const strokes = ref([]);
@@ -72,10 +73,14 @@ async function loadData(char) {
const hex = char.charCodeAt(0).toString(16).padStart(5, '0');
try {
const baseUrl = 'https://raw.githubusercontent.com/KanjiVG/kanjivg/master/kanji';
const res = await fetch(`${baseUrl}/${hex}.svg`);
let txt = props.svgContent;
if (!txt) {
const baseUrl = 'https://raw.githubusercontent.com/KanjiVG/kanjivg/master/kanji';
const res = await fetch(`${baseUrl}/${hex}.svg`);
txt = await res.text();
}
const txt = await res.text();
const parser = new DOMParser();
const doc = parser.parseFromString(txt, 'image/svg+xml');
const rawPaths = Array.from(doc.getElementsByTagName('path')).map((p) => p.getAttribute('d'));
@@ -147,5 +152,3 @@ defineExpose({ playAnimation });
onMounted(() => { if (props.char) loadData(props.char); });
watch(() => props.char, (n) => { if (n) loadData(n); });
</script>
<style lang="scss" src="@/styles/components/_kanji.scss" scoped></style>