feat: side panels

Signed-off-by: Mitch Hijlkema <mitch@hijlkema.codes>
This commit is contained in:
2023-08-31 17:03:27 +02:00
parent 14c1b97622
commit 41830bbca5
15 changed files with 1525 additions and 1055 deletions

View File

@@ -1,4 +1,5 @@
import { ReactiveController, ReactiveControllerHost } from 'lit';
import { Subject } from 'rxjs';
export class DestroyController implements ReactiveController {
@@ -16,12 +17,13 @@ export class DestroyController implements ReactiveController {
(this._host = host).addController(this);
}
/** Public methods */
public hostDisconnected() {
this.destroy$.next();
this.destroy$.complete();
}
/** Public methods */
/** Protected methods */
/** Private methods */

View File

@@ -1,21 +1,38 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"extends": [
"../../../.eslintrc.json"
],
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"files": [
"*.ts",
"*.tsx",
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"files": [
"*.ts",
"*.tsx"
],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"files": [
"*.js",
"*.jsx"
],
"rules": {}
},
{
"files": ["*.json"],
"files": [
"*.json"
],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error"

View File

@@ -0,0 +1,16 @@
import type { StorybookConfig } from '@storybook/web-components-vite';
const config: StorybookConfig = {
stories: ['../src/lib/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
framework: {
name: '@storybook/web-components-vite',
options: {},
},
};
export default config;
// To customize your webpack configuration you can use the webpackFinal field.
// Check https://storybook.js.org/docs/react/builders/webpack#extending-storybooks-webpack-config
// and https://nx.dev/packages/storybook/documents/custom-builder-configs

View File

@@ -6,17 +6,23 @@
"targets": {
"build": {
"executor": "@nx/js:tsc",
"outputs": ["{options.outputPath}"],
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/libs/panels/bottom-panel",
"main": "libs/panels/bottom-panel/src/index.ts",
"tsConfig": "libs/panels/bottom-panel/tsconfig.lib.json",
"assets": ["libs/panels/bottom-panel/*.md"]
"assets": [
"libs/panels/bottom-panel/*.md"
]
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"outputs": [
"{options.outputFile}"
],
"options": {
"lintFilePatterns": [
"libs/panels/bottom-panel/**/*.ts",
@@ -26,11 +32,46 @@
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["coverage/libs/panels/bottom-panel"],
"outputs": [
"coverage/libs/panels/bottom-panel"
],
"options": {
"passWithNoTests": true,
"reportsDirectory": "../../../coverage/libs/panels/bottom-panel"
}
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {
"port": 4400,
"configDir": "libs/panels/bottom-panel/.storybook"
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-storybook": {
"executor": "@nx/storybook:build",
"outputs": [
"{options.outputDir}"
],
"options": {
"outputDir": "dist/storybook/panels-bottom-panel",
"configDir": "libs/panels/bottom-panel/.storybook"
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"test-storybook": {
"executor": "nx:run-commands",
"options": {
"command": "test-storybook -c libs/panels/bottom-panel/.storybook --url=http://localhost:4400"
}
}
},
"tags": []

View File

@@ -0,0 +1,100 @@
import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import './bottom-panel.component';
import BottomPanel from './bottom-panel.component';
export default {
title: 'Design System/Atoms/Panels/Bottom',
component: 'z-panels.bottom',
render: (args) => html`
<style>
html {
background-color: #ccc;
}
</style>
<z-panels.bottom ?open=${args.open}>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
<p>
Esse laborum pariatur irure in consequat deserunt officia. Eu aliquip ad duis. Voluptate velit fugiat ex Lorem et. Ea laborum ut
veniam est ad consequat pariatur.
</p>
</z-panels.bottom>
`,
parameters: {
layout: 'fullscreen',
},
argTypes: {
open: {
control: 'boolean',
},
},
args: {
open: false,
},
} satisfies Meta<BottomPanel>;
type Story = StoryObj<BottomPanel>;
export const Primary: Story = {};

View File

@@ -0,0 +1,58 @@
import { DestroyController } from '@z-elements/_internal/controllers';
import { LitElement, PropertyValues, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { filter, fromEvent, takeUntil, tap } from 'rxjs';
import { hostStyles, panelStyles } from './styles';
@customElement('z-panels.bottom')
export default class BottomPanel extends LitElement {
static override get styles() {
return [hostStyles, panelStyles];
}
/** Private variables */
private readonly _destroyController = new DestroyController(this);
/** Protected variables */
/** Public variables */
@property({ type: Boolean, reflect: true }) public open = false;
/** constructor & lifecylce */
override connectedCallback() {
super.connectedCallback();
fromEvent<MouseEvent>(this, 'click')
.pipe(
filter(() => this.open),
filter((event: MouseEvent) => event.target === this),
tap(() => (this.open = false)),
takeUntil(this._destroyController.destroy),
)
.subscribe();
}
override updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
this.shadowRoot?.querySelector('aside')?.scrollTo(0, 0);
}
/** Public methods */
/** Protected methods */
protected override render(): unknown {
return html`
<aside>
<header><slot name="header"></slot></header>
<slot></slot>
</aside>
`;
}
/** Private methods */
}

View File

@@ -1,7 +0,0 @@
import { panelsBottomPanel } from './panels-bottom-panel';
describe('panelsBottomPanel', () => {
it('should work', () => {
expect(panelsBottomPanel()).toEqual('panels-bottom-panel');
});
});

View File

@@ -1,3 +0,0 @@
export function panelsBottomPanel(): string {
return 'panels-bottom-panel';
}

View File

@@ -0,0 +1,51 @@
import { css } from 'lit';
export const hostStyles = css`
:host {
display: block;
overflow: hidden;
position: fixed;
inset: 0;
pointer-events: none;
z-index: ${Number.MAX_SAFE_INTEGER};
}
:host([open]) {
pointer-events: auto;
}
:host(:not([open])) aside {
transform: translateY(100vh);
}
:host([open]) aside {
transform: translateY(0);
transition-timing-function: ease-in;
}
:host(:not([open])) aside::before {
opacity: 0;
}
:host([open]) aside {
opacity: 1;
transition-timing-function: ease-in;
}
`;
export const panelStyles = css`
aside {
position: absolute;
background-color: #fff;
box-shadow: 0 -3px 6px rgba(0, 0, 0, 0.04);
border-radius: 12px 12px 0 0;
padding: 2rem 1.5rem;
inset-inline: 0;
bottom: 0;
max-height: 60dvh;
pointer-events: auto;
transition: transform 0.3s ease-out;
overflow: scroll;
scroll-behavior: smooth;
}
`;

View File

@@ -8,7 +8,9 @@
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"types": ["vitest"]
"types": [
"vitest"
]
},
"files": [],
"include": [],
@@ -18,6 +20,9 @@
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./tsconfig.storybook.json"
}
]
}

View File

@@ -3,8 +3,18 @@
"compilerOptions": {
"outDir": "../../../dist/out-tsc",
"declaration": true,
"types": ["node"]
"types": [
"node"
]
},
"include": ["src/**/*.ts"],
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
"include": [
"src/**/*.ts"
],
"exclude": [
"jest.config.ts",
"src/**/*.spec.ts",
"src/**/*.test.ts",
"**/*.stories.ts",
"**/*.stories.js"
]
}

View File

@@ -0,0 +1,19 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": true
},
"exclude": [
"src/**/*.spec.ts",
"src/**/*.test.ts"
],
"include": [
"src/**/*.stories.ts",
"src/**/*.stories.js",
"src/**/*.stories.jsx",
"src/**/*.stories.tsx",
"src/**/*.stories.mdx",
".storybook/*.js",
".storybook/*.ts"
]
}

View File

@@ -27,15 +27,16 @@
"@storybook/test-runner": "^0.13.0",
"@storybook/testing-library": "~0.2.0",
"@storybook/web-components": "^7.3.2",
"@storybook/web-components-vite": "^7",
"@storybook/web-components-vite": "^7.3.2",
"@storybook/web-components-webpack5": "7.3.2",
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/jest": "^29.5.4",
"@types/node": "20.5.6",
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"@vitest/coverage-c8": "~0.32.0",
"@vitest/coverage-c8": "~0.32.4",
"@vitest/ui": "~0.34.3",
"eslint": "~8.47.0",
"eslint": "~8.48.0",
"eslint-config-prettier": "9.0.0",
"jest": "^29.6.4",
"jest-environment-node": "^29.6.4",

2214
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff