Skip to content

Commit 8bdc6e7

Browse files
committed
docs: make page for full stories
1 parent 739cf33 commit 8bdc6e7

File tree

8 files changed

+381
-28
lines changed

8 files changed

+381
-28
lines changed

catalog/eleventy-helpers/shortcodes/playground-example.cjs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function playgroundExample(eleventyConfig) {
5151
<md-icon aria-hidden="true">expand_more</md-icon>
5252
<md-icon aria-hidden="true" slot="selected">expand_less</md-icon>
5353
</md-outlined-icon-button>
54-
Expand interactive demo.
54+
View interactive demo inline.
5555
</summary>
5656
<lit-island on:visible import="/js/hydration-entrypoints/playground-elements.js" class="example" aria-hidden="true">
5757
<playground-project
@@ -68,6 +68,7 @@ function playgroundExample(eleventyConfig) {
6868
><md-circular-progress indeterminate></md-circular-progress></playground-file-editor>
6969
</lit-island>
7070
</details>
71+
<p><a href="./stories/" target="_blank">Open interactive demo in new tab.</a></p>
7172
`;
7273
});
7374
}

catalog/site/css/stories.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#dragbar {
2+
max-width: 100%;
3+
max-height: 100%;
4+
}
5+
6+
#editor {
7+
margin-block: 0;
8+
height: 100%;
9+
box-sizing: border-box;
10+
}
11+
12+
#editor-wrapper {
13+
height: 100%;
14+
overflow: hidden;
15+
}
16+
17+
body {
18+
height: 100dvh;
19+
}
20+
21+
#preview {
22+
position: relative;
23+
}
24+
25+
#preview md-circular-progress {
26+
inset: 50%;
27+
transform: translate(-50%, -50%);
28+
}

catalog/site/css/syntax-highlight.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050

5151
/* Formats the code boxes themselves */
5252
.example playground-file-editor,
53+
playground-file-editor,
5354
pre[class*='language-'] {
5455
padding: var(--__code-block-font-size);
5556
/* Remove the extra hard coded 3px from line number padding. */

catalog/site/stories/stories.html

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
---js
2+
{
3+
pagination: {
4+
data: "collections.component",
5+
size: 1,
6+
alias: "component",
7+
before: components => {
8+
// remove any components that don't have a dirname
9+
return components.filter(component => component.data.dirname)
10+
}
11+
},
12+
permalink: "components/{{component.data.page.fileSlug}}/stories/index.html",
13+
fullHeightContent: "true",
14+
collections: ["stories"],
15+
eleventyComputed: {
16+
dirname: ({component}) => component.data.dirname,
17+
}
18+
}
19+
---
20+
21+
<!DOCTYPE html>
22+
<html lang="en">
23+
<head>
24+
<meta charset="utf-8" />
25+
<meta
26+
name="viewport"
27+
content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"
28+
/>
29+
<title>Material Web - Stories {{component.data.name}}</title>
30+
<!-- Set the color of the url bar on mobile to match theme -->
31+
<meta name="theme-color" content="#251f16" />
32+
<link
33+
href="/images/favicon.svg"
34+
rel="icon"
35+
sizes="any"
36+
type="image/svg+xml"
37+
/>
38+
<!-- Inlines the global css in site/css/global.css -->
39+
{% inlinecss "global.css" %}
40+
<!-- MUST be loaded before any lit bundle. allows hydration of SSRd components -->
41+
<script type="module" src="/js/ssr-utils/lit-hydrate-support.js"></script>
42+
<!-- Inlines the material theming logic since we want to prevent FOUC -->
43+
{% inlinejs "inline/apply-saved-theme.js" %}
44+
<!-- Needed for intializing theme if this is the first page they ever visit -->
45+
<script type="module" src="/js/pages/global.js"></script>
46+
<noscript>
47+
<link rel="stylesheet" href="/css/no-js.css" />
48+
</noscript>
49+
<link
50+
rel="stylesheet"
51+
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&display=swap"
52+
/>
53+
<!-- If JS is disabled just show the contents without the polyfill -->
54+
<noscript
55+
><style>
56+
body[dsd-pending] {
57+
display: block !important;
58+
}
59+
</style>
60+
</noscript>
61+
<!-- Syncs theme with playground -->
62+
<script type="module" src="/js/pages/components.js"></script>
63+
<script type="module" src="/js/pages/stories.js"></script>
64+
<script
65+
type="module"
66+
src="/js/hydration-entrypoints/playground-elements.js"
67+
></script>
68+
<link rel="stylesheet" href="/css/syntax-highlight.css" />
69+
<link rel="stylesheet" href="/css/stories.css" />
70+
</head>
71+
<!-- dsd-pending hides body until the polyfill has run on browsers that do not support DSD -->
72+
<body dsd-pending>
73+
<!-- Inlines the declarative shadow dom polyfill for FF since it's performance sensitive -->
74+
{% inlinejs "ssr-utils/dsd-polyfill.js" %}
75+
<playground-project
76+
id="project"
77+
project-src="/assets/stories/{{dirname}}/project.json"
78+
>
79+
</playground-project>
80+
<drag-playground id="dragbar">
81+
<playground-preview id="preview" project="project" slot="preview">
82+
<md-circular-progress indeterminate></md-circular-progress>
83+
</playground-preview>
84+
<div slot="editor" id="editor-wrapper">
85+
<playground-file-editor
86+
id="editor"
87+
project="project"
88+
filename="stories.ts"
89+
line-numbers
90+
aria-hidden="true"
91+
>
92+
<md-circular-progress indeterminate></md-circular-progress>
93+
</playground-file-editor>
94+
</div>
95+
</drag-playground>
96+
</body>
97+
</html>
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import { LitElement, css, html } from 'lit';
2+
import { customElement, state, query } from 'lit/decorators.js';
3+
import { classMap } from 'lit/directives/class-map.js';
4+
import { styleMap } from 'lit/directives/style-map.js';
5+
import '@material/web/icon/icon.js';
6+
7+
/**
8+
* A playground preview + editor with a draggable handle.
9+
*/
10+
@customElement('drag-playground')
11+
export class DragPlayground extends LitElement {
12+
static styles = css`
13+
:host {
14+
display: block;
15+
--_drag-bar-height: 24px;
16+
--_drag-bar-border-width: 1px;
17+
--_half-drag-bar-height: calc(
18+
(var(--_drag-bar-height) / 2) + var(--_drag-bar-border-width)
19+
);
20+
}
21+
22+
#wrapper {
23+
display: flex;
24+
flex-direction: column;
25+
}
26+
27+
:host,
28+
#wrapper,
29+
::slotted(*) {
30+
height: 100%;
31+
}
32+
33+
slot {
34+
display: block;
35+
overflow: hidden;
36+
}
37+
38+
[name='preview'] {
39+
height: max(
40+
calc(
41+
100% - var(--editor-percentage, 0%) - var(--_half-drag-bar-height)
42+
),
43+
0px
44+
);
45+
}
46+
47+
[name='editor'] {
48+
height: max(
49+
calc(var(--editor-percentage, 0px) - var(--_half-drag-bar-height)),
50+
0px
51+
);
52+
}
53+
54+
#drag-bar {
55+
touch-action: none;
56+
background-color: var(--md-sys-color-surface-container);
57+
color: var(--md-sys-color-on-surface);
58+
border: var(--_drag-bar-border-width) solid var(--md-sys-color-outline);
59+
border-radius: 12px;
60+
height: var(--_drag-bar-height);
61+
display: flex;
62+
justify-content: center;
63+
align-items: center;
64+
-webkit-user-select: none;
65+
user-select: none;
66+
}
67+
68+
#drag-bar:hover {
69+
background-color: var(--md-sys-color-surface-container-high);
70+
cursor: grab;
71+
}
72+
73+
#drag-bar.isDragging {
74+
background-color: var(--md-sys-color-inverse-surface);
75+
color: var(--md-sys-color-inverse-on-surface);
76+
cursor: grabbing;
77+
}
78+
`;
79+
80+
/**
81+
* Whether or not we are in the "dragging" state.
82+
*/
83+
@state() private isDragging = false;
84+
85+
/**
86+
* The percentage of the editor height.
87+
*/
88+
@state() private editorHeightPercent = 0;
89+
90+
@query('#wrapper') private wrapperEl!: HTMLElement;
91+
92+
/**
93+
* A set of pointer IDs in the case that the user is dragging with multiple
94+
* pointers.
95+
*/
96+
private pointerIds: Set<number> = new Set();
97+
98+
render() {
99+
return html`<div
100+
id="wrapper"
101+
style=${styleMap({
102+
'--editor-percentage': `${this.editorHeightPercent}%`,
103+
})}
104+
>
105+
<slot name="preview"></slot>
106+
<div
107+
id="drag-bar"
108+
tabindex="0"
109+
role="slider"
110+
aria-orientation="vertical"
111+
aria-valuemax="100"
112+
aria-valuemin="0"
113+
aria-valuenow="${this.editorHeightPercent}"
114+
aria-valuetext="${this.editorHeightPercent} percent"
115+
aria-label="Editor height"
116+
@focus=${this.onFocus}
117+
@blur=${this.onBlur}
118+
@keydown=${this.onKeydown}
119+
@pointerdown=${this.onPointerdown}
120+
@pointerup=${this.onPointerup}
121+
@pointermove=${this.onPointermove}
122+
class=${classMap({
123+
isDragging: this.isDragging,
124+
})}
125+
>
126+
<md-icon aria-hidden="true">drag_handle</md-icon>
127+
</div>
128+
<slot name="editor"></slot>
129+
</div>`;
130+
}
131+
132+
private onFocus() {
133+
this.isDragging = true;
134+
}
135+
136+
private onBlur() {
137+
this.isDragging = false;
138+
}
139+
140+
private onKeydown(event: KeyboardEvent) {
141+
const { key } = event;
142+
switch (key) {
143+
case 'ArrowRight':
144+
case 'ArrowUp':
145+
this.editorHeightPercent = Math.min(this.editorHeightPercent + 1, 100);
146+
break;
147+
case 'ArrowLeft':
148+
case 'ArrowDown':
149+
this.editorHeightPercent = Math.max(this.editorHeightPercent - 1, 0);
150+
break;
151+
case 'PageUp':
152+
this.editorHeightPercent = Math.min(this.editorHeightPercent + 10, 100);
153+
break;
154+
case 'PageDown':
155+
this.editorHeightPercent = Math.max(this.editorHeightPercent - 10, 0);
156+
break;
157+
case 'Home':
158+
this.editorHeightPercent = 0;
159+
break;
160+
case 'End':
161+
this.editorHeightPercent = 100;
162+
break;
163+
}
164+
}
165+
166+
private onPointerdown(event: PointerEvent) {
167+
this.isDragging = true;
168+
169+
if (this.pointerIds.has(event.pointerId)) return;
170+
171+
this.pointerIds.add(event.pointerId);
172+
(event.target as HTMLElement).setPointerCapture(event.pointerId);
173+
}
174+
175+
private onPointerup(event: PointerEvent) {
176+
this.pointerIds.delete(event.pointerId);
177+
(event.target as HTMLElement).releasePointerCapture(event.pointerId);
178+
179+
if (this.pointerIds.size === 0) {
180+
this.isDragging = false;
181+
}
182+
}
183+
184+
private onPointermove(event: PointerEvent) {
185+
if (!this.isDragging) return;
186+
187+
const { clientY: mouseY } = event;
188+
const { top: wrapperTop, bottom: wrapperBottom } =
189+
this.wrapperEl.getBoundingClientRect();
190+
191+
// The height of the wrapper
192+
const height = wrapperBottom - wrapperTop;
193+
194+
// Calculate the percentage of the editor height in which the pointer is
195+
// located
196+
const editorHeightPercent = 100 - ((mouseY - wrapperTop) / height) * 100;
197+
198+
// Clamp the percentage between 0 and 100
199+
this.editorHeightPercent = Math.min(Math.max(editorHeightPercent, 0), 100);
200+
}
201+
}
202+
203+
declare global {
204+
interface HTMLElementTagNameMap {
205+
'drag-playground': DragPlayground;
206+
}
207+
}

catalog/src/pages/stories.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '../components/drag-playground.js';

catalog/src/ssr.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ import './components/catalog-component-header-title.js';
1212
import './components/nav-drawer.js';
1313
import './components/theme-changer.js';
1414
import './components/top-app-bar.js';
15+
import './components/drag-playground.js';
1516
// 🤫
1617
import '@material/web/labs/item/item.js';

0 commit comments

Comments
 (0)