login
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Lit App</title>
|
||||
<script type="module" src="/src/my-element.ts"></script>
|
||||
<script type="module" src="/src/umb-app.ts"></script>
|
||||
</head>
|
||||
<body>
|
||||
<my-element></my-element>
|
||||
<body class="uui-font uui-text" style="margin: 0; padding: 0">
|
||||
<umb-app></umb-app>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
7207
src/Umbraco.Web.UI.Client/package-lock.json
generated
Normal file
7207
src/Umbraco.Web.UI.Client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
||||
"lint": "echo 'TODO: Implement lint'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@umbraco-ui/uui": "^0.2.0",
|
||||
"lit": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -18,10 +19,10 @@
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-lit": "^1.6.1",
|
||||
"eslint-plugin-lit-a11y": "^1.1.0-next.1",
|
||||
"msw": "^0.39.2",
|
||||
"prettier": "2.6.2",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.9.9",
|
||||
"msw": "^0.39.2"
|
||||
"vite": "^2.9.9"
|
||||
},
|
||||
"msw": {
|
||||
"workerDirectory": "public"
|
||||
|
||||
BIN
src/Umbraco.Web.UI.Client/public/login.jpeg
Normal file
BIN
src/Umbraco.Web.UI.Client/public/login.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
1
src/Umbraco.Web.UI.Client/public/umbraco_logo_white.svg
Normal file
1
src/Umbraco.Web.UI.Client/public/umbraco_logo_white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1180 316"><path d="M.2 157.8C.3 70.6 71.1-.1 158.3.1S316.2 71 316.1 158.2s-70.8 157.7-157.9 157.7C70.8 315.9.1 245.1.2 157.8zm154.7 54.1c-12.3.4-24.5-.7-36.5-3.3-8.8-1.8-16.3-7.8-19.9-16-3.6-8.2-5.3-20.9-5.2-38.1.1-9 .6-17.9 1.7-26.8 1-8.7 2.1-15.8 3.1-21.5l1.1-5.6v-.5c0-1.6-1.1-2.9-2.6-3.2l-20.4-3.2h-.4c-1.5 0-2.8 1-3.1 2.5-.3 1.3-.6 2.3-1.2 5.4-1.2 6-2.2 11.8-3.4 20.4-1.3 9.3-2 18.6-2.3 27.9-.4 6.5-.4 13 0 19.6.5 17.3 3.4 31.1 8.9 41.4 5.5 10.3 14.7 17.8 27.7 22.3s31.2 6.8 54.4 6.7h2.9c23.3.1 41.4-2.1 54.4-6.7 13-4.5 22.2-12 27.7-22.3s8.4-24.1 8.9-41.4c.4-6.5.4-13 0-19.6-.3-9.3-1-18.7-2.3-27.9-1.2-8.4-2.3-14.3-3.4-20.4-.6-3.1-.8-4.1-1.2-5.4-.3-1.4-1.6-2.5-3.1-2.5h-.5l-20.4 3.2c-1.6.3-2.7 1.6-2.7 3.2v.5l1.1 5.6c1 5.6 2.1 12.8 3.1 21.5 1 8.9 1.6 17.9 1.7 26.8.2 17.1-1.6 29.8-5.2 38.1-3.6 8.2-11 14.2-19.8 16.1-12 2.5-24.2 3.6-36.5 3.3l-6.6-.1zm932.3-43.9c0-30.4 8.6-51.7 43.8-51.7s43.8 21.3 43.8 51.7-8.6 51.7-43.8 51.7-43.8-21.3-43.8-51.7zm65.3 0c0-21.1-2.7-33.1-21.5-33.1s-21.5 12-21.5 33.1 2.8 33.1 21.5 33.1c18.8 0 21.5-12 21.5-33.1zm-672.1 47.8c.5.9 1.5 1.5 2.5 1.4h8.2c1.6 0 2.9-1.3 2.9-2.9v-92.7c0-1.6-1.3-2.9-2.9-2.9h-16.3c-1.6 0-2.9 1.3-2.9 2.9v73.6c-7 3.9-14.9 5.9-22.8 5.8-10.4 0-15.6-4.5-15.6-14.6v-64.8c0-1.6-1.3-2.9-2.9-2.9h-16.4c-1.6 0-2.9 1.3-2.9 2.9v66.7c0 18.9 8.9 31.3 33.9 31.3 11.4-.1 22.6-3.7 32-10.2l2.9 6.5.3-.1zm184.1-68.1c0-18.7-9.3-31.4-32.6-31.4-11.3 0-22.3 3.4-31.6 9.8-4.1-6.1-12-9.8-25.3-9.8-10.7.2-21.1 3.8-29.7 10.2l-2.9-6.5c-.5-.9-1.5-1.5-2.5-1.4h-8.3c-1.6 0-2.9 1.3-2.9 2.9v92.8c0 1.6 1.3 2.9 2.9 2.9h16.3c1.6 0 2.9-1.3 2.9-2.9v-73.5c6.2-3.8 13.4-5.8 20.7-5.8 8.9 0 14 3.3 14 12.6v66.7c0 1.6 1.3 2.9 2.9 2.9h16.3c1.6 0 2.9-1.3 2.9-2.9v-73.6c6.2-3.9 13.4-5.9 20.7-5.8 8.6 0 14 3.3 14 12.6v66.7c0 1.6 1.3 2.9 2.9 2.9h16.3c1.6 0 2.9-1.3 2.9-2.9l.1-66.5zm50.4 61.7c9.3 6.9 20.5 10.5 32 10.2 28.8 0 39.4-19.3 39.4-51.7s-10.7-51.7-39.4-51.7c-9.4 0-18.5 2.7-26.4 7.7V94.2c0-1.6-1.2-2.9-2.8-3h-16.6c-1.6 0-2.9 1.3-2.9 2.9v120.3c0 1.6 1.3 2.9 2.9 2.9h8.2c1 0 2-.5 2.5-1.4l3.1-6.5zm26.8-8.5c-7.5 0-14.8-2-21.3-5.8v-54.4c6.5-3.8 13.8-5.8 21.3-5.8 19.3 0 22.3 14.8 22.3 32.9s-2.8 33.1-22.3 33.1zM868 135.7c-2.5-.3-5.1-.5-7.7-.5-8.8-.4-17.5 1.7-25.3 5.9v73.2c0 1.6-1.3 2.9-2.9 2.9h-16.3c-1.6 0-2.9-1.3-2.9-2.9v-92.7c0-1.6 1.3-2.9 2.9-2.9h8.2c1 0 2 .5 2.5 1.4l2.9 6.5c8.9-6.8 19.9-10.4 31.2-10.2 2.6 0 5.2.2 7.7.6 1.4 0 2.7 2.4 2.7 4v11.8c0 1.6-1.3 2.9-2.9 2.9h-.2m56.7 36.1c-9.8 1.2-15.6 4.9-15.6 15.2 0 7.5 3.3 14.6 15.2 14.6 7.5.1 14.9-2.2 21.1-6.5v-25.5l-20.7 2.2zm26.1 37.6c-8.5 6.7-19 10.3-29.8 10.2-25.5 0-33.9-15.8-33.9-31.6 0-21.3 13.8-30.4 36.1-32.1l22.1-1.8v-4.9c0-10.1-4.7-14-19.3-14-9.2 0-18.3 1.5-26.9 4.5h-.9c-1.6 0-2.9-1.3-2.9-2.9v-13.1c0-1.2.7-2.4 1.9-2.8 9.8-3.3 20.1-5 30.5-4.9 32.3 0 39.8 14.2 39.8 35.1V214c0 1.6-1.3 2.9-2.9 2.9h-8.2c-1 0-2-.5-2.5-1.4l-3.1-6.1zM1063 197h.9c1.6 0 2.9 1.3 2.9 2.9V213c0 1.2-.7 2.3-1.8 2.7-8.1 2.9-16.7 4.3-25.4 4.1-34.9 0-45.7-20.9-45.7-51.7s10.7-51.7 45.7-51.7c8.6-.2 17.1 1.1 25.2 4 1.1.4 1.9 1.5 1.8 2.7v13.1c0 1.6-1.3 2.9-2.9 2.9h-.9c-7.1-2.2-14.6-3.3-22-3.1-19.1 0-24.7 13.1-24.7 32.2s5.5 32.1 24.7 32.1c7.5.1 14.9-1 22-3.3" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 315.89 315.89"><path fill="#fff" d="M0 157.74a157.95 157.95 0 11158 158.15A157.95 157.95 0 010 157.74zm154.74 54.09a155.41 155.41 0 01-36.5-3.29 27.92 27.92 0 01-19.94-16q-5.35-12.34-5.21-38.1a243 243 0 011.69-26.84q1.55-13 3.09-21.46l1.07-5.59a2 2 0 000-.49 3.2 3.2 0 00-2.65-3.17l-20.37-3.22h-.44a3.19 3.19 0 00-3.11 2.48c-.35 1.31-.56 2.27-1.17 5.38-1.16 6-2.24 11.85-3.43 20.38a264.17 264.17 0 00-2.3 27.94 145.24 145.24 0 000 19.57q.72 25.94 8.9 41.42t27.72 22.3q19.53 6.81 54.43 6.66h2.91q34.94.15 54.41-6.66t27.71-22.3q8.17-15.53 8.91-41.42a145.24 145.24 0 000-19.57 266.84 266.84 0 00-2.3-27.94c-1.2-8.44-2.27-14.26-3.44-20.38-.61-3.11-.81-4.07-1.16-5.38a3.21 3.21 0 00-3.12-2.48h-.52l-20.38 3.18a3.2 3.2 0 00-2.68 3.17 4 4 0 000 .49l1.08 5.59q1.55 8.48 3.12 21.46a245.68 245.68 0 011.65 26.84q.27 25.69-5.21 38.07a27.9 27.9 0 01-19.76 16.07 155.19 155.19 0 01-36.48 3.29z"/></svg>
|
||||
|
After Width: | Height: | Size: 942 B |
117
src/Umbraco.Web.UI.Client/src/auth/login/umb-login.element.ts
Normal file
117
src/Umbraco.Web.UI.Client/src/auth/login/umb-login.element.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { html, css, LitElement, CSSResultGroup } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css';
|
||||
import { ifDefined } from 'lit/directives/if-defined.js';
|
||||
|
||||
// create custom element with lit-element named 'umb-login'
|
||||
|
||||
@customElement('umb-login')
|
||||
export class UmbLogin extends LitElement {
|
||||
static styles: CSSResultGroup = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
#email,
|
||||
#password {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
private _loggingIn = false;
|
||||
|
||||
private _handleSubmit = (e: SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target as HTMLFormElement;
|
||||
if (!form) return;
|
||||
|
||||
const isValid = form.checkValidity();
|
||||
if (!isValid) return;
|
||||
|
||||
const formData = new FormData(form);
|
||||
|
||||
const username = formData.get('email') as string;
|
||||
const password = formData.get('password') as string;
|
||||
const persist = formData.has('persist');
|
||||
|
||||
this._login(username, password, persist);
|
||||
};
|
||||
|
||||
private async _login(username: string, password: string, persist: boolean) {
|
||||
console.log('LOGIN', username, password, persist);
|
||||
this._loggingIn = true;
|
||||
|
||||
try {
|
||||
await fetch('/login', { method: 'POST' });
|
||||
this._loggingIn = false;
|
||||
// TODO: Change to redirect when router has been added.
|
||||
this.dispatchEvent(new CustomEvent('login', { detail: { username, password, persist } }));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
this._loggingIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _greetings: Array<string> = [
|
||||
'Happy super Sunday',
|
||||
'Happy manic Monday',
|
||||
'Happy tubular Tuesday',
|
||||
'Happy wonderful Wednesday',
|
||||
'Happy thunderous Thursday',
|
||||
'Happy funky Friday',
|
||||
'Happy Caturday',
|
||||
];
|
||||
|
||||
@state()
|
||||
private _greeting: string = this._greetings[new Date().getDay()];
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="uui-text">
|
||||
<h1 class="uui-h3">${this._greeting}</h1>
|
||||
<uui-form>
|
||||
<form id="LoginForm" name="login" @submit="${this._handleSubmit}">
|
||||
<uui-form-layout-item>
|
||||
<uui-label for="email" slot="label" required>Email</uui-label>
|
||||
<uui-input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="Enter your email..."
|
||||
required
|
||||
required-message="Email is required"></uui-input>
|
||||
</uui-form-layout-item>
|
||||
|
||||
<uui-form-layout-item>
|
||||
<uui-label for="password" slot="label" required>Password</uui-label>
|
||||
<uui-input-password
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="Enter your password..."
|
||||
required
|
||||
required-message="Password is required"></uui-input-password>
|
||||
</uui-form-layout-item>
|
||||
|
||||
<uui-form-layout-item>
|
||||
<uui-checkbox name="persist" label="Remember me"> Remember me </uui-checkbox>
|
||||
</uui-form-layout-item>
|
||||
|
||||
<uui-button
|
||||
type="submit"
|
||||
label="Login"
|
||||
look="positive"
|
||||
state=${ifDefined(this._loggingIn ? 'waiting' : undefined)}></uui-button>
|
||||
<uui-button type="button" label="Forgot Password?"></uui-button>
|
||||
</form>
|
||||
</uui-form>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-login': UmbLogin;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
|
||||
@customElement('umb-auth-layout')
|
||||
export class UmbAuthLayout extends LitElement {
|
||||
static styles: CSSResultGroup = [
|
||||
css`
|
||||
#background {
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
background-position: 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
background-image: url('/login.jpeg');
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: fixed;
|
||||
top: var(--uui-size-space-5);
|
||||
left: var(--uui-size-space-5);
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#logo img {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
#box {
|
||||
width: 500px;
|
||||
padding: var(--uui-size-space-6) var(--uui-size-space-5) var(--uui-size-space-5) var(--uui-size-space-5);
|
||||
}
|
||||
|
||||
#email,
|
||||
#password {
|
||||
width: 100%;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div id="background"></div>
|
||||
|
||||
<div id="logo">
|
||||
<img src="/umbraco_logo_white.svg" alt="Umbraco" />
|
||||
</div>
|
||||
|
||||
<div id="container">
|
||||
<uui-box id="box">
|
||||
<slot></slot>
|
||||
</uui-box>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-auth-layout': UmbAuthLayout;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
import { html, css, LitElement } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { worker } from './mocks/browser';
|
||||
|
||||
/**
|
||||
* An example element.
|
||||
*
|
||||
* @slot - This element has a slot
|
||||
* @csspart button - The button
|
||||
*/
|
||||
@customElement('my-element')
|
||||
export class MyElement extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
border: solid 1px gray;
|
||||
padding: 16px;
|
||||
max-width: 800px;
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* The name to say "Hello" to.
|
||||
*/
|
||||
@property()
|
||||
name = 'World';
|
||||
|
||||
/**
|
||||
* The number of times the button has been clicked.
|
||||
*/
|
||||
@property({ type: Number })
|
||||
count = 0;
|
||||
|
||||
@state()
|
||||
_authorized = false;
|
||||
|
||||
@state()
|
||||
_user: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private async _onLogin() {
|
||||
try {
|
||||
await fetch('/login', { method: 'POST' });
|
||||
this._authorized = true;
|
||||
this._getUser();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private _onLogout() {
|
||||
try {
|
||||
fetch('/logout', { method: 'POST' });
|
||||
this._authorized = false;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async _getUser() {
|
||||
try {
|
||||
const res = await fetch('/user');
|
||||
this._user = await res.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<h1>Hello, ${this.name}!</h1>
|
||||
<button @click=${this._onClick} part="button">Click Count: ${this.count}</button>
|
||||
|
||||
<h2>Login Here</h2>
|
||||
${this._authorized
|
||||
? html`<button @click=${this._onLogout}>Logout</button>`
|
||||
: html`<button @click=${this._onLogin}>Login</button>`}
|
||||
${this._authorized && this._user
|
||||
? html`
|
||||
<h3>User information</h3>
|
||||
<div>Username: ${this._user.username}</div>
|
||||
`
|
||||
: ''}
|
||||
|
||||
<slot></slot>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onClick() {
|
||||
this.count++;
|
||||
fetch('/login', { method: 'POST' });
|
||||
}
|
||||
|
||||
foo(): string {
|
||||
return 'foo';
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'my-element': MyElement;
|
||||
}
|
||||
}
|
||||
70
src/Umbraco.Web.UI.Client/src/umb-app.ts
Normal file
70
src/Umbraco.Web.UI.Client/src/umb-app.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import '@umbraco-ui/uui-css/dist/uui-css.css';
|
||||
import { html, css, LitElement } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { worker } from './mocks/browser';
|
||||
|
||||
// Import somewhere else?
|
||||
import '@umbraco-ui/uui';
|
||||
|
||||
import './auth/login/umb-login.element';
|
||||
import './auth/umb-auth-layout.element';
|
||||
|
||||
@customElement('umb-app')
|
||||
export class UmbApp extends LitElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
`;
|
||||
|
||||
@state()
|
||||
_authorized = false;
|
||||
|
||||
@state()
|
||||
_user: any;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
worker.start();
|
||||
}
|
||||
|
||||
private _onLogout() {
|
||||
try {
|
||||
fetch('/logout', { method: 'POST' });
|
||||
this._authorized = false;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async _getUser() {
|
||||
try {
|
||||
const res = await fetch('/user');
|
||||
this._user = await res.json();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleLogin = () => {
|
||||
this._authorized = true;
|
||||
this._getUser();
|
||||
};
|
||||
|
||||
render() {
|
||||
return html`
|
||||
${!this._authorized
|
||||
? html`<umb-auth-layout>
|
||||
<umb-login @login=${this._handleLogin}></umb-login>
|
||||
</umb-auth-layout>`
|
||||
: html`hej`}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
'umb-app': UmbApp;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user