-
-
Notifications
You must be signed in to change notification settings - Fork 572
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: added a profile settings and settings nav (#432)
- Loading branch information
1 parent
c8a7e6e
commit 613c531
Showing
23 changed files
with
698 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
<script lang="ts" setup> | ||
import type { Boundaries } from 'vue-advanced-cropper' | ||
import { Cropper } from 'vue-advanced-cropper' | ||
import 'vue-advanced-cropper/dist/style.css' | ||
export interface Props { | ||
/** Images to be cropped */ | ||
modelValue?: File | ||
/** Crop frame aspect ratio (width/height), default 1/1 */ | ||
stencilAspectRatio?: number | ||
/** The ratio of the longest edge of the cut box to the length of the cut screen, default 0.9, not more than 1 */ | ||
stencilSizePercentage?: number | ||
} | ||
const props = withDefaults(defineProps<Props>(), { | ||
stencilAspectRatio: 1 / 1, | ||
stencilSizePercentage: 0.9, | ||
}) | ||
const emits = defineEmits<{ | ||
(event: 'update:modelValue', value: File): void | ||
}>() | ||
const vmFile = useVModel(props, 'modelValue', emits, { passive: true }) | ||
const cropperDialog = ref(false) | ||
const cropper = ref<InstanceType<typeof Cropper>>() | ||
const cropperFlag = ref(false) | ||
const cropperImage = reactive({ | ||
src: '', | ||
type: 'image/jpg', | ||
}) | ||
const stencilSize = ({ boundaries }: { boundaries: Boundaries }) => { | ||
return { | ||
width: boundaries.width * props.stencilSizePercentage, | ||
height: boundaries.height * props.stencilSizePercentage, | ||
} | ||
} | ||
watch(vmFile, (file, _, onCleanup) => { | ||
let expired = false | ||
onCleanup(() => expired = true) | ||
if (file && !cropperFlag.value) { | ||
cropperDialog.value = true | ||
const reader = new FileReader() | ||
reader.readAsDataURL(file) | ||
reader.onload = (e) => { | ||
if (expired) | ||
return | ||
cropperImage.src = e.target?.result as string | ||
cropperImage.type = file.type | ||
} | ||
} | ||
cropperFlag.value = false | ||
}) | ||
const cropImage = () => { | ||
if (cropper.value && vmFile.value) { | ||
cropperFlag.value = true | ||
cropperDialog.value = false | ||
const { canvas } = cropper.value.getResult() | ||
canvas?.toBlob((blob) => { | ||
vmFile.value = new File([blob as any], `cropped${vmFile.value?.name}` as string, { type: blob?.type }) | ||
}, cropperImage.type) | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<ModalDialog v-model="cropperDialog" :use-v-if="false" max-w-500px flex> | ||
<div flex-1 w-0> | ||
<div text-lg text-center my2 px3> | ||
<h1> | ||
{{ $t('action.edit') }} | ||
</h1> | ||
</div> | ||
<div aspect-ratio-1> | ||
<Cropper | ||
ref="cropper" | ||
class="overflow-hidden w-full h-full" | ||
:src="cropperImage.src" | ||
:resize-image="{ | ||
adjustStencil: false, | ||
}" | ||
:stencil-size="stencilSize" | ||
:stencil-props="{ | ||
aspectRatio: props.stencilAspectRatio, | ||
movable: false, | ||
resizable: false, | ||
handlers: {}, | ||
}" | ||
image-restriction="stencil" | ||
/> | ||
</div> | ||
<div m-4> | ||
<button | ||
btn-solid w-full rounded text-sm | ||
@click="cropImage()" | ||
> | ||
{{ $t('action.confirm') }} | ||
</button> | ||
</div> | ||
</div> | ||
</ModalDialog> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<script lang="ts" setup> | ||
const props = withDefaults(defineProps<{ | ||
modelValue?: File | ||
/** The image src before change */ | ||
original?: string | ||
/** Allowed file types */ | ||
allowedFileTypes?: string[] | ||
/** Allowed file size */ | ||
allowedFileSize?: number | ||
imgClass?: string | ||
loading?: boolean | ||
}>(), { | ||
allowedFileTypes: () => ['image/jpeg', 'image/png'], | ||
allowedFileSize: 1024 * 1024 * 5, // 5 MB | ||
}) | ||
const emits = defineEmits<{ | ||
(event: 'update:modelValue', value: File): void | ||
(event: 'error', code: number, message: string): void | ||
}>() | ||
const vmFile = useVModel(props, 'modelValue', emits, { passive: true }) | ||
const { t } = useI18n() | ||
const elInput = ref<HTMLInputElement>() | ||
function clearInput() { | ||
if (elInput.value) | ||
elInput.value.value = '' | ||
} | ||
function selectImage(e: Event) { | ||
const target = e.target as HTMLInputElement | ||
const image = target.files?.[0] | ||
if (!image) { | ||
vmFile.value = image | ||
} | ||
else if (!props.allowedFileTypes.includes(image.type)) { | ||
emits('error', 1, t('error.unsupported_file_format')) | ||
clearInput() | ||
} | ||
else if (image.size > props.allowedFileSize) { | ||
emits('error', 2, t('error.file_size_cannot_exceed_n_mb', [5])) | ||
clearInput() | ||
} | ||
else { | ||
vmFile.value = image | ||
clearInput() | ||
} | ||
} | ||
const defaultImage = computed(() => props.original || '') | ||
/** Preview of selected images */ | ||
const previewImage = ref('') | ||
/** The current images on display */ | ||
const imageSrc = computed<string>(() => previewImage.value || defaultImage.value) | ||
// Update the preview image when the input file change | ||
watch(vmFile, (image, _, onCleanup) => { | ||
let expired = false | ||
onCleanup(() => expired = true) | ||
if (image) { | ||
const reader = new FileReader() | ||
reader.readAsDataURL(image) | ||
reader.onload = (e) => { | ||
if (expired) | ||
return | ||
previewImage.value = e.target?.result as string | ||
} | ||
} | ||
else { | ||
previewImage.value = '' | ||
clearInput() | ||
} | ||
}) | ||
defineExpose({ | ||
clearInput, | ||
}) | ||
</script> | ||
|
||
<template> | ||
<label | ||
class="bg-slate-500/10 focus-within:(outline outline-primary)" | ||
relative | ||
flex justify-center items-center | ||
cursor-pointer | ||
of-hidden | ||
> | ||
<img | ||
v-if="imageSrc" | ||
:src="imageSrc" | ||
:class="imgClass || ''" | ||
object-cover | ||
w-full | ||
h-full | ||
> | ||
<div absolute bg="black/50" text-white rounded-full text-xl w12 h12 flex justify-center items-center hover="bg-black/40 text-primary"> | ||
<div i-ri:upload-line /> | ||
</div> | ||
|
||
<div | ||
v-if="loading" | ||
absolute inset-0 | ||
bg="black/30" text-white | ||
flex justify-center items-center | ||
> | ||
<div class="i-ri:loader-4-line animate-spin animate-duration-[2.5s]" text-4xl /> | ||
</div> | ||
<input | ||
ref="elInput" | ||
type="file" | ||
absolute opacity-0 inset-0 z--1 | ||
:accept="allowedFileTypes.join(',')" | ||
@change="selectImage" | ||
> | ||
</label> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<script lang="ts" setup> | ||
const props = defineProps<{ | ||
text?: string | ||
icon?: string | ||
to: string | Record<string, string> | ||
command?: boolean | ||
}>() | ||
const router = useRouter() | ||
if (props.command) { | ||
useCommand({ | ||
scope: 'Settings', | ||
name: () => props.text ?? (typeof props.to === 'string' ? props.to as string : props.to.name), | ||
icon: () => props.icon || '', | ||
onActivate() { | ||
router.push(props.to) | ||
}, | ||
}) | ||
} | ||
</script> | ||
|
||
<template> | ||
<NuxtLink | ||
:to="to" | ||
exact-active-class="text-primary" | ||
block w-full group focus:outline-none | ||
@click="$scrollToTop" | ||
> | ||
<div | ||
w-full flex w-fit px5 py3 md:gap2 gap4 items-center | ||
transition-250 group-hover:bg-active | ||
group-focus-visible:ring="2 current" | ||
> | ||
<div flex-1 flex items-center md:gap2 gap4> | ||
<div | ||
flex items-center justify-center | ||
:class="$slots.description ? 'w-12 h-12' : ''" | ||
> | ||
<slot name="icon"> | ||
<div v-if="icon" :class="icon" md:text-size-inherit text-xl /> | ||
</slot> | ||
</div> | ||
<div space-y-1> | ||
<p> | ||
<slot> | ||
<span>{{ text }}</span> | ||
</slot> | ||
</p> | ||
<p v-if="$slots.description" text-sm text-secondary> | ||
<slot name="description" /> | ||
</p> | ||
</div> | ||
</div> | ||
<div i-ri:arrow-right-s-line text-xl text-secondary-light /> | ||
</div> | ||
</NuxtLink> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,4 +9,3 @@ async function run() { | |
} | ||
|
||
run() | ||
|
Oops, something went wrong.