Skip to content

Commit

Permalink
Merge pull request #1 from phase2/issue/202-evaluate-document-outline…
Browse files Browse the repository at this point in the history
…-templates-storybook-starter-kit

202: Evaluate/Document Outline templates storybook starter kit
  • Loading branch information
himerus authored Apr 15, 2024
2 parents 94121e6 + 508905c commit b48ae0c
Show file tree
Hide file tree
Showing 17 changed files with 603 additions and 104 deletions.
2 changes: 0 additions & 2 deletions packages/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@
"devDependencies": {
"@phase2/outline-adopted-stylesheets-controller": "^1.0.4",
"@phase2/outline-config": "^0.0.14",
"@phase2/outline-core": "^0.2.7",
"@phase2/outline-core-alert": "^0.0.4",
"@phase2/outline-core-link": "^0.0.16",
"@phase2/outline-docs": "^0.0.21",
"@storybook/addon-essentials": "^7.6.17",
"@storybook/addon-links": "^7.6.17",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Meta } from '@storybook/addon-docs';
import { OutlineExample } from '../outline-example';

<Meta
title="Outline Core/Example"
parameters={{
viewMode: 'docs',
previewTabs: {
canvas: {
hidden: true,
},
},
}}
/>

# `OutlineExample`: `outline-example`

> This is an `outline-example` component.
> This component is a wrapper around an anchor tag that provides **encapsulated** and **global** styles for links.
> This component extends `LitElement` and adds custom styling.
<outline-example><a href="https://github.com/phase2/outline" class="sb-unstyled">Outline</a></outline-example>

```html
<outline-example>
<a href="https://github.com/phase2/outline">Outline</a>
</outline-example>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { customElement } from 'lit/decorators.js';
import { LitElement, html } from 'lit';

import globalStyles from './styles/outline-example.global.css?inline';
import encapsulatedStyles from './styles/outline-example.encapsulated.css?inline';
import { AdoptedStyleSheets } from '../../../controllers/adopted-stylesheets';

/**
* Example component.
*
* @see {@link https://lit.dev/docs/components/defining/ | Defining components}
* @see {@link https://www.npmjs.com/package/@phase2/outline-example-alert | @phase2/outline-example-alert }
* @element outline-example
* @extends OutlineExample
*/
@customElement('outline-example')
export class OutlineExample extends LitElement {
adoptedStyleSheets = new AdoptedStyleSheets(this, {
globalCSS: globalStyles,
encapsulatedCSS: encapsulatedStyles,
});

/**
* Wrapping the rendered content in a div with the class "encapsulated-container".
* This demonstrates the difference between encapsulated and light DOM styling.
*
* @category rendering
* @returns {TemplateResult} The template to render.
*/
render() {
return html`
<div class="encapsulated-container">
<slot></slot>
</div>
`;
}
}

/**
* TypeScript declaration extends the HTMLElementTagNameMap interface, adding the web component.
* This enhances type checking and autocompletion in IDEs.
*
* @see {@link https://lit.dev/docs/components/defining/#typescript-typings | Providing good TypeScript typings}
*/
declare global {
interface HTMLElementTagNameMap {
'outline-example': OutlineExample;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
outline-link {
outline-example {
/* This is just here to demonstrate using a nested import in a CSS file. */
@nested-import './outline-link.imported.css';
@nested-import './outline-example.imported.css';
}

This file was deleted.

This file was deleted.

82 changes: 82 additions & 0 deletions packages/storybook/src/controllers/adopted-stylesheets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { ReactiveController, ReactiveControllerHost } from 'lit';

/**
* A controller for managing adopted stylesheets in a Lit element.
* This allows for styles to be dynamically applied to the component's
* light DOM or shadow DOM.
*/
export class AdoptedStyleSheets implements ReactiveController {
// The host element that the controller is associated with.
private host: ReactiveControllerHost & HTMLElement;
// An object containing the CSS to be applied globally or encapsulated within the shadow DOM.
private css: { globalCSS?: string; encapsulatedCSS?: string };

// A set to track the stylesheets applied to the light DOM.
private static appliedLightDomStylesheets: Set<string> = new Set();

/**
* Constructs an instance of the AdoptedStyleSheets controller.
* @param host The host element that the controller will be associated with.
* @param css An object containing optional global and encapsulated CSS strings.
*/
constructor(
host: ReactiveControllerHost & HTMLElement,
css: {
globalCSS?: string;
encapsulatedCSS?: string;
} = {}
) {
this.host = host;
this.css = css;
this.host.addController(this); // Register this instance as a controller for the host element.
}

/**
* Applies the given CSS text to the specified target (Document or ShadowRoot).
* @param cssText The CSS text to apply.
* @param target The target where the CSS should be applied.
*/
private applyCssToDom(cssText: string, target: Document | ShadowRoot) {
if (target instanceof Document) {
const store = AdoptedStyleSheets.appliedLightDomStylesheets;

if (store.has(cssText)) {
// If the stylesheet has already been applied, no further action is required.
return;
}
store.add(cssText); // Store the stylesheet with the provided key.
}

// Create a new stylesheet and replace its contents with the provided CSS text.
const sheet = new CSSStyleSheet();
sheet.replaceSync(cssText);

// Apply the stylesheet to the target's adoptedStyleSheets.
target.adoptedStyleSheets = [...target.adoptedStyleSheets, sheet];
}

/**
* Lifecycle callback called when the host element is connected to the document's DOM.
* Applies global and encapsulated CSS to the respective DOM targets.
*/
hostConnected() {
if (this.css.globalCSS) {
this.applyCssToDom(this.css.globalCSS, document); // Apply global CSS to the document if it exists.
}

// Apply encapsulated CSS to the host's shadow root if it exists.
if (this.css.encapsulatedCSS && this.host.shadowRoot) {
this.applyCssToDom(this.css.encapsulatedCSS, this.host.shadowRoot); // Apply encapsulated CSS to the host's shadow root if it exists.
}
}

/**
* Lifecycle callback called when the host element is disconnected from the document's DOM.
* Note: When a component with a Shadow DOM is disconnected from the document's DOM, the Shadow DOM is also removed along with the component.
* However, for Light DOM styles, they are not removed here because other instances of the component
* might still be present on the page and require these styles.
*/
hostDisconnected() {
// No action is taken when the host is disconnected.
}
}
Loading

0 comments on commit b48ae0c

Please sign in to comment.