@symbiotejs/symbiote 3.8.0-webmcp.2 → 3.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -20
- package/core/webmcp.js +60 -39
- package/docs/README.md +62 -0
- package/docs/animations.md +61 -0
- package/docs/attributes.md +85 -0
- package/docs/common-mistakes.md +267 -0
- package/docs/context.md +238 -0
- package/docs/css-data.md +101 -0
- package/docs/dev-mode.md +60 -0
- package/docs/ecosystem.md +110 -0
- package/docs/examples.md +1367 -0
- package/docs/flags.md +205 -0
- package/docs/get-started.md +165 -0
- package/docs/lifecycle.md +90 -0
- package/docs/list-rendering.md +360 -0
- package/docs/lit-vs-symbiote.md +200 -0
- package/docs/llms-index.md +50 -0
- package/docs/migration-2x-to-3x.md +171 -0
- package/docs/properties.md +173 -0
- package/docs/pubsub.md +159 -0
- package/docs/routing.md +234 -0
- package/docs/security.md +53 -0
- package/docs/ssr-server.md +272 -0
- package/docs/ssr.md +222 -0
- package/docs/styling.md +113 -0
- package/docs/templates.md +323 -0
- package/docs/typescript.md +239 -0
- package/docs/webmcp.md +149 -0
- package/llms-full.txt +5290 -0
- package/llms.txt +50 -0
- package/package.json +26 -33
- package/scripts/build-llms.js +58 -0
- package/types/core/webmcp.d.ts +3 -3
- package/types/core/webmcp.d.ts.map +1 -1
- package/AI_REFERENCE.md +0 -890
package/README.md
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
[](https://github.com/symbiotejs/symbiote.js/actions/workflows/tests.yml)
|
|
2
2
|
[](https://www.npmjs.com/package/@symbiotejs/symbiote)
|
|
3
3
|
[](https://www.npmjs.com/package/@symbiotejs/symbiote)
|
|
4
|
-

|
|
5
|
-

|
|
6
4
|

|
|
7
5
|
|
|
8
6
|
# Symbiote.js
|
|
9
7
|
|
|
10
8
|
<img src="https://rnd-pro.com/svg/symbiote/index.svg" width="200" alt="Symbiote.js">
|
|
11
9
|
|
|
12
|
-
A lightweight, standards-first UI library built on Web Components. No virtual DOM, no compiler, no build step required - works directly in the browser. A bundler is recommended for production performance, but entirely optional.
|
|
10
|
+
A lightweight, standards-first UI library built on Web Components. No virtual DOM, no compiler, no black boxes, no excess repaints. No build step required - works directly in the browser. A bundler is recommended for production performance, but entirely optional.
|
|
13
11
|
|
|
14
|
-
Symbiote.js gives you the convenience of a modern framework while staying close to the native platform - HTML, CSS, and DOM APIs. Components are
|
|
12
|
+
Symbiote.js gives you the convenience of a modern framework while staying close to the native platform - HTML, CSS, and DOM APIs. Components are custom elements that work everywhere: in any framework, in plain HTML, in a micro-frontend architecture. And with **isomorphic mode**, the same component code works on the server and the client - server-rendered pages hydrate automatically, no diffing, no mismatch errors.
|
|
15
13
|
|
|
16
|
-
|
|
14
|
+
Here are the three most important differences between Symbiote.js and other frameworks:
|
|
15
|
+
1. Natural DOM Extension Philosophy - designed to extend platform, not to replace it
|
|
16
|
+
2. Runtime-Agnostic HTML Templates - outstanding flexibility for rendering strategies and further customization
|
|
17
|
+
3. Powerful App-wide State Management - combine data contexts without bloated boilerplate or external tools
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
## What's new in v3.x?
|
|
20
|
+
|
|
21
|
+
- **WebMCP support** - expose live Symbiote UI actions as browser-native tools for agents. See the [WebMCP docs](https://github.com/symbiotejs/symbiote.js/blob/webmcp/docs/webmcp.md).
|
|
19
22
|
- **Server-Side Rendering** - render components to HTML with `SSR.processHtml()` or stream chunks with `SSR.renderToStream()`. Client-side hydration via `ssrMode` attaches bindings to existing DOM without re-rendering.
|
|
20
23
|
- **Isomorphic components** - `isoMode` flag makes components work in both SSR and client-only scenarios automatically. If server-rendered content exists, it hydrates; otherwise it renders the template from scratch. One component, zero conditional logic.
|
|
21
|
-
- **Computed properties** - reactive derived state with microtask batching.
|
|
24
|
+
- **Computed properties refined** - reactive derived state with microtask batching.
|
|
22
25
|
- **Path-based router** - optional `AppRouter` module with `:param` extraction, route guards, and lazy loading.
|
|
23
26
|
- **Exit animations** - `animateOut(el)` for CSS-driven exit transitions, integrated into itemize API.
|
|
24
27
|
- **Dev mode** - `Symbiote.devMode` enables verbose warnings; import `devMessages.js` for full human-readable messages.
|
|
@@ -336,18 +339,7 @@ CSS values are parsed automatically - quoted strings become strings, numbers bec
|
|
|
336
339
|
- **Reusable component libraries** - works in React, Vue, Angular, or plain HTML
|
|
337
340
|
- **SSR-powered apps** - lightweight server rendering without framework lock-in
|
|
338
341
|
- **Framework-agnostic solutions** - one codebase, any context
|
|
339
|
-
|
|
340
|
-
## Bundle size
|
|
341
|
-
|
|
342
|
-
| Library | Minified | Gzip | Brotli |
|
|
343
|
-
|---------|----------|------|--------|
|
|
344
|
-
| **Symbiote.js** (core) | 23.6 kb | 8.1 kb | **7.3 kb** |
|
|
345
|
-
| **Symbiote.js** (full, with AppRouter + WebMCP export) | 35.6 kb | 12.1 kb | **11.0 kb** |
|
|
346
|
-
| **Symbiote.js** (WebMCP extension) | 31.4 kb | 10.6 kb | **9.6 kb** |
|
|
347
|
-
| **Lit** 3.3 | 15.5 kb | 6.0 kb | **~5.1 kb** |
|
|
348
|
-
| **React 19 + ReactDOM** | ~186 kb | ~59 kb | **~50 kb** |
|
|
349
|
-
|
|
350
|
-
Symbiote and Lit have similar base sizes, but Symbiote's **7.3 kb** core includes more built-in features: global state management, lists (itemize API), exit animations, computed properties etc. Lit needs additional packages for comparable features. React is **~7× larger** before adding a router, state manager, or SSR framework.
|
|
342
|
+
- **Modern AI-first web** - expose the application state to WebMCP tools automatically
|
|
351
343
|
|
|
352
344
|
## Browser support
|
|
353
345
|
|
|
@@ -359,7 +351,8 @@ All modern browsers: Chrome, Firefox, Safari, Edge, Opera.
|
|
|
359
351
|
- [Lit vs Symbiote.js](https://github.com/symbiotejs/symbiote.js/blob/main/docs/lit-vs-symbiote.md) - Side-by-side comparison
|
|
360
352
|
- [Live Examples](https://rnd-pro.com/symbiote/3x/examples/) - Interactive Code Playground
|
|
361
353
|
- [JSDA-Kit](https://github.com/rnd-pro/jsda-kit) - All-in-one companion tool: server, SSG, bundling, import maps, and native Symbiote.js SSR integration
|
|
362
|
-
- [AI
|
|
354
|
+
- [AI / llms.txt](https://rnd-pro.com/symbiote/llms.txt) — index for AI tools
|
|
355
|
+
- [Full docs (single file)](https://rnd-pro.com/symbiote/llms-full.txt) — complete merged reference for AI context
|
|
363
356
|
- [Changelog](https://github.com/symbiotejs/symbiote.js/blob/main/CHANGELOG.md)
|
|
364
357
|
|
|
365
358
|
## Related articles
|
package/core/webmcp.js
CHANGED
|
@@ -22,7 +22,7 @@ import { parseProp } from './parseProp.js';
|
|
|
22
22
|
* @property {string | ((owner?: any) => string)} [description]
|
|
23
23
|
* @property {Object | ((owner?: any) => Object)} [inputSchema]
|
|
24
24
|
* @property {(args?: Object, owner?: any, event?: Event) => any} [execute]
|
|
25
|
-
* @property {() => boolean} [when]
|
|
25
|
+
* @property {(owner?: any) => boolean} [when]
|
|
26
26
|
* @property {string[]} [deps]
|
|
27
27
|
* @property {string[]} [exposedTo]
|
|
28
28
|
* @property {Object} [annotations]
|
|
@@ -53,6 +53,7 @@ let installedClasses = new WeakSet();
|
|
|
53
53
|
let pendingOwnerSync = new WeakSet();
|
|
54
54
|
/** @type {WeakMap<object, number>} */
|
|
55
55
|
let ownerVersions = new WeakMap();
|
|
56
|
+
let _modelContext;
|
|
56
57
|
|
|
57
58
|
function isToolDescriptor(val) {
|
|
58
59
|
return !!val?.[DICT.MCP_TOOL_DESCRIPTOR_MARKER];
|
|
@@ -244,13 +245,16 @@ function ownerId(owner) {
|
|
|
244
245
|
}
|
|
245
246
|
|
|
246
247
|
function getModelContext() {
|
|
248
|
+
if (_modelContext) return _modelContext;
|
|
247
249
|
let docCtx = /** @type {any} */ (globalThis.document)?.modelContext;
|
|
248
250
|
if (docCtx?.registerTool) {
|
|
249
|
-
|
|
251
|
+
_modelContext = docCtx;
|
|
252
|
+
return _modelContext;
|
|
250
253
|
}
|
|
251
254
|
let navCtx = /** @type {any} */ (globalThis.navigator)?.modelContext;
|
|
252
255
|
if (navCtx?.registerTool) {
|
|
253
|
-
|
|
256
|
+
_modelContext = navCtx;
|
|
257
|
+
return _modelContext;
|
|
254
258
|
}
|
|
255
259
|
return null;
|
|
256
260
|
}
|
|
@@ -591,13 +595,16 @@ function scheduleOwnerSync(owner) {
|
|
|
591
595
|
}
|
|
592
596
|
|
|
593
597
|
export class ToolDescriptor {
|
|
598
|
+
/** @type {((args?: Object, owner?: any, event?: Event) => any) | undefined} */
|
|
599
|
+
#fn;
|
|
600
|
+
|
|
594
601
|
/** @param {ToolDescriptorOptions} options */
|
|
595
602
|
constructor(options = {}) {
|
|
596
603
|
this[DICT.MCP_TOOL_DESCRIPTOR_MARKER] = true;
|
|
597
604
|
this.name = options.name;
|
|
598
605
|
this.description = options.description || 'Symbiote WebMCP tool.';
|
|
599
606
|
this.inputSchema = options.inputSchema || emptySchema();
|
|
600
|
-
this
|
|
607
|
+
this.#fn = options.execute;
|
|
601
608
|
this.when = options.when;
|
|
602
609
|
this.deps = options.deps || [];
|
|
603
610
|
this.exposedTo = options.exposedTo;
|
|
@@ -611,15 +618,15 @@ export class ToolDescriptor {
|
|
|
611
618
|
* @returns {any}
|
|
612
619
|
*/
|
|
613
620
|
execute(args = {}, owner, event) {
|
|
614
|
-
if (typeof this
|
|
621
|
+
if (typeof this.#fn !== 'function') {
|
|
615
622
|
throw new Error('ToolDescriptor requires an execute function.');
|
|
616
623
|
}
|
|
617
|
-
return this
|
|
624
|
+
return this.#fn.call(owner, args || {}, owner, event);
|
|
618
625
|
}
|
|
619
626
|
}
|
|
620
627
|
|
|
621
628
|
export const webMCPRegistry = (() => {
|
|
622
|
-
let existing = PubSub.getCtx(REGISTRY_UID
|
|
629
|
+
let existing = PubSub.getCtx(REGISTRY_UID);
|
|
623
630
|
let root = existing || PubSub.registerCtx({}, REGISTRY_UID);
|
|
624
631
|
ensureNestedCtx(root, 'tools');
|
|
625
632
|
ensureNestedCtx(root, 'owners');
|
|
@@ -662,32 +669,23 @@ export function registerWebMCPTool(owner, key, descriptor) {
|
|
|
662
669
|
webMCPRegistry.store.tools.add(publicName, resolvedEntry, true);
|
|
663
670
|
}
|
|
664
671
|
return resolvedEntry;
|
|
672
|
+
}).catch((e) => {
|
|
673
|
+
setDiagnostic('lastError', e);
|
|
674
|
+
throw e;
|
|
665
675
|
});
|
|
666
676
|
}
|
|
667
677
|
webMCPRegistry.store.tools.add(publicName, entry, true);
|
|
668
678
|
return entry;
|
|
669
679
|
}
|
|
670
680
|
|
|
671
|
-
/**
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
let id = ownerId(owner);
|
|
680
|
-
let desired = collectDesiredTools(owner);
|
|
681
|
-
setupDeps(owner, desired);
|
|
682
|
-
let owners = webMCPRegistry.store.owners;
|
|
683
|
-
let ownerEntry = owners.has(id)
|
|
684
|
-
? owners.read(id)
|
|
685
|
-
: {
|
|
686
|
-
ownerId: id,
|
|
687
|
-
ownerType: ownerType(owner),
|
|
688
|
-
ownerName: ownerName(owner),
|
|
689
|
-
toolsByKey: {},
|
|
690
|
-
};
|
|
681
|
+
/**
|
|
682
|
+
* @param {any} owner
|
|
683
|
+
* @param {string} id
|
|
684
|
+
* @param {{ownerId: string, ownerType: string, ownerName: string, toolsByKey: Record<string, string>}} ownerEntry
|
|
685
|
+
* @param {Array<{key: string, baseName: string, descriptor: ToolDescriptor, executor: Function}>} desired
|
|
686
|
+
* @param {string} componentDescription
|
|
687
|
+
*/
|
|
688
|
+
function applyDesiredTools(owner, id, ownerEntry, desired, componentDescription) {
|
|
691
689
|
let nextKeys = new Set();
|
|
692
690
|
for (let item of desired) {
|
|
693
691
|
nextKeys.add(item.key);
|
|
@@ -703,17 +701,9 @@ export function syncWebMCPTools(owner) {
|
|
|
703
701
|
if (hasToolName(publicName)) {
|
|
704
702
|
unregisterToolName(publicName);
|
|
705
703
|
}
|
|
706
|
-
let entry =
|
|
704
|
+
let entry = makeEntryWithComponentDescription(publicName, item.key, item.descriptor, owner, item.executor, componentDescription);
|
|
707
705
|
ownerEntry.toolsByKey[item.key] = publicName;
|
|
708
|
-
|
|
709
|
-
entry.then((resolvedEntry) => {
|
|
710
|
-
if (isCurrentOwnerVersion(owner, version)) {
|
|
711
|
-
webMCPRegistry.store.tools.add(publicName, resolvedEntry, true);
|
|
712
|
-
}
|
|
713
|
-
});
|
|
714
|
-
} else {
|
|
715
|
-
webMCPRegistry.store.tools.add(publicName, entry, true);
|
|
716
|
-
}
|
|
706
|
+
webMCPRegistry.store.tools.add(publicName, entry, true);
|
|
717
707
|
}
|
|
718
708
|
for (let key in ownerEntry.toolsByKey) {
|
|
719
709
|
if (!nextKeys.has(key)) {
|
|
@@ -723,9 +713,40 @@ export function syncWebMCPTools(owner) {
|
|
|
723
713
|
}
|
|
724
714
|
if (Object.keys(ownerEntry.toolsByKey).length) {
|
|
725
715
|
updateOwnerEntry(id, ownerEntry);
|
|
726
|
-
} else if (owners.has(id)) {
|
|
727
|
-
owners.delete(id);
|
|
716
|
+
} else if (webMCPRegistry.store.owners.has(id)) {
|
|
717
|
+
webMCPRegistry.store.owners.delete(id);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
/** @param {any} owner */
|
|
722
|
+
export function syncWebMCPTools(owner) {
|
|
723
|
+
if (!owner) return;
|
|
724
|
+
if (isElementOwner(owner) && !owner.isConnected) {
|
|
725
|
+
unregisterWebMCPTools(owner);
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
let version = bumpOwnerVersion(owner);
|
|
729
|
+
let id = ownerId(owner);
|
|
730
|
+
let desired = collectDesiredTools(owner);
|
|
731
|
+
setupDeps(owner, desired);
|
|
732
|
+
let owners = webMCPRegistry.store.owners;
|
|
733
|
+
let ownerEntry = owners.has(id)
|
|
734
|
+
? owners.read(id)
|
|
735
|
+
: {
|
|
736
|
+
ownerId: id,
|
|
737
|
+
ownerType: ownerType(owner),
|
|
738
|
+
ownerName: ownerName(owner),
|
|
739
|
+
toolsByKey: {},
|
|
740
|
+
};
|
|
741
|
+
let componentDescription = readComponentDescription(owner);
|
|
742
|
+
if (typeof componentDescription !== 'string') {
|
|
743
|
+
componentDescription.then((resolved) => {
|
|
744
|
+
if (!isCurrentOwnerVersion(owner, version)) return;
|
|
745
|
+
applyDesiredTools(owner, id, ownerEntry, desired, resolved);
|
|
746
|
+
}).catch((e) => setDiagnostic('lastError', e));
|
|
747
|
+
return;
|
|
728
748
|
}
|
|
749
|
+
applyDesiredTools(owner, id, ownerEntry, desired, componentDescription);
|
|
729
750
|
}
|
|
730
751
|
|
|
731
752
|
/** @param {any} owner */
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Symbiote.js Documentation
|
|
2
|
+
|
|
3
|
+
> **Version 3.x** — A lightweight, standards-first UI library built on Web Components.
|
|
4
|
+
> Zero dependencies. ~7.3 KB brotli / ~8.1 KB gzip. No virtual DOM, no compiler, no build step required.
|
|
5
|
+
|
|
6
|
+
## Getting Started
|
|
7
|
+
|
|
8
|
+
- [**Get Started**](./get-started.md) — Installation, first component, platform specs
|
|
9
|
+
- [**Ecosystem**](./ecosystem.md) — IDE setup, build tools, code sharing
|
|
10
|
+
|
|
11
|
+
## Core Concepts
|
|
12
|
+
|
|
13
|
+
- [**Templates**](./templates.md) — HTML string templates, binding syntax, slots, element references
|
|
14
|
+
- [**Properties**](./properties.md) — State initialization, `$` proxy, subscriptions, computed properties
|
|
15
|
+
- [**Context**](./context.md) — Local, named, pop-up, and shared data contexts
|
|
16
|
+
- [**List Rendering**](./list-rendering.md) — Itemize API for dynamic lists
|
|
17
|
+
- [**Lifecycle**](./lifecycle.md) — Component lifecycle callbacks and destruction
|
|
18
|
+
- [**Flags**](./flags.md) — Component configuration flags
|
|
19
|
+
- [**Attributes**](./attributes.md) — HTML attribute binding and observation
|
|
20
|
+
|
|
21
|
+
## State Management
|
|
22
|
+
|
|
23
|
+
- [**PubSub**](./pubsub.md) — Standalone state management, named contexts
|
|
24
|
+
|
|
25
|
+
## Styling
|
|
26
|
+
|
|
27
|
+
- [**Styling**](./styling.md) — rootStyles, shadowStyles, CSS processing
|
|
28
|
+
- [**CSS Data**](./css-data.md) — CSS custom properties as reactive state
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- [**WebMCP Experimental**](./webmcp.md) — Expose live UI state and handlers as browser-native tools
|
|
33
|
+
- [**Routing**](./routing.md) — SPA routing with path patterns, guards, lazy loading
|
|
34
|
+
- [**SSR**](./ssr.md) — Server-side rendering, streaming, hydration
|
|
35
|
+
- [**SSR and Your Server Setup**](./ssr-server.md) — Static build, streaming, Express or Fastify
|
|
36
|
+
- [**Animations**](./animations.md) — CSS-driven exit transitions
|
|
37
|
+
|
|
38
|
+
## Development
|
|
39
|
+
|
|
40
|
+
- [**Dev Mode**](./dev-mode.md) — Verbose warnings for debugging
|
|
41
|
+
- [**TypeScript**](./typescript.md) — JSDoc types, TypeScript usage, and hybrid template checks
|
|
42
|
+
- [**Security**](./security.md) — Trusted Types and CSP compliance
|
|
43
|
+
|
|
44
|
+
## Migration
|
|
45
|
+
|
|
46
|
+
- [**2.x → 3.x Migration Guide**](./migration-2x-to-3x.md) — Breaking changes and upgrade path
|
|
47
|
+
|
|
48
|
+
## Additional Resources
|
|
49
|
+
|
|
50
|
+
- [Lit vs Symbiote.js](./lit-vs-symbiote.md) — Side-by-side comparison
|
|
51
|
+
- [Live Examples](https://rnd-pro.com/symbiote/3x/examples/) - Interactive Code Playground
|
|
52
|
+
- [Common Mistakes](./common-mistakes.md) — Patterns that frequently cause bugs
|
|
53
|
+
- [Examples](./examples.md) — 26 complete working examples
|
|
54
|
+
- [llms.txt](https://rnd-pro.com/symbiote/llms.txt) — index for AI tools
|
|
55
|
+
- [llms-full.txt](https://rnd-pro.com/symbiote/llms-full.txt) — complete merged reference for AI context
|
|
56
|
+
- [Changelog](../CHANGELOG.md)
|
|
57
|
+
- [npm](https://www.npmjs.com/package/@symbiotejs/symbiote)
|
|
58
|
+
- [GitHub Discussions](https://github.com/symbiotejs/symbiote.js/discussions)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
© [rnd-pro.com](https://rnd-pro.com) — MIT License
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Animations
|
|
2
|
+
|
|
3
|
+
Symbiote.js provides CSS-driven exit transitions with zero JS animation code.
|
|
4
|
+
|
|
5
|
+
## `animateOut`
|
|
6
|
+
|
|
7
|
+
`animateOut(el)` sets the `[leaving]` attribute on an element, waits for CSS `transitionend`, then removes the element from the DOM. If no CSS transition is defined, removes immediately.
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
import { animateOut } from '@symbiotejs/symbiote';
|
|
11
|
+
|
|
12
|
+
// or as a static method:
|
|
13
|
+
Symbiote.animateOut(el);
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## CSS pattern
|
|
17
|
+
|
|
18
|
+
Use `@starting-style` for enter animations and `[leaving]` for exit:
|
|
19
|
+
```css
|
|
20
|
+
my-item {
|
|
21
|
+
opacity: 1;
|
|
22
|
+
transform: translateY(0);
|
|
23
|
+
transition: opacity 0.3s, transform 0.3s;
|
|
24
|
+
|
|
25
|
+
/* Enter (CSS-native, no JS needed): */
|
|
26
|
+
@starting-style {
|
|
27
|
+
opacity: 0;
|
|
28
|
+
transform: translateY(20px);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Exit (triggered by animateOut): */
|
|
32
|
+
&[leaving] {
|
|
33
|
+
opacity: 0;
|
|
34
|
+
transform: translateY(-10px);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Itemize integration
|
|
40
|
+
|
|
41
|
+
Both itemize processors use `animateOut` automatically for item removal. Items with CSS `transition` + `[leaving]` styles will animate out before being removed from the DOM:
|
|
42
|
+
```css
|
|
43
|
+
user-card {
|
|
44
|
+
opacity: 1;
|
|
45
|
+
transition: opacity 0.3s;
|
|
46
|
+
|
|
47
|
+
@starting-style {
|
|
48
|
+
opacity: 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
&[leaving] {
|
|
52
|
+
opacity: 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
No additional JavaScript is required — just define the CSS transitions and Symbiote handles the rest.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
Next: [CSS Data →](./css-data.md)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Attributes
|
|
2
|
+
|
|
3
|
+
Like all regular DOM elements, Symbiote components can have their own HTML attributes. And like standard Custom Elements, they can react to dynamic attribute changes.
|
|
4
|
+
|
|
5
|
+
## Attribute connection
|
|
6
|
+
|
|
7
|
+
The simplest way to connect a local state property to an attribute value is with the `@` token:
|
|
8
|
+
```js
|
|
9
|
+
class MyComponent extends Symbiote {
|
|
10
|
+
|
|
11
|
+
init$ = {
|
|
12
|
+
'@attribute-name': 'initial value',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or initiate from the component's template directly:
|
|
19
|
+
```js
|
|
20
|
+
class MyComponent extends Symbiote {}
|
|
21
|
+
|
|
22
|
+
MyComponent.template = html`<h1>{{@attribute-name}}</h1>`;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Then use it as an attribute in markup:
|
|
26
|
+
```html
|
|
27
|
+
<my-component attribute-name="attribute value"></my-component>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Attribute change reaction
|
|
31
|
+
|
|
32
|
+
Using a property accessor:
|
|
33
|
+
```js
|
|
34
|
+
class MyComponent extends Symbiote {
|
|
35
|
+
|
|
36
|
+
set 'my-attribute'(val) {
|
|
37
|
+
console.log(val);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
MyComponent.observedAttributes = [
|
|
43
|
+
'my-attribute',
|
|
44
|
+
];
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## `bindAttributes()` static method
|
|
48
|
+
|
|
49
|
+
Bind attributes to property values directly:
|
|
50
|
+
```js
|
|
51
|
+
class MyComponent extends Symbiote {
|
|
52
|
+
|
|
53
|
+
init$ = {
|
|
54
|
+
myProp: '',
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Map the attribute name to corresponding property key:
|
|
60
|
+
MyComponent.bindAttributes({
|
|
61
|
+
'my-attribute': 'myProp',
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// observedAttributes is auto-populated
|
|
65
|
+
|
|
66
|
+
MyComponent.template = html`
|
|
67
|
+
<div>{{myProp}}</div>
|
|
68
|
+
`;
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Reserved attribute names
|
|
72
|
+
|
|
73
|
+
These attribute names are used internally by Symbiote.js:
|
|
74
|
+
|
|
75
|
+
- `bind`
|
|
76
|
+
- `ctx`
|
|
77
|
+
- `ref`
|
|
78
|
+
- `itemize`
|
|
79
|
+
- `item-tag`
|
|
80
|
+
- `use-template`
|
|
81
|
+
- `skip-text-nodes`
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
Next: [PubSub →](./pubsub.md)
|