Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
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
33 changes: 17 additions & 16 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,
showDraggable: true,
bottomClose: false,
showDraggable: false,
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
3 changes: 3 additions & 0 deletions platforms/metagram/src/lib/ui/InputRadio/InputRadio.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
selected?: string;
name?: string;
value: string;
id?: string;
}

let {
value = '',
selected = $bindable(''),
name = '',
id,
...restProps
}: IInputRadioProps = $props();

Expand All @@ -23,6 +25,7 @@

<input
{...restProps}
{id}
type="radio"
{value}
bind:group={selected}
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 @@ -6,3 +6,4 @@ 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 InputRadio } from "./InputRadio/InputRadio.svelte";
export { default as Textarea } from "./Textarea/Textarea.svelte";
96 changes: 93 additions & 3 deletions platforms/metagram/src/routes/(protected)/+layout.svelte
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<script lang="ts">
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 { showComments } from '$lib/store/store.svelte';
import type { CommentType } from '$lib/types';
import { ArrowLeft01Icon, ArrowLeft02Icon } from '@hugeicons/core-free-icons';
import { Button, Label, Textarea } from '$lib/ui';
import InputRadio from '$lib/ui/InputRadio/InputRadio.svelte';
import { ArrowLeft02Icon } from '@hugeicons/core-free-icons';
import { HugeiconsIcon } from '@hugeicons/svelte';
import type { CupertinoPane } from 'cupertino-pane';
let { children } = $props();
let route = $derived(page.url.pathname);
Expand All @@ -15,7 +19,15 @@
let commentInput: HTMLInputElement | undefined = $state();
let _comments = $state(comments);
let activeReplyToId: string | null = $state(null);
let paneModal: CupertinoPane | undefined = $state();
let files: FileList | undefined = $state();
let imagePreviews: string[] = $state([]);
let isAddCaption: boolean = $state(false);
let caption: string = $state('');
let idFromParams = $state();
let postVisibility = $state('');
let postVisibilityOptions = ["only followers", "close-friends", "anyone"]
const handleSend = async () => {
const newComment = {
Expand Down Expand Up @@ -70,12 +82,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' && route !== '/profile' && route !== '/settings' && !route.includes('/profile') ? '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-16 md:px-8 md:pt-8">
{#if route === '/profile/post'}
<button
Expand Down Expand Up @@ -153,3 +188,58 @@
<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 flex items-center 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>
<h3 class="text-black-800 mt-20 mb-2">Who can see the post?</h3>
{#each postVisibilityOptions as option,i}
<div class="flex items-center justify-between w-[50%] mb-2">
<Label for={option + i}>{option}</Label>
<InputRadio name="post-visibility" id={option + i} value={option} bind:selected={postVisibility}/>
</div>
{/each}
{/if}
{#if files}
<div class="grid grid-cols-2 gap-2">
<Button
variant="secondary"
size="sm"
callback={async () => {
files = undefined;
paneModal?.destroy({animate: true})
}}>Cancel</Button
>
<Button
variant="secondary"
size="sm"
callback={async () => {
isAddCaption = true;
}}>Next</Button
>
</div>
{/if}
</Modal>
6 changes: 1 addition & 5 deletions platforms/metagram/src/routes/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,7 @@
<img src="/images/Logo.svg" alt="logo" />
</main>
{:else}
<main
class="h-[100dvh] overflow-hidden {page.url.pathname.includes('/profile')
? 'px-0'
: 'px-4'} md:px-0"
>
<main class="h-[100dvh] overflow-hidden px-4 md:px-0">
{@render children()}
</main>
{/if}
Loading