mirror of
https://github.com/acid-info/Kurate.git
synced 2025-01-12 17:04:07 +00:00
style: persona flow (#225)
Co-authored-by: Vojtech Simetka <vojtech@simetka.cz>
This commit is contained in:
parent
5184eb6680
commit
777e1f18f9
@ -43,6 +43,7 @@
|
||||
font-weight: 600;
|
||||
font-size: var(--font-size-normal);
|
||||
transition: border-color 0.2s, background-color 0.2s, color 0.2s;
|
||||
white-space: nowrap;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
@ -99,7 +100,7 @@
|
||||
}
|
||||
}
|
||||
.secondary {
|
||||
background-color: var(--color-body-bg);
|
||||
background-color: transparent;
|
||||
border-color: var(--grey-200);
|
||||
color: var(--color-body-text);
|
||||
|
||||
@ -164,7 +165,7 @@
|
||||
|
||||
&:active:not(:disabled),
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-black-rgb);
|
||||
background-color: var(--color-black);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
}
|
||||
|
34
packages/ui/src/lib/components/container.svelte
Normal file
34
packages/ui/src/lib/components/container.svelte
Normal file
@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
</script>
|
||||
|
||||
<div class={`container ${cls}`}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
padding: 0 var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
max-width: 498px;
|
||||
margin-inline: auto;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding: 0 var(--spacing-48);
|
||||
max-width: 996px;
|
||||
}
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
max-width: 1494px;
|
||||
}
|
||||
|
||||
@media (min-width: 1640px) {
|
||||
max-width: 1992px;
|
||||
}
|
||||
|
||||
@media (min-width: 2038px) {
|
||||
max-width: 2490px;
|
||||
}
|
||||
}
|
||||
</style>
|
81
packages/ui/src/lib/components/grid-card.svelte
Normal file
81
packages/ui/src/lib/components/grid-card.svelte
Normal file
@ -0,0 +1,81 @@
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="root card-wrapper" on:click>
|
||||
<div class="card">
|
||||
<slot />
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
|
||||
hr {
|
||||
@media (min-width: 688px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--grey-150);
|
||||
}
|
||||
}
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--spacing-12);
|
||||
padding: var(--spacing-24);
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
max-width: 498px;
|
||||
margin-inline: auto;
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
min-width: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes new {
|
||||
from {
|
||||
background-color: var(--success-highlight);
|
||||
}
|
||||
to {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
:global(.new) {
|
||||
animation-name: new;
|
||||
animation-duration: 2.5s;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.root {
|
||||
&:not(:last-child) {
|
||||
border-bottom-color: var(--grey-500);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--grey-500);
|
||||
}
|
||||
}
|
||||
|
||||
:global(svg) {
|
||||
fill: var(--grey-100);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -11,9 +11,9 @@
|
||||
grid-auto-columns: auto;
|
||||
grid-template-columns: 100%;
|
||||
grid-auto-rows: auto;
|
||||
align-items: end;
|
||||
margin-inline: auto;
|
||||
|
||||
// decided to use max-width here to make it easier to match the page headers' width
|
||||
@media (min-width: 688px) {
|
||||
padding: 0 var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
|
@ -44,7 +44,7 @@
|
||||
<Button
|
||||
icon={Wallet}
|
||||
variant={'primary'}
|
||||
label={'Connect'}
|
||||
label={y === 0 ? 'Connect' : ''}
|
||||
on:click={() => handleConnect()}
|
||||
/>
|
||||
{/if}
|
||||
@ -59,7 +59,7 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(var(--color-body-bg-rgb), 0.93);
|
||||
backdrop-filter: blur(3px);
|
||||
backdrop-filter: blur(var(--blur));
|
||||
transition: box-shadow 0.2s;
|
||||
z-index: 100;
|
||||
|
||||
@ -69,26 +69,11 @@
|
||||
align-items: center;
|
||||
padding: var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
max-width: 498px;
|
||||
margin-inline: auto;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding: var(--spacing-48);
|
||||
max-width: 996px;
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
max-width: 1494px;
|
||||
}
|
||||
|
||||
@media (min-width: 1640px) {
|
||||
max-width: 1992px;
|
||||
}
|
||||
|
||||
@media (min-width: 2038px) {
|
||||
max-width: 2490px;
|
||||
}
|
||||
}
|
||||
|
||||
.header-title {
|
||||
@ -104,10 +89,8 @@
|
||||
transition: box-shadow 0.2s;
|
||||
|
||||
.header-content {
|
||||
@media (min-width: 688px) {
|
||||
padding-block: var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
padding-block: var(--spacing-12);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 1px 5px 0 rgba(var(--color-body-bg-rgb), 0.75);
|
||||
|
98
packages/ui/src/lib/components/header.svelte
Normal file
98
packages/ui/src/lib/components/header.svelte
Normal file
@ -0,0 +1,98 @@
|
||||
<script lang="ts">
|
||||
import Button from './button.svelte'
|
||||
import Undo from '$lib/components/icons/undo.svelte'
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
|
||||
let y: number
|
||||
|
||||
export let onBack: (() => unknown) | undefined = undefined
|
||||
export let title = ''
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
<header class={`root ${y > 0 ? 'scrolled' : ''} ${cls}`}>
|
||||
<div class="content">
|
||||
<div>
|
||||
{#if typeof onBack === 'function'}
|
||||
<Button icon={Undo} on:click={onBack} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<h1 class={`title ${cls}`}>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
<div class="btns">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<style lang="scss">
|
||||
header.root {
|
||||
position: sticky;
|
||||
inset: 0 -10px auto;
|
||||
background-color: rgba(var(--color-body-bg-rgb), 0.93);
|
||||
backdrop-filter: blur(var(--blur));
|
||||
z-index: 100;
|
||||
padding-inline: var(--spacing-24);
|
||||
padding-block: var(--spacing-24);
|
||||
transition: box-shadow 0.2s, padding 0.2s;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding-block: var(--spacing-48);
|
||||
padding-inline: var(--spacing-48);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: var(--spacing-12);
|
||||
|
||||
> * {
|
||||
flex-basis: 65%;
|
||||
&.title:not(:empty) {
|
||||
flex-basis: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
&:last-child {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
.btns:not(:empty) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-12);
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.scrolled {
|
||||
box-shadow: 0 6px 6px -6px rgba(var(--color-body-text-rgb), 0.25);
|
||||
padding-block: var(--spacing-12);
|
||||
padding-inline: 12px;
|
||||
transition: box-shadow 0.2s, padding 0.2s;
|
||||
|
||||
// @media (prefers-color-scheme: dark) {
|
||||
// box-shadow: 0 1px 5px 0 rgba(var(--color-body-bg-rgb), 0.75);
|
||||
// }
|
||||
}
|
||||
}
|
||||
</style>
|
15
packages/ui/src/lib/components/icons/hourglass.svelte
Normal file
15
packages/ui/src/lib/components/icons/hourglass.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
import type { IconProps } from '$lib/types'
|
||||
|
||||
type $$Props = IconProps
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let size = 20
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width={size} height={size} class={cls}>
|
||||
<path
|
||||
d="M23,11.67V4h3V2H6V4H9v7.67a2,2,0,0,0,.4,1.2L11.75,16,9.4,19.13a2,2,0,0,0-.4,1.2V28H6v2H26V28H23V20.33a2,2,0,0,0-.4-1.2L20.25,16l2.35-3.13A2,2,0,0,0,23,11.67ZM21,4v7H11V4Zm0,16.33V28H11V20.33L14.25,16,12,13h8l-2.25,3Z"
|
||||
/>
|
||||
</svg>
|
16
packages/ui/src/lib/components/icons/information.svelte
Normal file
16
packages/ui/src/lib/components/icons/information.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { IconProps } from '$lib/types'
|
||||
|
||||
type $$Props = IconProps
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let size = 20
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width={size} height={size} class={cls}>
|
||||
<path
|
||||
d="M17 22L17 14 13 14 13 16 15 16 15 22 12 22 12 24 20 24 20 22 17 22zM16 8a1.5 1.5 0 101.5 1.5A1.5 1.5 0 0016 8z"
|
||||
/>
|
||||
<path d="M16,30A14,14,0,1,1,30,16,14,14,0,0,1,16,30ZM16,4A12,12,0,1,0,28,16,12,12,0,0,0,16,4Z" />
|
||||
</svg>
|
16
packages/ui/src/lib/components/icons/request-quote.svelte
Normal file
16
packages/ui/src/lib/components/icons/request-quote.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { IconProps } from '$lib/types'
|
||||
|
||||
type $$Props = IconProps
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let size = 20
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width={size} height={size} class={cls}>
|
||||
<path d="M22,22v6H6V4H16V2H6A2,2,0,0,0,4,4V28a2,2,0,0,0,2,2H22a2,2,0,0,0,2-2V22Z" />
|
||||
<path
|
||||
d="M29.54,5.76l-3.3-3.3a1.6,1.6,0,0,0-2.24,0l-14,14V22h5.53l14-14a1.6,1.6,0,0,0,0-2.24ZM14.7,20H12V17.3l9.44-9.45,2.71,2.71ZM25.56,9.15,22.85,6.44l2.27-2.27,2.71,2.71Z"
|
||||
/>
|
||||
</svg>
|
16
packages/ui/src/lib/components/icons/rocket.svelte
Normal file
16
packages/ui/src/lib/components/icons/rocket.svelte
Normal file
@ -0,0 +1,16 @@
|
||||
<script lang="ts">
|
||||
import type { IconProps } from '$lib/types'
|
||||
|
||||
type $$Props = IconProps
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let size = 20
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width={size} height={size} class={cls}>
|
||||
<path d="M6.34 19H17.65V21H6.34z" transform="rotate(-45 11.995 20.002)" />
|
||||
<path
|
||||
d="M17,30a1,1,0,0,1-.37-.07,1,1,0,0,1-.62-.79l-1-7,2-.28.75,5.27L21,24.52V17a1,1,0,0,1,.29-.71l4.07-4.07A8.94,8.94,0,0,0,28,5.86V4H26.14a8.94,8.94,0,0,0-6.36,2.64l-4.07,4.07A1,1,0,0,1,15,11H7.48L4.87,14.26l5.27.75-.28,2-7-1a1,1,0,0,1-.79-.62,1,1,0,0,1,.15-1l4-5A1,1,0,0,1,7,9h7.59l3.77-3.78A10.92,10.92,0,0,1,26.14,2H28a2,2,0,0,1,2,2V5.86a10.92,10.92,0,0,1-3.22,7.78L23,17.41V25a1,1,0,0,1-.38.78l-5,4A1,1,0,0,1,17,30Z"
|
||||
/>
|
||||
</svg>
|
29
packages/ui/src/lib/components/info-box.svelte
Normal file
29
packages/ui/src/lib/components/info-box.svelte
Normal file
@ -0,0 +1,29 @@
|
||||
<script lang="ts">
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
</script>
|
||||
|
||||
<div class={`info ${cls}`}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.info {
|
||||
text-align: center;
|
||||
padding-block: var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
|
||||
:global(p) {
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
|
||||
:global(.h2) {
|
||||
margin-bottom: var(--spacing-12);
|
||||
}
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding-block: var(--spacing-48);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,23 +1,12 @@
|
||||
<script lang="ts">
|
||||
import Undo from '$lib/components/icons/undo.svelte'
|
||||
import Button from '$lib/components/button.svelte'
|
||||
|
||||
let y: number
|
||||
import Header from '$lib/components/header.svelte'
|
||||
|
||||
export let title: string
|
||||
|
||||
export let onBack: () => unknown = () => history.back()
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
<header class={y > 0 ? 'scrolled' : ''}>
|
||||
<div class="header-content">
|
||||
<div class="btn-undo">
|
||||
<Button icon={Undo} on:click={onBack} />
|
||||
</div>
|
||||
<h1>{title}</h1>
|
||||
</div>
|
||||
</header>
|
||||
<Header {title} {onBack} />
|
||||
|
||||
<div class="content">
|
||||
<slot />
|
||||
@ -28,78 +17,6 @@
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(var(--color-body-bg-rgb), 0.93);
|
||||
backdrop-filter: blur(3px);
|
||||
z-index: 100;
|
||||
padding: var(--spacing-24);
|
||||
transition: padding 0.2s, box-shadow 0.2s;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 1px 5px 0 rgba(var(--color-body-bg-rgb), 0.75);
|
||||
}
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding: var(--spacing-48);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
position: relative;
|
||||
max-width: 450px;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: max-width 0.2s;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
max-width: 996px;
|
||||
transition: max-width 0.2s;
|
||||
}
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
max-width: 1494px;
|
||||
}
|
||||
|
||||
@media (min-width: 1640px) {
|
||||
max-width: 1992px;
|
||||
}
|
||||
|
||||
@media (min-width: 2038px) {
|
||||
max-width: 2490px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-undo {
|
||||
position: absolute;
|
||||
inset: 0 0 auto 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
&.scrolled {
|
||||
box-shadow: 0 1px 5px 0 rgba(var(--color-body-text-rgb), 0.25);
|
||||
transition: box-shadow 0.2s;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding-block: var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
min-height: calc(100dvh - 92px);
|
||||
min-height: calc(100vh - 92px);
|
||||
@ -118,7 +35,6 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-12);
|
||||
padding-top: var(--spacing-48);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -7,7 +7,7 @@
|
||||
export let disabled: boolean | undefined = undefined
|
||||
export let label: string | undefined = undefined
|
||||
export let icon: ComponentConstructor<IconProps> | undefined = undefined
|
||||
export let variant: 'secondary' | 'primary' = 'secondary'
|
||||
export let variant: 'secondary' | 'primary' | 'overlay' = 'secondary'
|
||||
</script>
|
||||
|
||||
<label class={`root ${variant} ${cls}`}>
|
||||
@ -28,9 +28,6 @@
|
||||
padding-left: var(--spacing-12);
|
||||
padding-right: var(--spacing-12);
|
||||
height: 44px;
|
||||
outline-width: 1px;
|
||||
outline-style: solid;
|
||||
outline-offset: -1px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
box-sizing: border-box;
|
||||
@ -40,11 +37,11 @@
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--spacing-6);
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: var(--font-size-normal);
|
||||
transition: outline-width 0.1s, outline-color 0.1s, outline-style 0.1s, border-color 0.1s,
|
||||
outline-offset 0.1s, background-color 0.2s, color 0.2s;
|
||||
transition: border-color 0.2s, background-color 0.2s, color 0.2s;
|
||||
|
||||
&:disabled {
|
||||
cursor: not-allowed;
|
||||
@ -60,11 +57,9 @@
|
||||
.wrapper {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: var(--spacing-6);
|
||||
}
|
||||
.primary {
|
||||
color: var(--color-body-bg);
|
||||
outline-color: var(--color-body-text);
|
||||
border-color: var(--color-body-text);
|
||||
background-color: var(--color-body-text);
|
||||
|
||||
@ -75,22 +70,19 @@
|
||||
&:disabled {
|
||||
background-color: var(--grey-200);
|
||||
border-color: var(--grey-200);
|
||||
outline-color: var(--grey-200);
|
||||
color: var(--color-body-bg);
|
||||
}
|
||||
|
||||
&:active:not(:disabled),
|
||||
&:hover:not(:disabled) {
|
||||
outline-width: 3px;
|
||||
transition: outline-width 0.1s, outline-color 0.1s, outline-style 0.1s, border-color 0.1s,
|
||||
outline-offset 0.1s;
|
||||
border-color: var(--color-black);
|
||||
background-color: var(--color-black);
|
||||
transition: border-color 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
&:disabled {
|
||||
background-color: var(--grey-500);
|
||||
border-color: transparent;
|
||||
outline-color: transparent;
|
||||
background-color: var(--grey-200);
|
||||
color: var(--color-body-bg);
|
||||
|
||||
& :global(svg) {
|
||||
@ -100,16 +92,13 @@
|
||||
|
||||
&:active:not(:disabled),
|
||||
&:hover:not(:disabled) {
|
||||
outline-width: 3px;
|
||||
transition: outline-width 0.1s, outline-color 0.1s, outline-style 0.1s, border-color 0.1s,
|
||||
outline-offset 0.1s;
|
||||
transition: border-color 0.2s, background-color 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
.secondary {
|
||||
background-color: var(--color-body-bg);
|
||||
border-color: transparent;
|
||||
outline-color: var(--grey-200);
|
||||
border-color: var(--grey-200);
|
||||
color: var(--color-body-text);
|
||||
|
||||
& :global(svg) {
|
||||
@ -126,19 +115,13 @@
|
||||
|
||||
&:active:not(:disabled),
|
||||
&:hover:not(:disabled) {
|
||||
outline-width: 1px;
|
||||
outline-color: var(--color-body-text);
|
||||
outline-offset: 2px;
|
||||
transition: outline-width 0.1s, outline-color 0.1s, outline-style 0.1s, border-color 0.1s,
|
||||
outline-offset 0.1s;
|
||||
background-color: var(--grey-150);
|
||||
transition: border-color 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--color-body-bg);
|
||||
// border-color: var(--color-body-text);
|
||||
outline-width: 1px;
|
||||
outline-offset: 0px;
|
||||
outline-color: var(--grey-500);
|
||||
border-color: var(--color-body-text);
|
||||
color: var(--color-body-text);
|
||||
|
||||
& :global(svg) {
|
||||
@ -147,8 +130,6 @@
|
||||
|
||||
&:disabled {
|
||||
background-color: var(--color-body-bg);
|
||||
outline-color: var(--grey-500);
|
||||
border-color: transparent;
|
||||
color: var(--grey-500);
|
||||
|
||||
& :global(svg) {
|
||||
@ -158,11 +139,31 @@
|
||||
|
||||
&:active:not(:disabled),
|
||||
&:hover:not(:disabled) {
|
||||
outline-offset: 2px;
|
||||
border-color: transparent;
|
||||
transition: outline-width 0.1s, outline-color 0.1s, outline-style 0.1s, border-color 0.1s,
|
||||
outline-offset 0.1s;
|
||||
transition: border-color 0.2s, background-color 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
background-color: rgba(var(--color-black-rgb), 0.5);
|
||||
border-color: transparent;
|
||||
backdrop-filter: blur(var(--blur));
|
||||
color: var(--color-body-bg);
|
||||
|
||||
& :global(svg) {
|
||||
fill: var(--color-body-bg);
|
||||
}
|
||||
&:disabled {
|
||||
color: var(--grey-300);
|
||||
|
||||
& :global(svg) {
|
||||
fill: var(--grey-300);
|
||||
}
|
||||
}
|
||||
|
||||
&:active:not(:disabled),
|
||||
&:hover:not(:disabled) {
|
||||
background-color: var(--color-black);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
25
packages/ui/src/lib/components/learn-more.svelte
Normal file
25
packages/ui/src/lib/components/learn-more.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import ArrowRight from '$lib/components/icons/arrow-right.svelte'
|
||||
|
||||
export let href: string | undefined = undefined
|
||||
export let target: string | undefined = '_blank'
|
||||
export let arrow: boolean | undefined = undefined
|
||||
</script>
|
||||
|
||||
<a class="root" {href} {target}>
|
||||
Learn more
|
||||
{#if arrow === true}
|
||||
<ArrowRight size={12} />
|
||||
{/if}
|
||||
</a>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 3px;
|
||||
}
|
||||
</style>
|
25
packages/ui/src/lib/components/message-banner.svelte
Normal file
25
packages/ui/src/lib/components/message-banner.svelte
Normal file
@ -0,0 +1,25 @@
|
||||
<script lang="ts">
|
||||
import type { ComponentConstructor, IconProps } from '$lib/types'
|
||||
export let icon: ComponentConstructor<IconProps> | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div class="info-banner">
|
||||
<svelte:component this={icon} />
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.info-banner {
|
||||
position: sticky;
|
||||
inset: 0 0 auto;
|
||||
z-index: 100;
|
||||
background-color: var(--grey-200);
|
||||
padding: var(--spacing-12);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-6);
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
@ -1,74 +1,33 @@
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/grid-card.svelte'
|
||||
import UserMultiple from './icons/user-multiple.svelte'
|
||||
import Forum from './icons/forum.svelte'
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let name: string | undefined
|
||||
export let description: string | undefined
|
||||
export let postsCount: number
|
||||
export let picture: string | undefined
|
||||
</script>
|
||||
|
||||
<div class="persona-card-wrapper">
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class={`root ${cls}`} on:click>
|
||||
<div class="picture"><img src={picture} alt="persona" /></div>
|
||||
<div class="details">
|
||||
<div class="header">{name}</div>
|
||||
<div class="description">{description}</div>
|
||||
<div class="post-count">
|
||||
<div>
|
||||
<UserMultiple size={18} />
|
||||
{postsCount}
|
||||
</div>
|
||||
<div>
|
||||
<Forum size={18} />
|
||||
{postsCount}
|
||||
</div>
|
||||
<Card on:click>
|
||||
<div class="picture"><img src={picture} alt="persona" /></div>
|
||||
<div class="details">
|
||||
<div class="header">{name}</div>
|
||||
<div class="description">{description}</div>
|
||||
<div class="post-count">
|
||||
<div>
|
||||
<UserMultiple size={18} />
|
||||
{postsCount}
|
||||
</div>
|
||||
<div>
|
||||
<Forum size={18} />
|
||||
{postsCount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.persona-card-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
hr {
|
||||
@media (min-width: 688px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--spacing-12);
|
||||
padding: var(--spacing-24);
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
max-width: 498px;
|
||||
margin-inline: auto;
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
min-width: 350px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--grey-150);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
}
|
||||
}
|
||||
|
||||
.picture {
|
||||
flex: 0 0 90px;
|
||||
aspect-ratio: 1;
|
||||
@ -125,22 +84,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.root {
|
||||
&:not(:last-child) {
|
||||
border-bottom-color: var(--grey-500);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--grey-500);
|
||||
}
|
||||
}
|
||||
|
||||
:global(svg) {
|
||||
fill: var(--grey-100);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -2,12 +2,14 @@
|
||||
import Image from '$lib/components/icons/image.svelte'
|
||||
import Renew from '$lib/components/icons/renew.svelte'
|
||||
import Undo from '$lib/components/icons/undo.svelte'
|
||||
|
||||
import UserMultiple from './icons/user-multiple.svelte'
|
||||
import Forum from './icons/forum.svelte'
|
||||
import Profile from '$lib/components/profile-default.svelte'
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import InputFile from '$lib/components/input-file.svelte'
|
||||
import Container from '$lib/components/container.svelte'
|
||||
|
||||
import { clipAndResize } from '$lib/utils/image'
|
||||
|
||||
import { MAX_DIMENSIONS } from '$lib/constants'
|
||||
|
||||
export let name: string
|
||||
@ -17,6 +19,7 @@
|
||||
export let picture: string | undefined
|
||||
export let cover: string | undefined
|
||||
export let canEditPictures = false
|
||||
export let postsCount: number | undefined = undefined
|
||||
|
||||
let coverFiles: FileList | undefined = undefined
|
||||
let pictureFiles: FileList | undefined = undefined
|
||||
@ -52,11 +55,11 @@
|
||||
{/if}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<Button icon={Undo} variant="primary" on:click={onBack} />
|
||||
<Button icon={Undo} variant="overlay" on:click={onBack} />
|
||||
{#if canEditPictures}
|
||||
<InputFile
|
||||
icon={cover ? Renew : Image}
|
||||
variant="primary"
|
||||
variant="overlay"
|
||||
label={cover ? 'Change cover' : 'Add cover'}
|
||||
bind:files={coverFiles}
|
||||
/>
|
||||
@ -68,25 +71,41 @@
|
||||
{#if picture}
|
||||
<div class="img">
|
||||
<img src={picture} alt="profile" />
|
||||
</div>
|
||||
{#if canEditPictures}
|
||||
<div class="change">
|
||||
<InputFile icon={Renew} variant="primary" bind:files={pictureFiles} />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="change">
|
||||
<InputFile icon={Renew} variant="primary" bind:files={pictureFiles} />
|
||||
{#if canEditPictures}
|
||||
<div class="change">
|
||||
<InputFile icon={Renew} variant="overlay" bind:files={pictureFiles} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if canEditPictures}
|
||||
<div class="empty">
|
||||
<InputFile icon={Image} variant="primary" label="Add picture" bind:files={pictureFiles} />
|
||||
<div class="no-img">
|
||||
<div class="empty">
|
||||
<InputFile icon={Image} variant="overlay" label="Add profile" bind:files={pictureFiles} />
|
||||
</div>
|
||||
<div class="profile-default">
|
||||
<Profile />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div>{name}</div>
|
||||
<div>{pitch}</div>
|
||||
<div>{description}</div>
|
||||
<Container>
|
||||
<div class="persona-info">
|
||||
<h1 class="name">{name}</h1>
|
||||
<div class="pitch">{pitch}</div>
|
||||
<div class="description">{description}</div>
|
||||
<div class="post-count">
|
||||
<div>
|
||||
<UserMultiple size={18} />
|
||||
{postsCount}
|
||||
</div>
|
||||
<div>
|
||||
<Forum size={18} />
|
||||
{postsCount}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
|
||||
<div class="buttons-bottom">
|
||||
<slot name="button_primary" />
|
||||
@ -97,77 +116,153 @@
|
||||
|
||||
<style lang="scss">
|
||||
.top {
|
||||
height: 360px;
|
||||
position: absolute;
|
||||
aspect-ratio: 16/9;
|
||||
max-height: 342px;
|
||||
width: 100vw;
|
||||
background-color: #666666;
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
overflow: hidden;
|
||||
|
||||
@media (min-width: 608px) {
|
||||
aspect-ratio: none;
|
||||
height: 342px;
|
||||
}
|
||||
|
||||
.img {
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
inset: 0 0 0 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttons {
|
||||
padding: 48px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding: var(--spacing-48);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 100vw;
|
||||
position: relative;
|
||||
margin-bottom: var(--spacing-12);
|
||||
|
||||
.no-img,
|
||||
.img {
|
||||
aspect-ratio: 1;
|
||||
height: calc(calc(100vw / 1.777) - 68px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #c9c9c9;
|
||||
margin-inline: auto;
|
||||
position: relative;
|
||||
|
||||
@media (min-width: 608px) {
|
||||
height: 274px;
|
||||
}
|
||||
|
||||
@media (min-width: 688px) {
|
||||
height: 250px;
|
||||
}
|
||||
img {
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.empty,
|
||||
.change {
|
||||
position: absolute;
|
||||
inset: auto var(--spacing-12) var(--spacing-12) auto;
|
||||
transform: none;
|
||||
width: max-content;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.profile-default {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
:global(svg) {
|
||||
fill: var(--grey-300);
|
||||
position: absolute;
|
||||
inset: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.persona-info {
|
||||
text-align: center;
|
||||
max-width: 738px;
|
||||
margin-inline: auto;
|
||||
.name,
|
||||
.pitch,
|
||||
.description {
|
||||
margin-bottom: var(--spacing-12);
|
||||
max-width: 498px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-family: var(--font-serif);
|
||||
}
|
||||
}
|
||||
|
||||
.post-count {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--spacing-12);
|
||||
|
||||
> div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: nowrap;
|
||||
gap: var(--spacing-3);
|
||||
}
|
||||
}
|
||||
|
||||
.buttons-bottom {
|
||||
padding: 48px;
|
||||
position: relative;
|
||||
padding: var(--spacing-24);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.avatar {
|
||||
width: 268px;
|
||||
height: 268px;
|
||||
background-color: #c9c9c9;
|
||||
margin: auto;
|
||||
gap: var(--spacing-12);
|
||||
|
||||
.img {
|
||||
position: absolute;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
img {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
.empty {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.change {
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
padding: 12px;
|
||||
@media (min-width: 688px) {
|
||||
padding: var(--spacing-48);
|
||||
}
|
||||
}
|
||||
|
||||
// @media (prefers-color-scheme: dark) {
|
||||
// .buttons-bottom {
|
||||
// border-bottom: 1px solid var(--grey-500);
|
||||
// }
|
||||
// }
|
||||
</style>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<script lang="ts">
|
||||
import Undo from '$lib/components/icons/undo.svelte'
|
||||
import Close from '$lib/components/icons/close.svelte'
|
||||
import ArrowRight from '$lib/components/icons/arrow-right.svelte'
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import Textarea from '$lib/components/textarea.svelte'
|
||||
import Header from '$lib/components/header.svelte'
|
||||
import Checkmark from './icons/checkmark.svelte'
|
||||
|
||||
let y: number
|
||||
|
||||
@ -11,20 +11,14 @@
|
||||
export let pitch = ''
|
||||
export let description = ''
|
||||
export let title: string
|
||||
export let onCancel: () => void | Promise<void>
|
||||
export let onSubmit: () => void | Promise<void>
|
||||
export let onCancel: () => unknown
|
||||
export let onSubmit: () => unknown
|
||||
export let onBack: undefined | (() => unknown) = undefined
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
<header class={y > 0 ? 'scrolled' : ''}>
|
||||
<div class="header-content">
|
||||
<div class="btn-undo">
|
||||
<Button icon={Undo} on:click={() => history.back()} />
|
||||
</div>
|
||||
<h1>{title}</h1>
|
||||
</div>
|
||||
</header>
|
||||
<Header {title} {onBack} />
|
||||
|
||||
<form>
|
||||
<Textarea placeholder="Enter a short memorable name…" label="Persona name" bind:value={name} />
|
||||
@ -40,91 +34,19 @@
|
||||
/>
|
||||
|
||||
<div class="btns">
|
||||
<Button label="Cancel" icon={Close} on:click={onCancel} />
|
||||
|
||||
<Button
|
||||
label="Proceed"
|
||||
icon={ArrowRight}
|
||||
icon={Checkmark}
|
||||
variant="primary"
|
||||
disabled={!name || !pitch || !description}
|
||||
on:click={onSubmit}
|
||||
/>
|
||||
|
||||
<Button label="Cancel" icon={Close} on:click={onCancel} />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<style lang="scss">
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(var(--color-body-bg-rgb), 0.93);
|
||||
backdrop-filter: blur(3px);
|
||||
z-index: 100;
|
||||
padding: var(--spacing-24);
|
||||
transition: padding 0.2s, box-shadow 0.2s;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
box-shadow: 0 1px 5px 0 rgba(var(--color-body-bg-rgb), 0.75);
|
||||
}
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding: var(--spacing-48);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
position: relative;
|
||||
max-width: 450px;
|
||||
margin-inline: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: max-width 0.2s;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
max-width: 996px;
|
||||
transition: max-width 0.2s;
|
||||
}
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
max-width: 1494px;
|
||||
}
|
||||
|
||||
@media (min-width: 1640px) {
|
||||
max-width: 1992px;
|
||||
}
|
||||
|
||||
@media (min-width: 2038px) {
|
||||
max-width: 2490px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-undo {
|
||||
position: absolute;
|
||||
inset: 0 0 auto 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: var(--font-body);
|
||||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
text-align: center;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
&.scrolled {
|
||||
box-shadow: 0 1px 5px 0 rgba(var(--color-body-text-rgb), 0.25);
|
||||
transition: box-shadow 0.2s;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
padding-block: var(--spacing-24);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
min-height: calc(100dvh - 92px);
|
||||
min-height: calc(100vh - 92px);
|
||||
|
@ -1,50 +1,73 @@
|
||||
<script lang="ts">
|
||||
import Card from '$lib/components/grid-card.svelte'
|
||||
import { formatDateAndTime } from '$lib/utils/format'
|
||||
|
||||
import type { Post } from '$lib/stores/post'
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let post: Post
|
||||
</script>
|
||||
|
||||
<div class={`root ${cls}`}>
|
||||
<Card on:click>
|
||||
<div class="content-wrapper">
|
||||
<div class="imgs">
|
||||
<!-- I HARD CODED SOME IMAGES TO STYLE THE SECTION AND LEFT THEM TO SHOW THE STRUCTURE IN USE -->
|
||||
<!-- MORE THAN 3 IMAGES SHOULD HAVE A "PLUS" ICON OVER THE THIRD WITH COUNT OF EXTRA IMAGES -->
|
||||
|
||||
<div>
|
||||
<img src="https://via.placeholder.com/300x500" alt="Placeholder for testing" />
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://via.placeholder.com/400x840" alt="Placeholder for testing" />
|
||||
</div>
|
||||
<div>
|
||||
<img src="https://via.placeholder.com/100x75" alt="Placeholder for testing" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-content">{post.text}</div>
|
||||
<div class="user-info">
|
||||
<div class="faded">{formatDateAndTime(post.timestamp)}</div>
|
||||
</div>
|
||||
<div class="post-content">{post.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<style lang="scss">
|
||||
.root {
|
||||
border-bottom: 1px solid var(--grey-200);
|
||||
padding: var(--spacing-12);
|
||||
.imgs {
|
||||
display: flex;
|
||||
gap: var(--spacing-12);
|
||||
flex-direction: row;
|
||||
break-inside: avoid-column;
|
||||
gap: var(--spacing-6);
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
@media (min-width: 640px) {
|
||||
border: none;
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
outline-color: var(--grey-200);
|
||||
outline-offset: -0.5px;
|
||||
div {
|
||||
img {
|
||||
max-height: 300px;
|
||||
}
|
||||
&:not(:only-child) img {
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
border-bottom-color: var(--grey-500);
|
||||
outline-color: var(--grey-500);
|
||||
/* one item */
|
||||
div:first-child:nth-last-child(1) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* two items */
|
||||
div:first-child:nth-last-child(2),
|
||||
div:first-child:nth-last-child(2) ~ div {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
/* three items */
|
||||
div:first-child:nth-last-child(3),
|
||||
div:first-child:nth-last-child(3) ~ div {
|
||||
width: 33.3333%;
|
||||
}
|
||||
}
|
||||
.user-img {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.content-wrapper {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@ -52,28 +75,15 @@
|
||||
margin-bottom: var(--spacing-3);
|
||||
font-size: var(--font-size-sm);
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--spacing-6);
|
||||
}
|
||||
|
||||
.post-content {
|
||||
font-family: var(--font-serif);
|
||||
line-height: 1.38;
|
||||
}
|
||||
.faded {
|
||||
color: var(--grey-300);
|
||||
@media (prefers-color-scheme: dark) {
|
||||
color: var(--grey-400);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes newpost {
|
||||
from {
|
||||
background-color: var(--success-highlight);
|
||||
}
|
||||
to {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
:global(.newpost) {
|
||||
animation-name: newpost;
|
||||
animation-duration: 2.5s;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,14 +1,16 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import Close from '$lib/components/icons/close.svelte'
|
||||
import SendAltFilled from '$lib/components/icons/send-alt-filled.svelte'
|
||||
import InputString from '$lib/components/input-string.svelte'
|
||||
import Checkmark from '$lib/components/icons/checkmark.svelte'
|
||||
import Image from '$lib/components/icons/image.svelte'
|
||||
import Header from '$lib/components/header.svelte'
|
||||
import Textarea from '$lib/components/textarea.svelte'
|
||||
import { profile } from '$lib/stores/profile'
|
||||
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
export let submit: (postText: string) => void | Promise<void>
|
||||
export let cancel: () => void
|
||||
export let submit: (postText: string) => unknown
|
||||
export let onBack: () => unknown = () => history.back()
|
||||
export let label: string | undefined = 'Publish'
|
||||
|
||||
let postText = ''
|
||||
let x: number
|
||||
@ -17,61 +19,24 @@
|
||||
<svelte:window bind:innerWidth={x} />
|
||||
|
||||
<div class={`root ${cls}`}>
|
||||
<div class="header">
|
||||
<div>Create post</div>
|
||||
<div class="btns">
|
||||
<Button
|
||||
variant="secondary"
|
||||
icon={Close}
|
||||
label={x < 1280 ? '' : 'Cancel'}
|
||||
on:click={() => cancel()}
|
||||
/>
|
||||
<Button
|
||||
variant="primary"
|
||||
label="Publish"
|
||||
icon={SendAltFilled}
|
||||
on:click={() => submit(postText)}
|
||||
disabled={!$profile.signer}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Header {onBack}>
|
||||
<Button icon={Image} />
|
||||
<Button
|
||||
icon={Checkmark}
|
||||
variant="primary"
|
||||
{label}
|
||||
on:click={() => submit(postText)}
|
||||
disabled={!$profile.signer}
|
||||
/>
|
||||
</Header>
|
||||
<div class="post-content">
|
||||
<InputString bind:value={postText} placeholder="Write here..." autofocus />
|
||||
<Textarea bind:value={postText} placeholder="Write here..." autofocus />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
padding: var(--spacing-12) var(--spacing-12) var(--spacing-24);
|
||||
margin: 0 auto;
|
||||
transition: padding 0.2s;
|
||||
max-width: var(--container-width);
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
padding-top: var(--spacing-48);
|
||||
transition: padding 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--spacing-12);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
padding: var(--spacing-24) var(--spacing-12) var(--spacing-12);
|
||||
height: calc(100vh - 80px);
|
||||
text-align: center;
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
height: calc(100vh - 116px);
|
||||
}
|
||||
max-width: 450px;
|
||||
margin-inline: auto;
|
||||
}
|
||||
</style>
|
||||
|
15
packages/ui/src/lib/components/profile-default.svelte
Normal file
15
packages/ui/src/lib/components/profile-default.svelte
Normal file
@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
let cls: string | undefined = undefined
|
||||
export { cls as class }
|
||||
</script>
|
||||
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 250 250"
|
||||
preserveAspectRatio="xMinYMin slice"
|
||||
class={cls}
|
||||
>
|
||||
<path
|
||||
d="M126.344 61.828c-.225 0-.449.014-.672.042-17.706.06-31.415 2.182-40.88 4.82-4.798 1.338-8.513 2.778-11.308 4.274-1.398.748-2.565 1.483-3.644 2.458-1.079.974-2.636 2.173-2.636 5.334v49.126c0 8.7 4.616 21.123 13.998 33.688 9.381 12.565 23.897 24.686 43.977 29.162.767.17 1.563.17 2.33 0 20.08-4.476 34.596-16.596 43.977-29.162 9.382-12.566 13.998-24.998 13.998-33.699V78.756c0-3.161-1.557-4.36-2.636-5.334-1.079-.975-2.246-1.71-3.644-2.458-2.794-1.496-6.51-2.936-11.309-4.274-9.46-2.637-23.163-4.758-40.858-4.82a5.551 5.551 0 0 0-.693-.042zm0 10.753c17.204 0 30.2 2.104 38.664 4.463 4.231 1.18 7.351 2.454 9.125 3.403.35.188.361.226.598.378v47.046c0 4.048-3.659 16.283-11.855 27.262-7.082 9.486-17.43 18.29-31.156 22.987v-14.324h-10.752v14.324c-13.726-4.697-24.074-13.502-31.156-22.987-8.196-10.977-11.855-23.204-11.855-27.251V80.825c.237-.152.248-.19.599-.378 1.773-.95 4.893-2.223 9.125-3.403 8.463-2.359 21.46-4.463 38.663-4.463zm-22.965 16.13c-14.667 0-20.046 10.754-20.046 10.754s7.28-5.377 16.13-5.377c7.214 0 11.292 7.733 11.33 7.813h.01a5.376 5.376 0 0 0 4.788 2.94 5.377 5.377 0 0 0 4.81-7.78l-.021-.032c-.3-.634-3.162-8.317-17-8.317zm45.93 0c-13.914 0-16.747 7.79-17.022 8.35h.011a5.377 5.377 0 0 0-.578 2.404 5.377 5.377 0 0 0 5.377 5.377 5.376 5.376 0 0 0 4.788-2.951l.01.01c.038-.08 4.116-7.813 11.33-7.813 8.85 0 16.13 5.377 16.13 5.377s-5.38-10.753-20.046-10.753zm-47.158 16.13c-5.511 0-10.355 2.13-13.441 5.377 3.086 3.248 7.93 5.377 13.44 5.377 5.511 0 10.355-2.13 13.441-5.377-3.086-3.247-7.93-5.376-13.44-5.376zm48.387 0c-5.511 0-10.355 2.13-13.441 5.377 3.086 3.248 7.93 5.377 13.44 5.377 5.511 0 10.355-2.13 13.441-5.377-3.086-3.247-7.93-5.376-13.44-5.376zm-24.162 23.408-10.953 14.23h-8.096l-9.135-10.806-8.212 6.952 12.37 14.607h18.355l5.608-7.278 5.512 7.278h18.513l12.37-14.607-8.212-6.952-9.135 10.806h-8.19l-10.795-14.23z"
|
||||
/>
|
||||
</svg>
|
@ -4,6 +4,7 @@
|
||||
export let value = ''
|
||||
export let placeholder = ''
|
||||
export let label = ''
|
||||
export let autofocus = false
|
||||
|
||||
let placeholderHeight: number
|
||||
let textarea: HTMLTextAreaElement
|
||||
@ -47,7 +48,8 @@
|
||||
>
|
||||
{placeholder}
|
||||
</div>
|
||||
<textarea bind:value bind:this={textarea} class={value != '' ? 'content' : ''} />
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<textarea bind:value bind:this={textarea} class={value != '' ? 'content' : ''} {autofocus} />
|
||||
</div>
|
||||
</label>
|
||||
|
||||
@ -59,6 +61,14 @@
|
||||
gap: var(--spacing-6);
|
||||
font-size: var(--font-size-sm);
|
||||
color: var(--color-body-text);
|
||||
padding: var(--spacing-24);
|
||||
background-color: transparent;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:focus-within {
|
||||
background-color: var(--grey-150);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.area-placeholder {
|
||||
position: relative;
|
||||
@ -83,15 +93,11 @@
|
||||
background-color: transparent;
|
||||
transition: background-color 0.2s;
|
||||
font-size: var(--font-size-lg);
|
||||
font-family: var(--font-serif);
|
||||
|
||||
&:focus,
|
||||
&.content {
|
||||
background-color: #000;
|
||||
transition: background-color 0.2s;
|
||||
outline: none;
|
||||
@media (prefers-color-scheme: light) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
&.content {
|
||||
|
@ -1,141 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount, onDestroy, tick } from 'svelte'
|
||||
|
||||
interface GridItem {
|
||||
_el: HTMLDivElement
|
||||
gap: number
|
||||
items: HTMLElement[]
|
||||
ncol: number
|
||||
mod: number
|
||||
}
|
||||
|
||||
export let stretchFirst = false
|
||||
export let gridGap = '0.5em'
|
||||
export let colWidth = 'minmax(Min(20em, 100%), 1fr)'
|
||||
export let items: unknown[] = [] // pass in data if it's dynamically updated
|
||||
|
||||
let grids: GridItem[] = []
|
||||
let masonryElement: HTMLDivElement | undefined
|
||||
|
||||
const refreshLayout = async () => {
|
||||
grids.forEach(async (grid) => {
|
||||
/* get the post relayout number of columns */
|
||||
let ncol = getComputedStyle(grid._el).gridTemplateColumns.split(' ').length
|
||||
|
||||
grid.items.forEach((c) => {
|
||||
let new_h = c.getBoundingClientRect().height
|
||||
|
||||
if (new_h !== Number(c.dataset.h)) {
|
||||
c.dataset.h = new_h.toString()
|
||||
grid.mod++
|
||||
}
|
||||
})
|
||||
|
||||
/* if the number of columns has changed */
|
||||
if (grid.ncol !== ncol || grid.mod) {
|
||||
/* update number of columns */
|
||||
grid.ncol = ncol
|
||||
/* revert to initial positioning, no margin */
|
||||
grid.items.forEach((c) => c.style.removeProperty('margin-top'))
|
||||
/* if we have more than one column */
|
||||
if (grid.ncol > 1) {
|
||||
grid.items.slice(ncol).forEach((c, i) => {
|
||||
let prev_fin =
|
||||
grid.items[i].getBoundingClientRect().bottom /* bottom edge of item above */,
|
||||
curr_ini = c.getBoundingClientRect().top /* top edge of current item */
|
||||
|
||||
c.style.marginTop = `${prev_fin + grid.gap - curr_ini}px`
|
||||
})
|
||||
}
|
||||
|
||||
grid.mod = 0
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const calcGrid = async (_masonryArr: HTMLDivElement[]) => {
|
||||
await tick()
|
||||
if (_masonryArr.length && getComputedStyle(_masonryArr[0]).gridTemplateRows !== 'masonry') {
|
||||
grids = _masonryArr.map((grid) => {
|
||||
return {
|
||||
_el: grid,
|
||||
gap: parseFloat(getComputedStyle(grid).gridRowGap),
|
||||
items: ([...grid.childNodes] as HTMLElement[]).filter(
|
||||
(c) => c.nodeType === 1 && +getComputedStyle(c).gridColumnEnd !== -1,
|
||||
),
|
||||
ncol: 0,
|
||||
mod: 0,
|
||||
}
|
||||
})
|
||||
refreshLayout() /* initial load */
|
||||
}
|
||||
}
|
||||
|
||||
let _window: Window | undefined
|
||||
onMount(() => {
|
||||
_window = window
|
||||
_window.addEventListener('resize', refreshLayout, false) /* on resize */
|
||||
})
|
||||
onDestroy(() => {
|
||||
if (_window) {
|
||||
_window.removeEventListener('resize', refreshLayout, false) /* on resize */
|
||||
}
|
||||
})
|
||||
|
||||
$: if (masonryElement) {
|
||||
calcGrid([masonryElement])
|
||||
}
|
||||
|
||||
$: if (items) {
|
||||
// update if items are changed
|
||||
masonryElement = masonryElement // refresh masonryElement
|
||||
}
|
||||
</script>
|
||||
|
||||
<!--
|
||||
An almost direct copy and paste of: https://css-tricks.com/a-lightweight-masonry-solution
|
||||
Usage:
|
||||
- stretchFirst stretches the first item across the top
|
||||
<Masonry stretchFirst={true} >
|
||||
{#each data as o}
|
||||
<div class="_card _padding">
|
||||
Here's some stuff {o.name}
|
||||
<header>
|
||||
<h3>{o.name}</h3>
|
||||
</header>
|
||||
<section>
|
||||
<p>{o.text}</p>
|
||||
</section>
|
||||
</div>
|
||||
{/each}
|
||||
</Masonry>
|
||||
-->
|
||||
|
||||
<div
|
||||
bind:this={masonryElement}
|
||||
class={`__grid--masonry ${stretchFirst ? '__stretch-first' : ''}`}
|
||||
style={`--grid-gap: ${gridGap}; --col-width: ${colWidth};`}
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<!--
|
||||
$w: var(--col-width); // minmax(Min(20em, 100%), 1fr);
|
||||
$s: var(--grid-gap); // .5em;
|
||||
-->
|
||||
<style>
|
||||
:global(.__grid--masonry) {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, var(--col-width));
|
||||
grid-template-rows: masonry;
|
||||
justify-content: center;
|
||||
grid-gap: var(--grid-gap);
|
||||
padding: var(--grid-gap);
|
||||
}
|
||||
:global(.__grid--masonry > *) {
|
||||
align-self: start;
|
||||
}
|
||||
:global(.__grid--masonry.__stretch-first > *:first-child) {
|
||||
grid-column: 1/ -1;
|
||||
}
|
||||
</style>
|
@ -1,7 +1,13 @@
|
||||
<script lang="ts">
|
||||
import Search from '$lib/components/icons/search.svelte'
|
||||
import SettingsView from '$lib/components/icons/settings-view.svelte'
|
||||
import Add from '$lib/components/icons/add.svelte'
|
||||
|
||||
import HeaderTop from '$lib/components/header-top.svelte'
|
||||
import Persona from '$lib/components/persona.svelte'
|
||||
import Grid from '$lib/components/grid.svelte'
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import Container from '$lib/components/container.svelte'
|
||||
|
||||
import { profile } from '$lib/stores/profile'
|
||||
import { personas } from '$lib/stores/persona'
|
||||
@ -10,11 +16,6 @@
|
||||
import { goto } from '$app/navigation'
|
||||
import { ROUTES } from '$lib/routes'
|
||||
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import Search from '$lib/components/icons/search.svelte'
|
||||
import SettingsView from '$lib/components/icons/settings-view.svelte'
|
||||
import Add from '$lib/components/icons/add.svelte'
|
||||
|
||||
let filterText = ''
|
||||
let showChat = false
|
||||
|
||||
@ -49,9 +50,13 @@
|
||||
{:else}
|
||||
{#if $personas.draft?.length !== 0 && $profile.signer !== undefined}
|
||||
<div class="section-wrapper">
|
||||
<div class="subtitle">Draft personas</div>
|
||||
<div class="section-top">
|
||||
<Container class="flex">
|
||||
<div class="subtitle">Draft personas</div>
|
||||
<Button icon={Add} label="Create persona" on:click={createDraft} />
|
||||
</Container>
|
||||
</div>
|
||||
<hr />
|
||||
<Button icon={Add} label="Create persona" on:click={createDraft} />
|
||||
<Grid>
|
||||
{#each $personas.draft as draftPersona, index}
|
||||
<Persona
|
||||
@ -68,7 +73,11 @@
|
||||
|
||||
{#if $personas.favorite.length !== 0 && $profile.signer !== undefined}
|
||||
<div class="section-wrapper">
|
||||
<div class="subtitle">Favorites</div>
|
||||
<div class="section-top">
|
||||
<Container>
|
||||
<div class="subtitle">Favorites</div>
|
||||
</Container>
|
||||
</div>
|
||||
<hr />
|
||||
<Grid>
|
||||
{#each $personas.favorite as personaId}
|
||||
@ -146,20 +155,26 @@
|
||||
border-bottom-color: var(--grey-500);
|
||||
}
|
||||
}
|
||||
|
||||
.section-top {
|
||||
padding-block: var(--spacing-24);
|
||||
|
||||
:global(.flex) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
}
|
||||
.subtitle {
|
||||
padding: var(--spacing-24);
|
||||
font-size: var(--font-size-lg);
|
||||
font-weight: var(--font-weight-sb);
|
||||
|
||||
transition: padding 0.2s;
|
||||
max-width: 498px;
|
||||
margin-inline: auto;
|
||||
|
||||
@media (min-width: 688px) {
|
||||
max-width: 996px;
|
||||
transition: padding 0.2s;
|
||||
padding-inline: var(--spacing-48);
|
||||
}
|
||||
|
||||
@media (min-width: 1242px) {
|
||||
|
@ -3,51 +3,72 @@
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import Edit from '$lib/components/icons/edit.svelte'
|
||||
import Star from '$lib/components/icons/star.svelte'
|
||||
import Wallet from '$lib/components/icons/wallet.svelte'
|
||||
import StarFilled from '$lib/components/icons/star_filled.svelte'
|
||||
import Hourglass from '$lib/components/icons/hourglass.svelte'
|
||||
import Grid from '$lib/components/grid.svelte'
|
||||
import PersonaDetail from '$lib/components/persona_detail.svelte'
|
||||
import Header from '$lib/components/header.svelte'
|
||||
|
||||
import { posts } from '$lib/stores/post'
|
||||
import { personas } from '$lib/stores/persona'
|
||||
import { profile } from '$lib/stores/profile'
|
||||
import { goto } from '$app/navigation'
|
||||
import { browser } from '$app/environment'
|
||||
import { page } from '$app/stores'
|
||||
import Masonry from '$lib/masonry.svelte'
|
||||
import { ROUTES } from '$lib/routes'
|
||||
import PersonaDetail from '$lib/components/persona_detail.svelte'
|
||||
|
||||
let windowWidth: number = browser ? window.innerWidth : 0
|
||||
|
||||
function getMasonryColumnWidth(windowInnerWidth: number) {
|
||||
if (windowInnerWidth < 739) return '100%'
|
||||
if (windowInnerWidth < 1060) return 'minmax(min(100%/2, max(320px, 100%/2)), 1fr)'
|
||||
if (windowInnerWidth < 1381) return 'minmax(min(100%/3, max(320px, 100%/3)), 1fr)'
|
||||
if (windowInnerWidth < 1702) return 'minmax(min(100%/4, max(320px, 100%/4)), 1fr)'
|
||||
if (windowInnerWidth < 2023) return 'minmax(min(100%/5, max(320px, 100%/5)), 1fr)'
|
||||
if (windowInnerWidth < 2560) return 'minmax(min(100%/6, max(320px, 100%/6)), 1fr)'
|
||||
if (windowInnerWidth < 3009) return 'minmax(min(100%/7, max(320px, 100%/7)), 1fr)'
|
||||
return 'minmax(323px, 1fr)'
|
||||
}
|
||||
import { connectWallet } from '$lib/services'
|
||||
|
||||
const persona = $personas.all.get($page.params.id)
|
||||
|
||||
const handleConnect = async () => {
|
||||
try {
|
||||
const signer = await connectWallet()
|
||||
const address = await signer.getAddress()
|
||||
|
||||
$profile = { signer, address }
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
let y: number
|
||||
|
||||
export let onBack: () => unknown = () => history.back()
|
||||
</script>
|
||||
|
||||
<svelte:window bind:innerWidth={windowWidth} />
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
{#if persona === undefined}
|
||||
<div>There is no persona with group ID {$page.params.id}</div>
|
||||
{:else}
|
||||
<div class={`header ${y > 0 ? 'scrolled' : ''}`}>
|
||||
<Header title={persona.name} {onBack}>
|
||||
{#if $profile.signer !== undefined}
|
||||
<Button
|
||||
variant="primary"
|
||||
icon={Edit}
|
||||
on:click={() => goto(ROUTES.POST_NEW($page.params.id))}
|
||||
/>
|
||||
{:else}
|
||||
<Button variant="primary" icon={Wallet} on:click={() => handleConnect()} />
|
||||
{/if}
|
||||
</Header>
|
||||
</div>
|
||||
<PersonaDetail
|
||||
name={persona.name}
|
||||
pitch={persona.pitch}
|
||||
description={persona.description}
|
||||
postsCount={persona.postsCount}
|
||||
bind:picture={persona.picture}
|
||||
bind:cover={persona.cover}
|
||||
>
|
||||
<svelte:fragment slot="button_top">
|
||||
{#if $personas.favorite.includes($page.params.id)}
|
||||
<Button icon={StarFilled} variant="primary" label="Remove favorite" />
|
||||
{:else}
|
||||
<Button icon={Star} variant="primary" label="Add to favorites" />
|
||||
{#if $profile.signer !== undefined}
|
||||
{#if $personas.favorite.includes($page.params.id)}
|
||||
<Button icon={StarFilled} variant="overlay" label="Remove favorite" />
|
||||
{:else}
|
||||
<Button icon={Star} variant="overlay" label="Add to favorites" />
|
||||
{/if}
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
@ -59,22 +80,48 @@
|
||||
icon={Edit}
|
||||
on:click={() => goto(ROUTES.POST_NEW($page.params.id))}
|
||||
/>
|
||||
{/if}</svelte:fragment
|
||||
>
|
||||
{:else}
|
||||
<Button
|
||||
variant="primary"
|
||||
label="Connect to post"
|
||||
icon={Wallet}
|
||||
on:click={() => handleConnect()}
|
||||
/>
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="button_other">
|
||||
<!-- NEED TO ADD CORRECT ACTION HERE -->
|
||||
<Button label="Review pending" icon={Hourglass} />
|
||||
</svelte:fragment>
|
||||
|
||||
<!-- PLACE FILTER COMPONENT HERE -->
|
||||
|
||||
{#if $posts.loading}
|
||||
<p>Loading posts...</p>
|
||||
{:else if $posts.posts.length == 0}
|
||||
<p>There are no posts yet</p>
|
||||
{:else}
|
||||
<Masonry gridGap="0" colWidth={getMasonryColumnWidth(windowWidth)} items={$posts.posts}>
|
||||
<Grid>
|
||||
{#each $posts.posts as post}
|
||||
<Post {post} />
|
||||
<!-- NEEDS ONCLICK ACTION => SHOULD GO TO POST PAGE -->
|
||||
<Post {post} on:click />
|
||||
{/each}
|
||||
</Masonry>
|
||||
</Grid>
|
||||
{/if}
|
||||
</PersonaDetail>
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
position: fixed;
|
||||
inset: -100% 0 auto;
|
||||
z-index: 100;
|
||||
transition: inset 0.5s;
|
||||
|
||||
&.scrolled {
|
||||
inset: 0 0 auto;
|
||||
transition: inset 0.3s;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -54,10 +54,6 @@
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
history.back()
|
||||
}
|
||||
</script>
|
||||
|
||||
<PostNew {submit} {cancel} />
|
||||
<PostNew {submit} />
|
||||
|
@ -1,21 +1,28 @@
|
||||
<script lang="ts">
|
||||
import ArrowRight from '$lib/components/icons/arrow-right.svelte'
|
||||
import Checkmark from '$lib/components/icons/checkmark.svelte'
|
||||
import Close from '$lib/components/icons/close.svelte'
|
||||
import Edit from '$lib/components/icons/edit.svelte'
|
||||
import EditPersona from '$lib/components/icons/request-quote.svelte'
|
||||
import Undo from '$lib/components/icons/undo.svelte'
|
||||
import Info from '$lib/components/icons/information.svelte'
|
||||
import Launch from '$lib/components/icons/rocket.svelte'
|
||||
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import PersonaEditText from '$lib/components/persona_edit_text.svelte'
|
||||
import PersonaDetail from '$lib/components/persona_detail.svelte'
|
||||
import Post from '$lib/components/post.svelte'
|
||||
import PostNew from '$lib/components/post_new.svelte'
|
||||
import LearnMore from '$lib/components/learn-more.svelte'
|
||||
import Header from '$lib/components/header.svelte'
|
||||
import InfoScreen from '$lib/components/info_screen.svelte'
|
||||
import Banner from '$lib/components/message-banner.svelte'
|
||||
import Grid from '$lib/components/grid.svelte'
|
||||
import Container from '$lib/components/container.svelte'
|
||||
import InfoBox from '$lib/components/info-box.svelte'
|
||||
|
||||
import { personas } from '$lib/stores/persona'
|
||||
import { tokens } from '$lib/stores/tokens'
|
||||
|
||||
import { page } from '$app/stores'
|
||||
import InfoScreen from '$lib/components/info_screen.svelte'
|
||||
|
||||
const PERSONA_LIMIT = 5
|
||||
const TOKEN_POST_COST = 10
|
||||
@ -33,8 +40,13 @@
|
||||
$tokens.go -= TOKEN_POST_COST
|
||||
state = 'publish_success'
|
||||
}
|
||||
|
||||
let y: number
|
||||
export let onBack: () => unknown = () => history.back()
|
||||
</script>
|
||||
|
||||
<svelte:window bind:scrollY={y} />
|
||||
|
||||
{#if persona === undefined}
|
||||
<div>No draft persona with id {personaIndex}</div>
|
||||
{:else if state === 'text'}
|
||||
@ -42,7 +54,7 @@
|
||||
bind:name
|
||||
bind:pitch
|
||||
bind:description
|
||||
title="Edit persona"
|
||||
title="Edit Persona details"
|
||||
onSubmit={() => {
|
||||
persona.name = name
|
||||
persona.pitch = pitch
|
||||
@ -55,6 +67,10 @@
|
||||
}}
|
||||
/>
|
||||
{:else if state === 'posts'}
|
||||
<Banner icon={Info}>This is a preview of the Persona's page</Banner>
|
||||
<div class={`header ${y > 0 ? 'scrolled' : ''}`}>
|
||||
<Header title={persona.name} {onBack} />
|
||||
</div>
|
||||
<PersonaDetail
|
||||
name={persona.name}
|
||||
pitch={persona.pitch}
|
||||
@ -74,9 +90,9 @@
|
||||
/>
|
||||
{:else}
|
||||
<Button
|
||||
icon={Edit}
|
||||
icon={Launch}
|
||||
variant="primary"
|
||||
label="Publish persona"
|
||||
label="Publish Persona"
|
||||
on:click={() => (state = 'publish_warning')}
|
||||
/>
|
||||
{/if}
|
||||
@ -84,26 +100,35 @@
|
||||
<Button
|
||||
slot="button_other"
|
||||
variant="secondary"
|
||||
label="Edit persona"
|
||||
icon={Edit}
|
||||
label="Edit Persona details"
|
||||
icon={EditPersona}
|
||||
on:click={() => (state = 'text')}
|
||||
/>
|
||||
|
||||
{#if persona.posts.length < PERSONA_LIMIT}
|
||||
<p>{persona.posts.length} out {PERSONA_LIMIT} seed posts added</p>
|
||||
<p>You need {PERSONA_LIMIT} seed posts to publish this persona.</p>
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
{:else}
|
||||
<p>{PERSONA_LIMIT} out {PERSONA_LIMIT} seed posts added</p>
|
||||
<p>You can publish this persona.</p>
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
{/if}
|
||||
|
||||
{#each persona.posts as post}
|
||||
<Post {post} />
|
||||
{:else}
|
||||
<p>There are no posts yet</p>
|
||||
{/each}
|
||||
<hr />
|
||||
<Container>
|
||||
<InfoBox>
|
||||
{#if persona.posts.length < PERSONA_LIMIT}
|
||||
<div class="icon-info">
|
||||
<Info size={32} />
|
||||
</div>
|
||||
<p class="h2">{persona.posts.length} out {PERSONA_LIMIT} seed posts added</p>
|
||||
<p>You need {PERSONA_LIMIT} seed posts to publish this Persona.</p>
|
||||
<LearnMore href="/" />
|
||||
{:else}
|
||||
<div class="icon-success">
|
||||
<Checkmark />
|
||||
</div>
|
||||
<p>{PERSONA_LIMIT} out {PERSONA_LIMIT} seed posts added</p>
|
||||
<p>You can publish this Persona.</p>
|
||||
<LearnMore href="/" />
|
||||
{/if}
|
||||
</InfoBox>
|
||||
</Container>
|
||||
<Grid>
|
||||
{#each persona.posts as post}
|
||||
<Post {post} />
|
||||
{/each}
|
||||
</Grid>
|
||||
</PersonaDetail>
|
||||
{:else if state === 'post_new'}
|
||||
<PostNew
|
||||
@ -112,56 +137,77 @@
|
||||
personas.updateDraft(personaIndex, persona)
|
||||
state = 'posts'
|
||||
}}
|
||||
cancel={() => (state = 'posts')}
|
||||
label="Save seed post"
|
||||
onBack={() => (state = 'posts')}
|
||||
/>
|
||||
{:else if state === 'publish_warning'}
|
||||
<InfoScreen title="Publish persona" onBack={() => history.back()}>
|
||||
<InfoScreen
|
||||
title={$tokens.go >= TOKEN_POST_COST ? 'Publish Persona' : 'Not enough token'}
|
||||
onBack={() => history.back()}
|
||||
>
|
||||
{#if $tokens.go >= TOKEN_POST_COST}
|
||||
<div>
|
||||
<h1>This will use {TOKEN_POST_COST} GO</h1>
|
||||
<p>People will be able to post with this persona.</p>
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Currently available</h1>
|
||||
<span>{$tokens.go} GO</span>
|
||||
<p>Until cycle ends.</p>
|
||||
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
<div class="token-info">
|
||||
<div>
|
||||
<div class="icon">
|
||||
<Info size={32} />
|
||||
</div>
|
||||
<h2>This will use {TOKEN_POST_COST} GO</h2>
|
||||
<p>This Persona will be live, and everyone will be able to post with it.</p>
|
||||
<p><LearnMore href="/" /></p>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="h3">Currently available</div>
|
||||
<div class="go-amt">
|
||||
{$tokens.go}
|
||||
</div>
|
||||
<div class="go">GO</div>
|
||||
<p>Until new cycle begins</p>
|
||||
<LearnMore href="/" />
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div>
|
||||
<h1>Sorry, you can’t publish now</h1>
|
||||
<p>You need {TOKEN_POST_COST} GO to publish a persona.</p>
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
</div>
|
||||
<div>
|
||||
<h1>Currently available</h1>
|
||||
<span>{$tokens.go} GO</span>
|
||||
<p>Until cycle ends.</p>
|
||||
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
<div class="token-info">
|
||||
<div>
|
||||
<div class="icon">
|
||||
<Info size={32} />
|
||||
</div>
|
||||
<h2>Sorry, you can't publish now</h2>
|
||||
<p>You need {TOKEN_POST_COST} GO to publish a Persona.</p>
|
||||
<LearnMore href="/" />
|
||||
</div>
|
||||
<div class="box error">
|
||||
<div class="h3">Currently available</div>
|
||||
<div class="go-amt">
|
||||
{$tokens.go}
|
||||
</div>
|
||||
<div class="go">GO</div>
|
||||
<p>Until new cycle begins</p>
|
||||
<LearnMore href="/" />
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="buttons">
|
||||
{#if $tokens.go >= TOKEN_POST_COST}
|
||||
<Button variant="secondary" label="Cancel" icon={Close} on:click={() => (state = 'text')} />
|
||||
<Button icon={ArrowRight} variant="primary" label="Proceed" on:click={publishPersona} />
|
||||
<Button icon={Checkmark} variant="primary" label="I agree" on:click={publishPersona} />
|
||||
<Button variant="secondary" label="Nope" icon={Close} on:click={() => (state = 'text')} />
|
||||
{:else}
|
||||
<Button variant="secondary" label="Back" icon={Undo} on:click={() => (state = 'text')} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</InfoScreen>
|
||||
{:else}
|
||||
<InfoScreen title="Publish successful" onBack={() => history.back()}>
|
||||
<div>
|
||||
<h1>This persona is now public</h1>
|
||||
<InfoScreen title="Persona published">
|
||||
<div class="token-info">
|
||||
<div class="icon-success">
|
||||
<Checkmark />
|
||||
</div>
|
||||
<h2>This Persona is now public</h2>
|
||||
<p>
|
||||
Anyone can now submit posts with this persona. All posts will be subject to community review
|
||||
before being published. This persona was added to your favorites on your homepage.
|
||||
Anyone can now submit posts with this Persona. All posts will be subject to community review
|
||||
before being published. This Persona was added to your favorites.
|
||||
</p>
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
<LearnMore href="/" />
|
||||
</div>
|
||||
|
||||
<svelte:fragment slot="buttons">
|
||||
@ -171,4 +217,84 @@
|
||||
{/if}
|
||||
|
||||
<style lang="scss">
|
||||
.header {
|
||||
position: fixed;
|
||||
inset: -100% 0 auto;
|
||||
z-index: 50;
|
||||
transition: inset 0.5s;
|
||||
|
||||
&.scrolled {
|
||||
inset: 44px 0 auto;
|
||||
transition: inset 0.3s;
|
||||
}
|
||||
}
|
||||
|
||||
.token-info {
|
||||
text-align: center;
|
||||
|
||||
.icon {
|
||||
margin-bottom: var(--spacing-12);
|
||||
}
|
||||
|
||||
p,
|
||||
h2 {
|
||||
margin-bottom: var(--spacing-6);
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
border: 1px solid var(--grey-200);
|
||||
padding: var(--spacing-24);
|
||||
margin-top: var(--spacing-48);
|
||||
|
||||
.go-amt {
|
||||
font-size: 40px;
|
||||
line-height: 1;
|
||||
font-weight: var(--font-weight-sb);
|
||||
margin-top: var(--spacing-12);
|
||||
}
|
||||
|
||||
.go {
|
||||
text-transform: uppercase;
|
||||
font-weight: var(--font-weight-sb);
|
||||
margin-bottom: var(--spacing-12);
|
||||
}
|
||||
|
||||
&.error {
|
||||
border: 1px solid var(--color-red);
|
||||
background-color: rgba(var(--color-red-rgb), 0.05);
|
||||
|
||||
.go-amt,
|
||||
.go {
|
||||
color: var(--color-red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-success {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-bottom: var(--spacing-12);
|
||||
|
||||
:global(svg) {
|
||||
fill: var(--color-body-bg);
|
||||
}
|
||||
|
||||
:global(polygon) {
|
||||
stroke: #fff;
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
inset: -4px auto auto -6px;
|
||||
background-color: var(--color-success);
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
transform: translateX(2px);
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -3,10 +3,15 @@
|
||||
import Checkmark from '$lib/components/icons/checkmark.svelte'
|
||||
import Close from '$lib/components/icons/close.svelte'
|
||||
import Edit from '$lib/components/icons/edit.svelte'
|
||||
import Info from '$lib/components/icons/information.svelte'
|
||||
|
||||
import Button from '$lib/components/button.svelte'
|
||||
import PersonaEditText from '$lib/components/persona_edit_text.svelte'
|
||||
import PersonaDetail from '$lib/components/persona_detail.svelte'
|
||||
import LearnMore from '$lib/components/learn-more.svelte'
|
||||
import Banner from '$lib/components/message-banner.svelte'
|
||||
import Container from '$lib/components/container.svelte'
|
||||
import InfoBox from '$lib/components/info-box.svelte'
|
||||
|
||||
import { ROUTES } from '$lib/routes'
|
||||
import { goto } from '$app/navigation'
|
||||
@ -36,14 +41,14 @@
|
||||
</script>
|
||||
|
||||
{#if showWarningModal}
|
||||
<InfoScreen title="Leaving persona creation" onBack={() => (showWarningModal = false)}>
|
||||
<div>
|
||||
<h1>Are you sure you want to leave?</h1>
|
||||
<p>
|
||||
You are about to leave the persona creation screenWARNING: If you do so, all changes will be
|
||||
lost.
|
||||
</p>
|
||||
</div>
|
||||
<InfoScreen title="Leaving Persona creation" onBack={() => (showWarningModal = false)}>
|
||||
<Container>
|
||||
<InfoBox>
|
||||
<h2>Are you sure you want to leave?</h2>
|
||||
<p>You are about to leave the persona creation screen</p>
|
||||
<p>WARNING: If you do so, all changes will be lost.</p>
|
||||
</InfoBox>
|
||||
</Container>
|
||||
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button
|
||||
@ -64,9 +69,11 @@
|
||||
onSubmit={() => {
|
||||
state = 'edit_images'
|
||||
}}
|
||||
onBack={onCancel}
|
||||
{onCancel}
|
||||
/>
|
||||
{:else if state === 'edit_images'}
|
||||
<Banner icon={Info}>This is a preview of the Persona's page</Banner>
|
||||
<PersonaDetail
|
||||
name={persona.name}
|
||||
pitch={persona.pitch}
|
||||
@ -80,21 +87,24 @@
|
||||
>
|
||||
<Button
|
||||
slot="button_primary"
|
||||
variant="secondary"
|
||||
label="Edit text"
|
||||
icon={Edit}
|
||||
on:click={onCancel}
|
||||
/>
|
||||
<Button
|
||||
slot="button_other"
|
||||
variant="primary"
|
||||
label="Save changes"
|
||||
label="Save Persona"
|
||||
icon={Checkmark}
|
||||
disabled={!persona.picture || !persona.cover}
|
||||
on:click={savePersona}
|
||||
/>
|
||||
<p>Please provide at least a cover image.</p>
|
||||
<a href="/" target="_blank">Learn more →</a>
|
||||
<Button
|
||||
slot="button_other"
|
||||
variant="secondary"
|
||||
label="Edit Persona details"
|
||||
icon={Edit}
|
||||
on:click={() => (state = 'edit_text')}
|
||||
/>
|
||||
<Container>
|
||||
<InfoBox>
|
||||
<p>Please provide a profile picture and a cover image.</p>
|
||||
</InfoBox>
|
||||
</Container>
|
||||
</PersonaDetail>
|
||||
{:else}
|
||||
<InfoScreen title="All changes saved">
|
||||
@ -105,7 +115,7 @@
|
||||
“seed” posts. These posts should serve as inspiring examples for people willing to post with
|
||||
this persona.
|
||||
</p>
|
||||
<a href="/" target="_blank">Learn more</a>
|
||||
<LearnMore href="/" />
|
||||
</div>
|
||||
<svelte:fragment slot="buttons">
|
||||
<Button variant="secondary" label="Continue later" on:click={() => history.back()} />
|
||||
|
@ -1,15 +1,10 @@
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
/* color-scheme: light dark; */
|
||||
|
||||
--font-body: 'Source Sans Pro';
|
||||
--font-mono: 'Source Code Pro';
|
||||
--font-serif: 'Source Serif Pro';
|
||||
|
||||
--font-size-normal: 16px;
|
||||
--font-size-lg: 18px;
|
||||
--font-size-sm: 14px;
|
||||
--font-size-xs: 12px;
|
||||
|
||||
--font-weight-normal: 400;
|
||||
--font-weight-sb: 600;
|
||||
|
||||
@ -19,6 +14,10 @@
|
||||
--color-body-text-rgb: 24, 24, 24;
|
||||
--color-black: #000000;
|
||||
--color-black-rgb: 0, 0, 0;
|
||||
--color-red: #ff0000;
|
||||
--color-red-rgb: 255, 0, 0;
|
||||
|
||||
--color-success: #12bb10;
|
||||
|
||||
--grey-100: #f0f0f0;
|
||||
--grey-150: #f9f9f9;
|
||||
@ -38,12 +37,54 @@
|
||||
--spacing-24: 24px;
|
||||
--spacing-48: 48px;
|
||||
|
||||
--blur: 3px;
|
||||
|
||||
--font-size-xl: 18px;
|
||||
--font-size-lg: 16px;
|
||||
--font-size-normal: 14px;
|
||||
--font-size-sm: 12px;
|
||||
--font-size-xs: 12px;
|
||||
|
||||
--container-width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
h1:not(:empty),
|
||||
h2:not(:empty),
|
||||
h3:not(:empty),
|
||||
.h1:not(:empty),
|
||||
.h2:not(:empty),
|
||||
.h3:not(:empty) {
|
||||
font-family: var(--font-body);
|
||||
font-weight: var(--font-weight-sb);
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
h1:not(:empty),
|
||||
.h1:not(:empty) {
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
|
||||
h2:not(:empty),
|
||||
.h2:not(:empty) {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
h3:not(:empty),
|
||||
.h3:not(:empty) {
|
||||
font-size: var(--font-size-normal);
|
||||
}
|
||||
|
||||
.text-large {
|
||||
font-size: var(--font-size-lg);
|
||||
}
|
||||
|
||||
@media (min-width: 398px) {
|
||||
:root {
|
||||
--container-width: 1280px;
|
||||
--font-size-xl: 20px;
|
||||
--font-size-lg: 18px;
|
||||
--font-size-normal: 16px;
|
||||
--font-size-sm: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +104,8 @@ body {
|
||||
color: var(--color-body-text);
|
||||
background-color: var(--color-body-bg);
|
||||
font-family: var(--font-body);
|
||||
font-weight: 400;
|
||||
font-weight: var(--font-weight-normal);
|
||||
font-size: var(--font-size-normal);
|
||||
}
|
||||
|
||||
*,
|
||||
@ -80,7 +122,7 @@ body {
|
||||
|
||||
img,
|
||||
picture,
|
||||
svg,
|
||||
/* svg, */
|
||||
video {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
@ -89,11 +131,31 @@ video {
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
background-color: var(--grey-200);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
a {
|
||||
color: var(--color-black);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* THIS CLASS SHOULD BE REMOVED WHEN NO LONGER NEEDED */
|
||||
.note {
|
||||
background-color: palevioletred;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: var(--spacing-24);
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
:root {
|
||||
--container-width: 1280px;
|
||||
}
|
||||
}
|
||||
|
||||
/* @media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--success-highlight: #052b05;
|
||||
--color-body-bg: #000000;
|
||||
@ -108,4 +170,4 @@ hr {
|
||||
hr {
|
||||
background-color: var(--grey-500);
|
||||
}
|
||||
}
|
||||
} */
|
||||
|
Loading…
x
Reference in New Issue
Block a user