@scalable.software/pin
v0.2.1
Published
The Pin component is a button that can be visible or hidden, pinned or unpinned.
Downloads
18
Readme
Pin Component Documentation
Below is a comprehensive document detailing the Pin component’s architecture, usage, and developer workflow. It prioritizes a Quick Start so you can see the pin in action right away, then dives into State, Operations, Events, and more advanced topics.
1. Introduction
The Pin component provides a toggleable element that can be pinned/unpinned and hidden/shown via both imperative and declarative APIs. It leverages custom Web Component features, including Shadow DOM, custom events, and attribute reflection for a clean, modular design.
1.1 Installation & Setup
Clone the Repository
git clone <repository-url> cd pin-componentInstall Dependencies
npm installRun Tests (Optional)
npm test- Ensures everything is working as expected before you start.
Start Local Server
npm run serve- This runs a local development server, making
<pin-button>available in your browser.
- This runs a local development server, making
2. Quick Start Usage Walkthrough
This walkthrough demonstrates how to interact with <pin-button> right after installation, letting you see how the pin behaves before exploring its internal design.
Open the served page in your browser.
- You should see a
<pin-button>component displayed.
- You should see a
Open the Developer Tools, select the Console.
Get a reference to the component:
const pin = document.querySelector("pin-button");Create a handler for when the pin is pinned:
const onpin = (event) => { console.log("Pin was pinned:", event.detail.status); };Assign that handler to the
onpinproperty:pin.onpin = onpin;Pin the component imperatively:
pin.pin();- This changes
statusfromunpinnedtopinned. - Observe the console output:
"Pin was pinned: pinned".
- This changes
Inspect the
<pin-button>in the Elements tab:<pin-button status="pinned"></pin-button>Manually change the
statusattribute to"unpinned"- Notice the pin’s icon and internal state update.
Check the current status in the console:
pin.status; // Should return "unpinned"Click the
<pin-button>in the UI- By default, clicking toggles it between
pinnedandunpinned. - If pinned, the
onpinevent is fired (if you have a listener).
- By default, clicking toggles it between
Check the status again:
pin.status; // "pinned" or "unpinned"Visibility
- You can similarly call
pin.hide()orpin.show()to toggle thevisiblestate.- Watch for
onhideoronshowevents in the console.
For more details on States, Operations, and Events, consult the sections below or check the generated API documentation (see npm run document instructions).
3. Architectural Overview
3.1 Composition
Template (e.g.,
Pin.template.html):
Defines DOM structure (a.iconcontainer, pinned/unpinned SVGs, etc.).Style (e.g.,
Pin.style.css):
Encapsulates appearance, including hover effects, pinned/unpinned icon transitions.Class (
Pin.ts):
Extends a base component class to handle lifecycle hooks, Shadow DOM creation, attribute reflection, etc.
Separation of Concerns
- HTML for structure
- CSS for presentation
- TypeScript for behavior
3.2 Class Lifecycle
Lifecycle methods are implemented in the base class and extended by the Pin component:
connectedCallback()- Creates a Shadow DOM.
- Clones the HTML template into the Shadow Root.
- Inserts a
<link>for scoped CSS. - Initializes default states (
visible,status). - Sets up event listeners (e.g., on click).
disconnectedCallback()- Removes event listeners and cleans up references.
4. Core Concepts: State, Operations, and Events
After seeing the pin in action, the following sections explain how it works internally.
4.1 State
The Pin component uses both internal properties and DOM attributes to manage its state. This ensures consistency whether you modify the pin via script or HTML attributes.
4.1.1 Definition
visible(yes|no)- Whether the pin is displayed.
- Not-initialized by default, meaning the component reverts to an internal default (
yes) ifvisibleis absent.
status(pinned|unpinned)- Whether the pin is pinned or unpinned.
- Initialized by default, so if
<pin-button status="pinned">is found at startup, it remains pinned initially.
4.1.2 Internal vs. Attribute-Driven State
Internal State
private _visible: Visibility = Visible.YES; private _status: Statuses = Status.UNPINNED;- Predictable defaults decoupled from external attributes.
Attribute-Driven State
public get visible(): Visibility { return this.hasAttribute(Attribute.VISIBLE) ? (this.getAttribute(Attribute.VISIBLE) as Visibility) : this._visible; } public set visible(visible: Visibility) { visible = visible || Visible.YES; // fallback if (this._visible !== visible) { this._visible = visible; visible === Visible.YES && this.removeAttribute(Attribute.VISIBLE); visible === Visible.NO && this.setAttribute(Attribute.VISIBLE, visible); } }- Keeps
_visiblein sync with[visible="no"]if you set it in HTML or JS.
- Keeps
4.1.3 Lifecycle & Attribute Handlers
_initialize()sets internal defaults on connect.attributeChangedCallback()invokes_attributeHandlers, updating state when external attributes change.
4.1.4 Usage Tips
- Fallback: Removing
[visible="no"]reverts toYES. - Clean DOM: If
visibleisYES, no attribute is present.
4.2 Operations
The Pin component’s imperative API manipulates state, which in turn updates the DOM and triggers events.
4.2.1 Definition
hide()→visible="no"show()→visible="yes"pin()→status="pinned"unpin()→status="unpinned"toggle()→ flips between pinned/unpinned
4.2.2 Example
public toggle = () => (
this.status = this.status === Status.PINNED ? Status.UNPINNED : Status.PINNED
);4.3 Events
The Pin component broadcasts custom events when its internal state changes.
4.3.1 Definition
onhide: Fired when going from visible to hidden.onshow: Fired when going from hidden to visible.onpin: Fired when going from unpinned to pinned.onunpin: Fired when going from pinned to unpinned.on: A catch-all for any of the above changes.
4.3.2 Usage Tips
- Assign a handler:
pin.onpin = (event) => console.log(event.detail.status);- Or use standard DOM events:
pin.addEventListener("onpin", (e) => {
// ...
});5. Development Workflow
Below is a recommended workflow for building, testing, documenting, and publishing the Pin component:
5.1 From Specifications to Implementation
- Define Specs (states, operations, events).
- Implement in TypeScript (reflect states as attributes, dispatch events on changes).
- Link the final compiled JS or leverage import maps.
5.2 Testing
- Unit Tests using Karma + Jasmine or real-time Wallaby.
- Ensure coverage for states, operations, events.
- Generate coverage:
npm test5.3 Import Maps
<script type="importmap">
{
"imports": {
"pin-component": "./dist/Pin.js"
}
}
</script>
<script type="module">
import "pin-component";
</script>5.4 Documentation (Typedoc)
npm run document- Auto-generates API docs from TypeScript annotations.
5.5 Publishing & Versioning
Build:
npm run build→ outputs
dist/.Bump Version:
npm version [major|minor|patch]Publish:
npm publishto npm or private registry.
6. Best Practices & Extensibility
Descriptive Naming
pinned/unpinnedis domain-friendly for toggles.
Accessibility
- Consider ARIA attributes and keyboard interactions if you want
<pin-button>fully accessible as a toggle/button.
- Consider ARIA attributes and keyboard interactions if you want
Testing
- Simulate user clicks/touches to confirm correct state changes and event dispatch.
Documentation
- Provide real usage snippets (like in the Quick Start) for developer onboarding.
Publishing
- Use semantic versioning to manage features/breaking changes.
- Employ CI/CD for automated build/test/publish.
7. Conclusion
By showing you how to pin or unpin the component immediately (in the Usage Walkthrough), we’ve demonstrated its core features. Internally, the Pin component’s State, Operations, and Events ensure predictable, event-driven functionality. Follow the development workflow for specs, build, test, docs, and publishing to confidently integrate <pin-button> into your projects.
License
This document is released under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) license.
For more details, please visit the license agreement.
