-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(login): add
<SLoginPage>
component
- Loading branch information
1 parent
a72e35a
commit b4da060
Showing
7 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
# SLoginPage | ||
|
||
`<SLoginPage>` is the component to render login page. | ||
|
||
## Usage | ||
|
||
You may use `<SLoginPage>` only for login page. | ||
|
||
```vue | ||
<script setup lang="ts"> | ||
import SLoginPage from '@globalbrain/sefirot/lib/components/SLoginPage.vue' | ||
</script> | ||
<template> | ||
<SLoginPage /> | ||
</template> | ||
``` | ||
|
||
## Props | ||
|
||
Here are the list of props you may pass to the component. | ||
|
||
### `:cover` | ||
|
||
This prop is the URL of cover image, which is used as background image. | ||
|
||
```ts | ||
interface Props { | ||
cover: string | ||
} | ||
``` | ||
|
||
### `:coverTitle` | ||
|
||
This prop is an object whose `text` is the title of the cover image and `link` is its link. | ||
|
||
```ts | ||
interface CoverTitle { | ||
text: string | ||
link: string | ||
} | ||
``` | ||
|
||
### `:coverPhotographer` | ||
|
||
This prop is an object whose `text` is the name of photographer of the cover image and `link` is its link. | ||
|
||
```ts | ||
interface CoverPhotographer { | ||
text: string | ||
link: string | ||
} | ||
``` | ||
|
||
### `:actions` | ||
|
||
This prop is an array of login buttons, | ||
where `type` is auth provider, `label` is to override default label, and `onClick` is a function to log in. | ||
|
||
```ts | ||
interface Action { | ||
type: 'google' | ||
label?: string | ||
onClick: () => Promise<void> | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
<script setup lang="ts"> | ||
import { computed } from 'vue' | ||
import SButton from './SButton.vue' | ||
import SLink from './SLink.vue' | ||
import SIconGbLogoWhite from './icon/SIconGbLogoWhite.vue' | ||
import SIconGoogle from './icon/SIconGoogle.vue' | ||
export interface CoverTitle { | ||
text: string | ||
link: string | ||
} | ||
export interface CoverPhotographer { | ||
text: string | ||
link: string | ||
} | ||
export interface Action { | ||
type: 'google' | ||
label?: string | ||
onClick: () => Promise<void> | ||
} | ||
const props = defineProps<{ | ||
cover: string | ||
coverTitle: CoverTitle | ||
coverPhotographer: CoverPhotographer | ||
actions: Action[] | ||
}>() | ||
const coverBgImageStyle = computed(() => props.cover ? `url(${props.cover})` : '') | ||
function getActionLabel(type: Action['type']) { | ||
switch (type) { | ||
case 'google': | ||
return 'Sign in via Google' | ||
default: | ||
throw new Error('Invalid action type') | ||
} | ||
} | ||
function getIconComponent(type: Action['type']) { | ||
switch (type) { | ||
case 'google': | ||
return SIconGoogle | ||
default: | ||
throw new Error('Invalid action type') | ||
} | ||
} | ||
</script> | ||
|
||
<template> | ||
<div class="SLoginPage"> | ||
<div class="cover"> | ||
<div class="cover-caption"> | ||
<p class="cover-caption-text"> | ||
<SLink class="cover-caption-link" :href="coverTitle.link"> | ||
{{ coverTitle.text }} | ||
</SLink> | ||
by | ||
<SLink class="cover-caption-link" :href="coverPhotographer.link"> | ||
{{ coverPhotographer.text }} | ||
</SLink> | ||
</p> | ||
</div> | ||
</div> | ||
<div class="form"> | ||
<div class="form-container"> | ||
<div class="form-logo"> | ||
<SIconGbLogoWhite class="form-logo-icon" /> | ||
</div> | ||
|
||
<div class="form-content"> | ||
<h1 class="form-title">Sign in to account</h1> | ||
<p class="form-lead">This is a very closed login form meant for specific audiences only. If you can’t login, well, you know who to ask.</p> | ||
</div> | ||
|
||
<div class="form-actions"> | ||
<SButton | ||
v-for="action in actions" | ||
:key="action.type" | ||
size="large" | ||
mode="white" | ||
rounded | ||
block | ||
:label="action.label || getActionLabel(action.type)" | ||
:icon="getIconComponent(action.type)" | ||
@click="action.onClick" | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<style scoped lang="postcss"> | ||
.SLoginPage { | ||
position: relative; | ||
display: grid; | ||
grid-template-columns: 1fr 480px; | ||
gap: 4px; | ||
background-color: var(--c-black); | ||
@media (min-width: 768px) { | ||
grid-template-columns: 1fr 392px; | ||
} | ||
@media (min-width: 1024px) { | ||
grid-template-columns: 1fr 480px; | ||
} | ||
} | ||
.cover { | ||
width: 100%; | ||
height: 100%; | ||
background-image: v-bind(coverBgImageStyle); | ||
background-position: 50% 50%; | ||
background-size: cover; | ||
background-repeat: no-repeat; | ||
@media (min-width: 768px) { | ||
display: block; | ||
} | ||
} | ||
.cover-caption { | ||
position: absolute; | ||
left: 0; | ||
bottom: 0; | ||
border-top: 4px solid var(--c-bg-elv-1); | ||
border-right: 4px solid var(--c-bg-elv-1); | ||
padding: 16px 24px; | ||
font-size: 12px; | ||
background-color: var(--c-bg-elv-2); | ||
@media (min-width: 768px) { | ||
display: block; | ||
} | ||
} | ||
.cover-caption-text { | ||
color: var(--c-text-2); | ||
} | ||
.cover-caption-link { | ||
color: var(--c-text-1); | ||
transition: color 0.25s; | ||
&:hover { | ||
color: var(--c-text-2); | ||
} | ||
} | ||
.form { | ||
padding: 96px 32px 48px; | ||
min-height: 100vh; | ||
background-color: var(--c-black-soft); | ||
@media (min-width: 768px) { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
padding: 48px; | ||
} | ||
} | ||
.form-container { | ||
@media (min-width: 768px) { | ||
margin-top: -96px; | ||
} | ||
} | ||
.form-logo { | ||
margin: 0 auto; | ||
width: 80px; | ||
} | ||
.form-content { | ||
padding-top: 64px; | ||
text-align: center; | ||
} | ||
.form-title { | ||
font-size: 20px; | ||
font-weight: 600; | ||
color: var(--c-text-dark-1); | ||
} | ||
.form-lead { | ||
margin: 0 auto; | ||
padding: 12px; | ||
max-width: 336px; | ||
font-size: 14px; | ||
color: var(--c-text-dark-2); | ||
} | ||
.form-actions { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
gap: 8px; | ||
max-width: 170px; | ||
padding-top: 24px; | ||
text-align: center; | ||
margin: 0 auto; | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<template> | ||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 192"> | ||
<polygon class="line" points="140.49 21.76 140.49 36.09 192 17.32 192 3 140.49 21.76 " /> | ||
<polygon class="line" points="42.93 159.04 0 174.68 0 189 42.93 173.36 42.93 159.04 " /> | ||
<path class="mark" d="M82.7,57v5.63a24.68,24.68,0,0,0-15.42-5.84c-14.66,0-28.11,11.55-28.11,36,0,27.18,12.22,38.32,27.43,38.32,6.33,0,11.49-2.55,16.06-6.22-.48,19.11-6.5,22.06-24.48,28.61v14.32c23.72-8.65,38.23-13.59,38.23-42.56V52ZM53.16,92.37c0-15.08,5.43-22.69,15.48-22.69,5.45,0,10.57,3.08,14.06,6.42V112c-3.57,3.44-8.29,6.21-13.52,6.21C58.86,118.19,53.16,110.31,53.16,92.37Z " /> | ||
<path class="mark" d="M143.47,57.57c-7.32,0-13.17,3.46-18.23,8.13V27.39l-13.58,4.94V129h13.58v-6.53a24.77,24.77,0,0,0,17.57,8c14.51,0,27.55-11.33,27.55-35.31C170.36,68.49,158.52,57.57,143.47,57.57Zm-2,60.22c-6.92,0-13.31-5.06-16.37-9.19V79.15c3.59-4.53,9.31-8.92,15.84-8.92,10.11,0,15.7,7.72,15.7,25.31C156.65,110.33,151.33,117.79,141.48,117.79Z " /> | ||
</svg> | ||
</template> | ||
|
||
<style scoped lang="postcss"> | ||
.line { fill: #979fa4; } | ||
.mark { fill: #ffffff; } | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<template> | ||
<svg | ||
viewBox="0 0 16 16" | ||
xmlns="http://www.w3.org/2000/svg" | ||
fill-rule="evenodd" | ||
clip-rule="evenodd" | ||
stroke-linejoin="round" | ||
stroke-miterlimit="1.414" | ||
> | ||
<path d="M8.16 6.857V9.6h4.537c-.183 1.177-1.37 3.45-4.537 3.45-2.73 0-4.96-2.26-4.96-5.05s2.23-5.05 4.96-5.05c1.554 0 2.594.66 3.19 1.233l2.17-2.092C12.126.79 10.32 0 8.16 0c-4.423 0-8 3.577-8 8s3.577 8 8 8c4.617 0 7.68-3.246 7.68-7.817 0-.526-.057-.926-.126-1.326H8.16z" /> | ||
</svg> | ||
</template> |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
<script setup lang="ts"> | ||
import SLoginPage from 'sefirot/components/SLoginPage.vue' | ||
import coverImage from '../_img/login-page-cover.jpg' | ||
const title = 'Components / SLoginPage / 01. Playground' | ||
const docs = '/components/login-page' | ||
function state() { | ||
const state: InstanceType<typeof SLoginPage>['$props'] = { | ||
cover: coverImage, | ||
coverTitle: { | ||
text: 'Golden Gate Bridge', | ||
link: 'https://unsplash.com/photos/bottom-view-of-orange-building-LjE32XEW01g' | ||
}, | ||
coverPhotographer: { | ||
text: 'Keegan Houser', | ||
link: 'https://unsplash.com/@khouser01' | ||
}, | ||
actions: [ | ||
{ type: 'google', onClick: async () => {} }, | ||
{ type: 'google', label: 'Sign in via G', onClick: async () => {} } | ||
] | ||
} | ||
return state | ||
} | ||
</script> | ||
|
||
<template> | ||
<Story :title="title" :init-state="state" source="Not available" auto-props-disabled> | ||
<template #controls="{ state }"> | ||
<HstText | ||
title="cover" | ||
v-model="state.cover" | ||
/> | ||
<HstJson | ||
title="coverTitle" | ||
v-model="state.coverTitle" | ||
/> | ||
<HstJson | ||
title="coverPhotographer" | ||
v-model="state.coverPhotographer" | ||
/> | ||
<HstJson | ||
title="actions" | ||
v-model="state.actions" | ||
/> | ||
</template> | ||
|
||
<template #default="{ state }"> | ||
<Board :title="title" :docs="docs"> | ||
<SLoginPage | ||
:cover="state.cover" | ||
:cover-title="state.coverTitle" | ||
:cover-photographer="state.coverPhotographer" | ||
:actions="state.actions" | ||
/> | ||
</Board> | ||
</template> | ||
</Story> | ||
</template> |