Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions platforms/metagram/src/lib/fragments/InputFile/InputFile.svelte
Original file line number Diff line number Diff line change
@@ -1,33 +1,46 @@
<script lang="ts">
import { cn } from '$lib/utils';
import { Album01Icon } from '@hugeicons/core-free-icons';
import { HugeiconsIcon } from '@hugeicons/svelte';
import type { HTMLLabelAttributes } from 'svelte/elements';

interface IInputFileProps {
interface IInputFileProps extends HTMLLabelAttributes {
files: FileList | undefined;
accept: string;
label: string;
cancelLabel: string;
oncancel: () => void;
label?: string;
multiple?: boolean;
cancelLabel?: string;
oncancel?: () => void;
}

let {
files = $bindable(),
accept = 'image/*',
label = 'Click to upload a photo',
multiple = false,
cancelLabel = 'Delete upload',
oncancel
oncancel,
...restProps
}: IInputFileProps = $props();

const uniqueId = Math.random().toString().split('.')[1];
let inputFile: HTMLInputElement | undefined = $state();

let cBase =
'bg-grey text-black-400 font-geist flex min-h-[158px] w-full items-center justify-center rounded-4xl text-base font-normal';
</script>

<input id={uniqueId} type="file" bind:files class="hidden" {accept} bind:this={inputFile} />
<input
id={uniqueId}
type="file"
bind:files
class="hidden"
{multiple}
{accept}
bind:this={inputFile}
/>

<label
for={uniqueId}
class="bg-grey text-black-400 font-geist flex h-[158px] w-full items-center justify-center rounded-4xl text-base font-normal"
>
<label {...restProps} for={uniqueId} class={cn([cBase, restProps.class].join(' '))}>
{#if files}
<div class="flex flex-col items-center gap-2">
<HugeiconsIcon size="24px" icon={Album01Icon} color="var(--color-black-600)" />
Expand Down
31 changes: 16 additions & 15 deletions platforms/metagram/src/lib/fragments/Modal/Modal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@
interface IDrawerProps extends HTMLAttributes<HTMLDivElement> {
modalEl?: HTMLDivElement;
paneModal?: CupertinoPane;
initialBreak?: 'bottom' | 'top' | 'middle';
handleDismiss?: () => void;
children?: Snippet;
}
let {
modalEl = $bindable(),
paneModal = $bindable(),
children = undefined,
initialBreak,
handleDismiss,
...restProps
}: IDrawerProps = $props();
function present() {
if (paneModal) paneModal.present({ animate: true });
}
function dismiss() {
handleDismiss && handleDismiss();
if (paneModal) paneModal.destroy({ animate: true });
}
Expand All @@ -30,25 +31,25 @@
paneModal = new CupertinoPane(modalEl, {
modal: true,
backdrop: true,
backdropBlur: true,
backdropOpacity: 0.4,
animationType: 'ease',
animationDuration: 300,
fitHeight: true,
bottomClose: true,
showDraggable: true,
buttonDestroy: false,
initialBreak: initialBreak,
breaks: {
bottom: { enabled: true, height: 250 }
top: { enabled: true, height: 600 },
middle: { enabled: true, height: 400 },
bottom: { enabled: true, height: 200 }
},
initialBreak: 'bottom',
cssClass: 'modal',
events: {
onBackdropTap: () => dismiss()
}
});
present();
return () => {
if (paneModal) paneModal.destroy({ animate: false });
};
});
</script>

Expand All @@ -60,13 +61,13 @@

<style>
:global(.modal .pane) {
width: 95% !important;
max-height: 300px !important;
width: 100% !important;
max-height: 600px !important;
min-height: 100px !important;
height: auto !important;
position: fixed !important;
bottom: 30px !important;
left: 50% !important;
left: 40% !important;
transform: translateX(-50%) !important;
border-radius: 32px !important;
padding: 20px !important;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,20 @@
$props();
</script>

<article {...restProps} class={cn(['flex justify-between items-center', restProps.class].join(' '))}>
<div class="me-4.5 flex items-start">
<Avatar size="sm" src={userImgSrc} />
<div class="ms-2">
<h3 class="font-semibold text-black">{userName}</h3>
<p class="text-black-600">{description}</p>
</div>
</div>
<Button class="max-w-[100px]" variant="secondary" size="sm" callback={handleFollow}>Follow</Button>
<article
{...restProps}
class={cn(['flex items-center justify-between', restProps.class].join(' '))}
>
<div class="me-4.5 flex items-start">
<Avatar size="sm" src={userImgSrc} />
<div class="ms-2">
<h3 class="font-semibold text-black">{userName}</h3>
<p class="text-black-600">{description}</p>
</div>
</div>
<Button class="max-w-[100px]" variant="secondary" size="sm" callback={handleFollow}
>Follow</Button
>
</article>

<!--
Expand Down
17 changes: 17 additions & 0 deletions platforms/metagram/src/lib/ui/Textarea/Textarea.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Textarea } from '..';

export default {
title: 'UI/Textarea',
component: Textarea,
tags: ['autodocs'],
render: (args: { type: string; placeholder: string }) => ({
Component: Textarea,
props: args
})
};

export const Main = {
args: {
placeholder: 'Joe Biden'
}
};
32 changes: 32 additions & 0 deletions platforms/metagram/src/lib/ui/Textarea/Textarea.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<script lang="ts">
import { cn } from '$lib/utils';
import type { HTMLTextareaAttributes } from 'svelte/elements';
interface IInputProps extends HTMLTextareaAttributes {
textarea?: HTMLTextAreaElement;
value: string | number | any;
placeholder?: string;
}
let {
textarea = $bindable(),
value = $bindable(),
placeholder = '',
...restProps
}: IInputProps = $props();
const cbase = $derived(
'w-full bg-grey resize-none py-3.5 px-6 text-[15px] text-black-800 font-geist font-normal placeholder:text-black-600 rounded-4xl outline-0 border border-transparent invalid:border-red invalid:text-red focus:invalid:text-black-800 focus:invalid:border-transparent'
);
</script>

<!-- svelte-ignore element_invalid_self_closing_tag -->
<textarea
{...restProps}
rows={6}
{placeholder}
bind:value
bind:this={textarea}
class={cn([cbase, restProps.class].join(' '))}
tabindex="0"
/>
1 change: 1 addition & 0 deletions platforms/metagram/src/lib/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { default as Select } from './Select/Select.svelte';
export { default as Label } from './Label/Label.svelte';
export { default as Toggle } from './Toggle/Toggle.svelte';
export { default as Helper } from './Helper/Helper.svelte';
export { default as Textarea } from './Textarea/Textarea.svelte';
84 changes: 82 additions & 2 deletions platforms/metagram/src/routes/(protected)/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { comments } from '$lib/dummyData';
import { BottomNav, Header, Comment, MessageInput, SideBar } from '$lib/fragments';
import { BottomNav, Header, Comment, MessageInput, SideBar, Modal } from '$lib/fragments';
import InputFile from '$lib/fragments/InputFile/InputFile.svelte';
import UserRequest from '$lib/fragments/UserRequest/UserRequest.svelte';
import { Settings } from '$lib/icons';
import { showComments } from '$lib/store/store.svelte';
import type { CommentType } from '$lib/types';
import Button from '$lib/ui/Button/Button.svelte';
import Label from '$lib/ui/Label/Label.svelte';
import Textarea from '$lib/ui/Textarea/Textarea.svelte';
import type { CupertinoPane } from 'cupertino-pane';
let { children } = $props();
let route = $derived(page.url.pathname);
Expand All @@ -16,6 +21,11 @@
let _comments = $state(comments);
let activeReplyToId: string | null = $state(null);
let chatFriendId = $state();
let paneModal: CupertinoPane | undefined = $state();
let files: FileList | undefined = $state();
let imagePreviews: string[] = $state([]);
let isAddCaption: boolean = $state(false);
let caption: string = $state('');
const handleSend = async () => {
const newComment = {
Expand Down Expand Up @@ -71,12 +81,35 @@
heading = 'Profile';
}
});
$effect(() => {
if (files) {
const readers = Array.from(files).map((file) => {
return new Promise<string>((resolve) => {
const reader = new FileReader();
reader.onload = (e) => resolve(e.target?.result as string);
reader.readAsDataURL(file);
});
});
Promise.all(readers).then((previews) => {
imagePreviews = previews;
});
} else {
imagePreviews = [];
}
});
</script>

<main
class={`block h-[100dvh] ${route !== '/home' && route !== '/messages' ? 'grid-cols-[20vw_auto]' : 'grid-cols-[20vw_auto_30vw]'} md:grid`}
>
<SideBar profileSrc="https://picsum.photos/200" handlePost={async () => alert('adas')} />
<SideBar
profileSrc="https://picsum.photos/200"
handlePost={async () => {
if (paneModal) paneModal.present({ animate: true });
}}
/>
<section class="hide-scrollbar h-[100dvh] overflow-y-auto px-4 pb-8 md:px-8 md:pt-8">
<div class="flex items-center justify-between">
<Header
Expand Down Expand Up @@ -152,3 +185,50 @@
<BottomNav class="btm-nav" profileSrc="https://picsum.photos/200" />
{/if}
</main>

<Modal bind:paneModal initialBreak="middle" handleDismiss={() => (files = undefined)}>
<h1 class="mb-6 font-semibold text-black">Upload a Photo</h1>
{#if !isAddCaption}
{#if !files}
<InputFile class="mb-4 h-[40vh]" bind:files accept="images/*" multiple={true} />
{:else}
<div class="mb-4 grid grid-cols-3 gap-2">
{#each imagePreviews as src}
<div class="aspect-[4/5] overflow-hidden rounded-lg border">
<!-- svelte-ignore a11y_img_redundant_alt -->
<img {src} alt="Selected image" class="h-full w-full object-cover" />
</div>
{/each}
</div>
{/if}
{:else}
<Label>Add a Caption</Label>
<Textarea class="mb-4" bind:value={caption} placeholder="enter caption" />
<div class="mb-4 grid grid-cols-3 gap-2">
{#each imagePreviews as src}
<div class="h-[100px] w-[80px] overflow-hidden rounded-lg border">
<!-- svelte-ignore a11y_img_redundant_alt -->
<img {src} alt="Selected image" class="h-[100px] w-[80px] object-cover" />
</div>
{/each}
</div>
{/if}
{#if files}
<div class="grid grid-cols-2 gap-2">
<Button
variant="secondary"
size="sm"
callback={async () => {
files = undefined;
}}>Cancel</Button
>
<Button
variant="secondary"
size="sm"
callback={async () => {
isAddCaption = true;
}}>Next</Button
>
</div>
{/if}
</Modal>
28 changes: 14 additions & 14 deletions platforms/metagram/src/routes/(protected)/discover/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@

<section>
<Input type="text" bind:value={searchValue} placeholder="Search user, post and more." />
{#if searchValue}
<ul class="pb-4 mt-6">
{#each { length: 5 } as _}
<li class="mb-4">
<UserRequest
userImgSrc="https://www.gravatar.com/avatar/2c7d99fe281ecd3bcd65ab915bac6dd5?s=250"
userName="luffythethird"
description="I’ve always wished life came at me fast. Funny how that wish never came through"
handleFollow={async () => alert('Adsad')}
/>
</li>
{/each}
</ul>
{/if}
{#if searchValue}
<ul class="mt-6 pb-4">
{#each { length: 5 } as _}
<li class="mb-4">
<UserRequest
userImgSrc="https://www.gravatar.com/avatar/2c7d99fe281ecd3bcd65ab915bac6dd5?s=250"
userName="luffythethird"
description="I’ve always wished life came at me fast. Funny how that wish never came through"
handleFollow={async () => alert('Adsad')}
/>
</li>
{/each}
</ul>
{/if}
</section>
Loading
Loading