1
0
mirror of https://github.com/jaandrle/deka-dom-el synced 2025-07-01 04:12:14 +02:00

42 Commits

Author SHA1 Message Date
9a5909eb90 🔤 logos 2025-03-07 11:48:34 +01:00
e9d75a4631 🐛 npx-hint
[Scrollable region must have keyboard access | Axe Rules | Deque University | Deque Systems](https://dequeuniversity.com/rules/axe/4.10/scrollable-region-focusable?application=axeAPI)
2025-03-07 10:57:17 +01:00
7f4787d704 🔤 UI/UX/wording 2025-03-07 10:40:57 +01:00
57a5ff2dfe irelands 2025-03-06 18:33:12 +01:00
b3356afa88 🔤 improvemens 2025-03-06 14:33:47 +01:00
da4e3e52d9 issignal 2025-03-06 11:44:13 +01:00
6c297672c1 🔤 2025-03-06 11:42:56 +01:00
66fdee2c05 🔤 ui/ux 2025-03-06 10:59:41 +01:00
963ed53c84 🔤 2025-03-06 10:59:31 +01:00
59efa84494 🔤 scopes 2025-03-06 09:41:29 +01:00
17e40fdd9c 🔤 2025-03-05 19:29:03 +01:00
05413cb2bb 🔤 2025-03-05 18:59:37 +01:00
5a6f011823 🔤 ui/ux 2025-03-05 18:40:09 +01:00
49243b978a 🐛 🔤 types 3ps 2025-03-05 18:00:04 +01:00
02f7b3fd67 🔤 logo 2025-03-05 16:54:09 +01:00
7078ec68c1 🐛 wrong file(s) in git 2025-03-05 16:24:47 +01:00
41d7728d18 🐛 fixes completitions for el with components 2025-03-05 16:24:06 +01:00
8f0879196f 🔤 intro 2025-03-05 15:29:59 +01:00
9ed6de2f8a 🔤 elements 2025-03-05 14:47:29 +01:00
2a3b6dc5cd 🔤 events 2025-03-05 14:39:28 +01:00
1c5f0dab5e dispatch 2025-03-05 14:39:19 +01:00
e1f2b32736 🔤UI 2025-03-05 14:08:21 +01:00
e2df9705d1 🔤 updates texts 2025-03-05 11:53:32 +01:00
209fa49dee 🔤 UI 2025-03-05 10:14:09 +01:00
1b0312f6bd 🔤 2025-03-04 17:52:55 +01:00
508d93bb1a bs/lint 2025-03-04 17:00:14 +01:00
f2ce23d9f7 🔤 2025-03-04 16:46:00 +01:00
b08f75bfb0 🔤 ssr 2025-03-04 16:24:34 +01:00
4edc509646 🔤 adds debugging 2025-03-04 15:42:52 +01:00
56232a9f64 (bs) (un)min 2025-03-04 14:07:00 +01:00
dcf389e28e 🔤 UI enhancements 2025-03-04 13:19:16 +01:00
bdb20ec298 🔤 Docs UI/UX 2025-03-03 19:06:23 +01:00
7ec50e1660 🐛 coumputed signal 2025-03-03 15:25:04 +01:00
6c4ddd655f 🔤 2025-03-03 15:21:43 +01:00
198f4a3777 🔤 2025-03-03 15:20:31 +01:00
3435ea6cfe 🐛 Better types for on* 2025-03-03 15:10:20 +01:00
ed7e6c7963 Refatc signals to .get/.set syntax #26 2025-03-03 14:19:41 +01:00
3168f452ae wip 2025-02-28 19:53:07 +01:00
b53f3926b3 wip 2025-02-28 17:12:40 +01:00
8f2fd5a68c 🔤 2025-02-28 14:32:51 +01:00
f53b97a89c wip 2025-02-28 13:40:56 +01:00
f8a94ab9f8 🎉 2025-02-28 13:05:46 +01:00
135 changed files with 6823 additions and 17360 deletions

View File

@ -1,40 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ":bug: "
labels: bug
assignees: ''
---
## Bug Description
<!-- A clear and concise description of what the bug is -->
## Steps to Reproduce
<!-- Steps to reproduce the behavior -->
1.
2.
3.
## Expected Behavior
<!-- A clear and concise description of what you expected to happen -->
## Actual Behavior
<!-- A clear and concise description of what actually happened -->
## Code Sample
<!-- If applicable, add minimal code sample to reproduce the issue -->
```js
// Your code here
```
## Environment
- Browser and version: <!-- e.g. Chrome 120, Firefox 120, Safari 17 -->
- OS: <!-- e.g. Windows 11, macOS Sonoma, Ubuntu 22.04 -->
- dd<el> version: <!-- e.g. 0.9.2 -->
- Other relevant details:
## Screenshots
<!-- If applicable, add screenshots to help explain your problem -->
## Additional Context
<!-- Add any other context about the problem here -->

View File

@ -1,22 +0,0 @@
---
name: Documentation improvement
about: Suggest improvements to the documentation
title: ":abc: "
labels: documentation
assignees: ''
---
## Documentation Area
<!-- Which part of the documentation needs improvement? Provide links if applicable -->
## Current Issue
<!-- What's currently unclear, missing, or incorrect in the documentation? -->
## Suggested Improvement
<!-- Describe the improvement or addition you'd like to see -->
## Example Content
<!-- If applicable, provide example content or wording -->
## Additional Context
<!-- Any other context or screenshots about the documentation request -->

View File

@ -1,29 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: ":zap: "
labels: enhancement
assignees: ''
---
<!-- Consider open discussion: https://github.com/jaandrle/deka-dom-el/discussions first -->
## Problem Statement
<!-- A clear and concise description of the problem this feature would solve -->
## Proposed Solution
<!-- A detailed description of the feature you're suggesting -->
## Use Cases
<!-- Describe specific use cases where this feature would be beneficial -->
## Example Implementation
<!-- If possible, provide example code or pseudocode for how this feature might work -->
```js
// Example code
```
## Alternatives Considered
<!-- A description of any alternative solutions or features you've considered -->
## Additional Context
<!-- Any other context, screenshots, or examples that might be helpful -->

View File

@ -1,39 +0,0 @@
<!--
Please use an appropriate git3moji in your PR title: https://robinpokorny.github.io/git3moji/
Examples:
- :bug: Fix signal update not triggering on nested properties
- :zap: Improve event delegation performance
- :abc: Add documentation for custom elements
-->
## Description
<!-- Describe the changes introduced by this PR -->
## Related Issues
<!-- Link any related issues using the format #ISSUE_NUMBER -->
## Type of Change
- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation update
- [ ] Code refactoring
- [ ] Performance improvement
- [ ] Test update
## Testing Performed
<!-- Describe the tests you've done to verify your changes -->
## Screenshots
<!-- If applicable, add screenshots to help explain your changes -->
## Checklist
- [ ] My code follows the code style of this project
- [ ] I have performed a self-review of my own code
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] I have updated the documentation accordingly
- [ ] My changes generate no new warnings
- [ ] All existing tests are passing
## Additional Notes
<!-- Any additional information that might be helpful for reviewers -->

View File

@ -1,18 +0,0 @@
name: Publish Package to npmjs
on:
workflow_dispatch:
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.3.0
with:
node-version: '20.16'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

3
.npmrc
View File

@ -1,3 +0,0 @@
//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
registry=https://registry.npmjs.org/
always-auth=true

View File

@ -1,134 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or advances of
any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
andrle.jan@centrum.cz.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of
actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the
community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].
[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

View File

@ -1,177 +0,0 @@
# Contributing to Deka DOM Elements
Thank you for your interest in contributing to Deka DOM Elements (dd<el> or DDE)! This document provides guidelines and
instructions for contributing to the project.
## Table of Contents
- [Code of Conduct](#code-of-conduct)
- [Getting Started](#getting-started)
- [Development Workflow](#development-workflow)
- [Commit Guidelines](#commit-guidelines)
- [Pull Request Process](#pull-request-process)
- [Issue Guidelines](#issue-guidelines)
- [Coding Standards](#coding-standards)
- [Testing](#testing)
- [Documentation](#documentation)
## Code of Conduct
Please be respectful and inclusive in your interactions with other contributors. We aim to foster a welcoming community
where everyone feels comfortable participating.
## Getting Started
1. **Fork the repository**:
- Click the "Fork" button on the GitHub repository
2. **Clone your fork**:
```bash
git clone https://github.com/YOUR-USERNAME/deka-dom-el.git
cd deka-dom-el
```
3. **Set up the development environment**:
```bash
npm ci
```
4. **Add the upstream repository**:
```bash
git remote add upstream https://github.com/jaandrle/deka-dom-el.git
```
## Development Workflow
1. **Create a new branch**:
```bash
git checkout -b your-feature-branch
```
Use descriptive branch names that reflect the changes you're making.
2. **Make your changes**:
- Write clean, modular code
- Follow the project's coding standards (see [Coding Standards](#coding-standards))
- Include relevant tests for your changes
3. ~**Run tests**:~
```bash
#npm test
```
4. **Build the project**:
```bash
npm run build
#or
bs/build.js
```
5. **Preview documentation changes** (if applicable):
```bash
npm run docs
#or
bs/docs.js
```
…see [BS folder](./bs/README.md) for more info.
## Categorizing [![git3moji](https://img.shields.io/badge/git3moji%E2%80%93v1-%E2%9A%A1%EF%B8%8F%F0%9F%90%9B%F0%9F%93%BA%F0%9F%91%AE%F0%9F%94%A4-fffad8.svg?style=flat-square)](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
We use [git3moji](https://git3moji.netlify.app/) for commit messages, issue titles, pull request titles and in other
areas. To make categorizing quick and consistent.
## Commit Guidelines
We use [git3moji](https://git3moji.netlify.app/) for commit messages. This helps keep the commit history clear and
consistent.
```
:emoji: Short summary of the change
```
…for example:
```
:bug: Fix signal update not triggering on nested properties
:zap: Improve event delegation performance
:abc: Add documentation for custom elements
```
## Pull Request Process
1. **Push your changes**:
```bash
git push origin your-feature-branch
```
2. **Open a Pull Request**:
- Go to the repository on GitHub
- Click "New Pull Request"
- Select your branch
- Provide a clear description of your changes
3. **PR Guidelines**:
- Use a clear, descriptive title with the appropriate git3moji
- Reference any related issues
- Explain what the changes do and why they are needed
- List any dependencies that are required for the change
- Include screenshots or examples if applicable
4. **Code Review**:
- Address any feedback from reviewers
- Make necessary changes and push to your branch
- The PR will be updated automatically
5. **Merge**:
- Once approved, a maintainer will merge your PR
- The main branch is protected, so you cannot push directly to it
## Issue Guidelines
When creating an issue, please use the appropriate template and include as much information as possible:
### Bug Reports
- Use the `:bug:` emoji in the title
- Clearly describe the issue
- Include steps to reproduce
- Mention your environment (browser, OS, etc.)
- Add screenshots if applicable
### Feature Requests
- Use the `:zap:` emoji in the title
- Describe the feature clearly
- Explain why it would be valuable
- Include examples or mockups if possible
### Documentation Improvements
- Use the `:abc:` emoji in the title
- Identify what documentation needs improvement
- Suggest specific changes or additions
## Coding Standards
- Follow the existing code style in the project
- Use meaningful variable and function names
- Keep functions small and focused
- Add comments for complex logic
- Use TypeScript types appropriately
<!--
## Testing
- Add tests for new features
- Update tests for modified code
- Ensure all tests pass before submitting a PR
-->
## Documentation
- Update the documentation when you add or modify features
- Document both API usage and underlying concepts
- Use clear, concise language
- Include examples where appropriate
---
Thank you for contributing to Deka DOM Elements! Your efforts help make the project better for everyone.

163
README.md
View File

@ -1,11 +1,17 @@
**Alpha**
| [Docs](https://jaandrle.github.io/deka-dom-el "Official documentation and guide site")
| [NPM](https://www.npmjs.com/package/deka-dom-el "Official NPM package page")
| [GitHub](https://github.com/jaandrle/deka-dom-el "Official GitHub repository")
([*Gitea*](https://gitea.jaandrle.cz/jaandrle/deka-dom-el "GitHub repository mirror on my own Gitea instance"))
**WIP** (the experimentation phase)
| [source code on GitHub](https://github.com/jaandrle/deka-dom-el)
| [*mirrored* on Gitea](https://gitea.jaandrle.cz/jaandrle/deka-dom-el)
<p align="center">
<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
</p>
# Deka DOM Elements
***Vanilla for flavouring — a full-fledged feast for large projects***
*…use simple DOM API by default and library tools and logic when you need them*
```javascript
// 🌟 Reactive component with clear separation of concerns
document.body.append(
@ -13,41 +19,38 @@ document.body.append(
);
function EmojiCounter({ initial }) {
// ✨ - Define reactive data
// ✨ State - Define reactive data
const count = S(0);
const emoji = S(initial);
const textContent = S(() => `Hello World ${emoji.get().repeat(count.get())}`);
// 🔄 - UI updates automatically when signals change
/** @param {HTMLOptionElement} el */
const isSelected= el=> (el.selected= el.value===initial);
// 🔄 View - UI updates automatically when signals change
return el().append(
el("p", { textContent, className: "output" }),
// 🎮 - Update state on events
el("button", { textContent: "Add Emoji" },
on("click", () => count.set(count.get() + 1)),
),
el("select", null,
on.defer(el=> el.value= initial),
on("change", e => emoji.set(e.target.value)),
).append(
el(Option, "🎉"),
el(Option, "🚀"),
el(Option, "💖"),
)
el("p", {
className: "output",
textContent: S(() =>
`Hello World ${emoji.get().repeat(clicks.get())}`),
}),
// 🎮 Controls - Update state on events
el("button", { textContent: "Add Emoji" },
on("click", () => count.set(count.get() + 1))
),
el("select", null, on("change", e => emoji.set(e.target.value)))
.append(
el(Option, "🎉", isSelected),
el(Option, "🚀", isSelected),
el(Option, "💖", isSelected),
)
);
}
function Option({ textContent }){
return el("option", { value: textContent, textContent });
return Ol("option", { value: textContent, textContent });
}
```
*…use simple DOM API by default and library tools and logic when you need them*
<p align="center">
<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
</p>
# Deka DOM Elements (dd\<el\> or DDE)
Creating reactive elements, components, and Web Components using the native
[IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API enhanced with
@ -56,57 +59,19 @@ Creating reactive elements, components, and Web Components using the native
## Features at a Glance
-**No build step required** — use directly in browsers or Node.js
- ☑️ **Lightweight** — ~10-15kB minified (original goal 10kB) with **zero**/minimal dependencies
- ☑️ **Lightweight** — ~10-15kB minified (original goal 10kB) with zero/minimal dependencies
-**Declarative & functional approach** for clean, maintainable code
-**Signals and events** for reactive UI
-**Auto-releasing resources** for memory management but nice development experience
- **Memoization for performance** — optimize rendering with intelligent caching
- ☑️ **Optional build-in signals** with support for custom reactive implementations (#39)
- ☑️ **Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
-**TypeScript support**
-**Support for debugging with browser DevTools** without extensions
- ☑️ **Enhanced Web Components** support
## Getting Started
### Quick Links
- [**Documentation and Guide**](https://jaandrle.github.io/deka-dom-el)
- [**Examples**](https://jaandrle.github.io/deka-dom-el/p15-examples.html)
- [**Changelog**](https://github.com/jaandrle/deka-dom-el/releases)
### Installation
```bash
npm install deka-dom-el --save
```
…or via CDN / Direct Script:
For CDN links and various build formats (ESM/IIFE, with/without signals, minified/unminified), see the [interactive
format selector](https://jaandrle.github.io/deka-dom-el/#h-getting-started) on the documentation site.
```html
<!-- Example with IIFE build (creates a global DDE object) -->
<script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/iife-with-signals.min.js"></script>
<script>
const { el, S } = DDE;
// Your code here
</script>
<!-- Or with ES modules -->
<script type="module">
import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
// Your code here
</script>
```
-**Optional signals** with support for custom reactive implementations
-**Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
- 🔄 **TypeScript support** (work in progress)
- 🔄 **Enhanced Web Components** support (work in progress)
## Why Another Library?
This library bridges the gap between minimal solutions like van/hyperscript and more comprehensive frameworks like
[solid-js](https://github.com/solidjs/solid), offering a balanced trade-off between size, complexity, and usability.
Following functional programming principles, dd\<el\> starts with pure JavaScript (DOM API) and gradually adds
Following functional programming principles, Deka DOM Elements starts with pure JavaScript (DOM API) and gradually adds
auxiliary functions. These range from minor improvements to advanced features for building complete declarative
reactive UI templates.
@ -114,6 +79,29 @@ A key advantage: any internal function (`assign`, `classListDeclarative`, `on`,
independently while also working seamlessly together. This modular approach makes it easier to integrate the library
into existing projects.
## Getting Started
### Installation
#### npm
```bash
# TBD
# npm install deka-dom-el
```
#### Direct Script
```html
<script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/dde-with-signals.min.js"></script>
<script type="module">
const { el, S } = dde;
</script>
```
### Documentation
- [**Interactive Guide**](https://jaandrle.github.io/deka-dom-el): WIP
- [Examples](./examples/): TBD/WIP
## Understanding Signals
Signals are the reactive backbone of Deka DOM Elements:
@ -123,24 +111,11 @@ Signals are the reactive backbone of Deka DOM Elements:
- [TC39 Signals Proposal](https://github.com/tc39/proposal-signals) (future standard)
- [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) (underlying concept)
## Contributing
We welcome contributions from the community! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to
get started, coding standards, commit guidelines, and the pull request process.
## Inspiration and Alternatives
- [vanjs-org/van](https://github.com/vanjs-org/van) World's smallest reactive UI framework
- [adamhaile/S](https://github.com/adamhaile/S) Simple, clean, fast reactive programming
- [hyperhype/hyperscript](https://github.com/hyperhype/hyperscript) Create HyperText with JavaScript
- [potch/signals](https://github.com/potch/signals) A small reactive signals library
- [AseasRoa/paintor](https://github.com/AseasRoa/paintor) - JavaScript library for building reactive client-side user
interfaces or HTML code.
- [pota](https://pota.quack.uy/) — small and pluggable Reactive Web Renderer. It's compiler-less, includes an html
function, and a optimized babel preset in case you fancy JSX.
- [TarekRaafat/eleva](https://github.com/TarekRaafat/eleva) — A minimalist, lightweight, pure vanilla JavaScript
frontend runtime framework.
- [didi/mpx](https://github.com/didi/mpx) — Mpx一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架
- [mxjp/rvx](https://github.com/mxjp/rvx) — A signal based frontend framework
- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) —
Functional DOM components without JSX/virtual DOM (my old library)
- [vanjs-org/van](https://github.com/vanjs-org/van) - World's smallest reactive UI framework
- [adamhaile/S](https://github.com/adamhaile/S) - Simple, clean, fast reactive programming
- [hyperhype/hyperscript](https://github.com/hyperhype/hyperscript) - Create HyperText with JavaScript
- [potch/signals](https://github.com/potch/signals) - A small reactive signals library
- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) -
Functional DOM components without JSX/virtual DOM

View File

@ -2,18 +2,15 @@
This project uses [jaandrle/bs: The simplest possible build system using executable/bash scripts](
https://github.com/jaandrle/bs).
#### bs/build.js [main|signals] [--no-types|--help]
#### bs/build.js [--minify|--help]
Generates alternative versions of the project (other than native ESM code).
Also generates typescript definitions.
#### bs/docs.js
Generates documentation, from `docs/`. Uses “SSR” technique, using deka-dom-el itself.
For running use `npx serve dist/docs`.
#### bs/lint.sh
Lints size of the project, jshint. See configs:
- `package.json`: key `size-limit`
- `package.json`: key `jshintConfig`
- `.editorconfig`

View File

@ -4,33 +4,30 @@ const files= [ "index", "index-with-signals" ];
$.api("")
.command("main", "Build main files", { default: true })
.option("--no-types", "Also generate d.ts files", false)
.action(function main({ types }){
const regular = build({
.action(async function main(){
const regular = await build({
files,
filesOut,
minify: "no",
types,
});
const min = build({
const min = await build({
files,
filesOut(file, mark= "esm"){
const out= filesOut(file, mark);
const idx= out.indexOf(".");
const idx= out.lastIndexOf(".");
return out.slice(0, idx)+".min"+out.slice(idx);
},
minify: "full",
types,
});
return $.exit(regular + min);
})
.command("signals", "Build only signals (for example for analysis)")
.action(function signals(){
const regular = build({
.action(async function signals(){
const regular = await build({
files: [ "signals" ],
filesOut(file){ return "dist/."+file; },
minify: "no",
iife: false,
dde: false,
});
return $.exit(regular);
})

View File

@ -1,101 +1,65 @@
#!/usr/bin/env -S npx nodejsscript
import { buildSync as esbuildSync } from "esbuild";
import { bundle as bundleDTS } from "dts-bundler";
const css= echo.css`
.info{ color: gray; }
`;
export function build({ files, filesOut, minify= "partial", iife= true, types= true }){
export async function build({ files, filesOut, minify= "partiala", dde= true }){
for(const file_root of files){
const file= file_root+".js";
echo(`Processing ${file} (minified: ${minify})`);
const out= filesOut(file);
esbuild({ file, out, minify });
const esbuild_output= s.$().run([
"npx esbuild '::file::'",
"--platform=neutral",
"--bundle",
minifyOption(minify),
"--legal-comments=inline",
"--packages=external",
"--outfile='::out::'"
].filter(Boolean).join(" "), { file, out });
if(esbuild_output.code)
return $.exit(esbuild_output.code, echo(esbuild_output.stderr));
echoVariant(esbuild_output.stderr.split("\n")[1].trim()+ " (esbuild)");
pipe(
f=> f.replace(/^ +/gm, m=> "\t".repeat(m.length/2)),
f=> s.echo(f).to(out)
)(s.cat(out));
if(types){
const file_dts= file_root+".d.ts";
const file_dts_out= filesOut(file_dts);
echoVariant(file_dts_out, true);
buildDts({
bundle: out,
entry: file_dts,
});
echoVariant(file_dts_out);
}
const file_dts= file_root+".d.ts";
const file_dts_out= filesOut(file_dts);
echoVariant(file_dts_out);
s.echo(bundleDTS(file_dts)).to(file_dts_out);
if(iife) toIIFE(file, file_root, types);
if(dde) await toDDE(out, file_root);
}
return 0;
function toIIFE(file, file_root, types){
const fileMark= "iife";
const name= "DDE";
const out= filesOut(file_root+".js", fileMark);
async function toDDE(file, file_root){
const name= "dde";
const out= filesOut(file_root+".js", name);
echoVariant(`${out} (${file} → globalThis.${name})`)
const params= {
format: "iife",
globalName: name
};
esbuild({ file, out, minify, params });
if(!types) return;
const file_dts= file_root+".d.ts";
const file_dts_out= filesOut(file_dts, fileMark);
echoVariant(file_dts_out, true);
buildDts({
name: fileMark,
bundle: out,
entry: file_dts,
})
echoVariant(file_dts_out);
let content= s.cat(file).toString().split(/export ?{/);
content.splice(1, 0, `\nglobalThis.${name}= {`);
content[2]= content[2]
.replace(/,(?!\n)/g, ",\n")
.replace(/(?<!\n)}/, "\n}")
.replace(/^(\t*)(.*) as ([^,\n]*)(,?)$/mg, "$1$3: $2$4");
s.echo([
`//deka-dom-el library is available via global namespace \`${name}\``,
"(()=> {",
content.join(""),
"})();"
].join("\n")).to(out);
}
}
export function buildDts({ bundle, entry, name }){
const out= bundle.slice(0, bundle.lastIndexOf("."))+".d.ts";
const dts_b_g_output= s.run([
"npx dts-bundle-generator",
"--silent",
"-o ::out::",
!name ? false : ("--umd-module-name "+name),
"--inline-declare-global",
"::entry::"
].filter(Boolean).join(" "), { out, entry });
return dts_b_g_output;
}
export function esbuild({ file, out, minify= "partial", params= {} }){
const esbuild_output= esbuildSync({
entryPoints: [file],
outfile: out,
platform: "neutral",
bundle: true,
legalComments: "inline",
packages: "external",
metafile: true,
...minifyOption(minify),
...params
});
pipe(
f=> f.replace(/^ +/gm, m=> "\t".repeat(m.length/2)),
f=> s.echo(f).to(out)
)(s.cat(out));
echoVariant(metaToLineStatus(esbuild_output.metafile, out));
return esbuild_output;
}
/** @param {"no"|"full"|"partial"} level */
function minifyOption(level= "partial"){
if("no"===level) return { minify: false };
if("full"===level) return { minify: true };
return { minifySyntax: true, minifyIdentifiers: true };
if("no"===level) return undefined;
if("full"===level) return "--minify";
return "--minify-syntax --minify-identifiers";
}
function metaToLineStatus(meta, file){
const status= meta.outputs[file];
if(!status) return `? ${file}: unknown`;
const { bytes }= status;
const kbytes= bytes/1024;
const kbytesR= kbytes.toFixed(2);
return `${file}: ${kbytesR} kB`;
}
function echoVariant(name, todo= false){
if(todo) return echo.use("-R", "~ "+name);
return echo("%c✓ "+name, css.info);
function echoVariant(name){
return echo("%c✓ "+name, css.info+css);
}

View File

@ -2,7 +2,7 @@
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */// editorconfig-checker-disable-line
echo("Building static documentation files…");
echo("Preparing…");
import { path_target, pages as pages_registered, styles, currentPageId, dispatchEvent, t } from "../docs/ssr.js";
import { path_target, pages as pages_registered, styles, dispatchEvent, t } from "../docs/ssr.js";
import { createHTMl } from "./docs/jsdom.js";
import { register, queue } from "../jsdom.js";
const pkg= s.cat("package.json").xargs(JSON.parse);
@ -28,7 +28,6 @@ for(const { id, info } of pages){
);
const { el }= await register(serverDOM.dom);
const { page }= await import(`../docs/${id}.html.js`);
currentPageId(id)
serverDOM.document.body.append(
el(page, { pkg, info }),
);

View File

@ -1,11 +1,5 @@
#!/usr/bin/env bash
set -eou pipefail
# if $1=vim -no-color
one=${1:-''}
additional=''
[ "$one" = 'vim' ] && additional='-no-color'
npx editorconfig-checker -format gcc ${additional}
[ "$one" = 'vim' ] && additional='--reporter unix'
npx jshint index.js src ${additional}
[ "$one" = 'vim' ] && exit 0
npx editorconfig-checker -format gcc
npx size-limit
npx jshint index.js src

1012
dist/dde-with-signals.js vendored Normal file

File diff suppressed because it is too large Load Diff

31
dist/dde-with-signals.min.js vendored Normal file

File diff suppressed because one or more lines are too long

667
dist/dde.js vendored Normal file
View File

@ -0,0 +1,667 @@
//deka-dom-el library is available via global namespace `dde`
(()=> {
// src/helpers.js
function isUndef(value) {
return typeof value === "undefined";
}
function isInstance(obj, cls) {
return obj instanceof cls;
}
function isProtoFrom(obj, cls) {
return Object.prototype.isPrototypeOf.call(cls, obj);
}
function oAssign(...o) {
return Object.assign(...o);
}
function onAbort(signal, listener) {
if (!signal || !isInstance(signal, AbortSignal))
return true;
if (signal.aborted)
return;
signal.addEventListener("abort", listener);
return function cleanUp() {
signal.removeEventListener("abort", listener);
};
}
function observedAttributes(instance, observedAttribute) {
const { observedAttributes: observedAttributes3 = [] } = instance.constructor;
return observedAttributes3.reduce(function(out, name) {
out[kebabToCamel(name)] = observedAttribute(instance, name);
return out;
}, {});
}
function kebabToCamel(name) {
return name.replace(/-./g, (x) => x[1].toUpperCase());
}
// src/signals-lib/common.js
var signals_global = {
/**
* Checks if a value is a signal
* @param {any} attributes - Value to check
* @returns {boolean} Whether the value is a signal
*/
isSignal(attributes) {
return false;
},
/**
* Processes an attribute that might be reactive
* @param {Element} obj - Element that owns the attribute
* @param {string} key - Attribute name
* @param {any} attr - Attribute value
* @param {Function} set - Function to set the attribute
* @returns {any} Processed attribute value
*/
processReactiveAttribute(obj, key, attr, set) {
return attr;
}
};
function registerReactivity(def, global = true) {
if (global) return oAssign(signals_global, def);
Object.setPrototypeOf(def, signals_global);
return def;
}
function signals(_this) {
return isProtoFrom(_this, signals_global) && _this !== signals_global ? _this : signals_global;
}
// src/dom-common.js
var enviroment = {
setDeleteAttr,
ssr: "",
D: globalThis.document,
F: globalThis.DocumentFragment,
H: globalThis.HTMLElement,
S: globalThis.SVGElement,
M: globalThis.MutationObserver,
q: (p) => p || Promise.resolve()
};
function setDeleteAttr(obj, prop, val) {
Reflect.set(obj, prop, val);
if (!isUndef(val)) return;
Reflect.deleteProperty(obj, prop);
if (isInstance(obj, enviroment.H) && obj.getAttribute(prop) === "undefined")
return obj.removeAttribute(prop);
if (Reflect.get(obj, prop) === "undefined")
return Reflect.set(obj, prop, "");
}
var keyLTE = "__dde_lifecyclesToEvents";
var evc = "dde:connected";
var evd = "dde:disconnected";
var eva = "dde:attributeChanged";
// src/dom.js
function queue(promise) {
return enviroment.q(promise);
}
var scopes = [{
get scope() {
return enviroment.D.body;
},
host: (c) => c ? c(enviroment.D.body) : enviroment.D.body,
prevent: true
}];
var scope = {
/**
* Gets the current scope
* @returns {Object} Current scope context
*/
get current() {
return scopes[scopes.length - 1];
},
/**
* Gets the host element of the current scope
* @returns {Function} Host accessor function
*/
get host() {
return this.current.host;
},
/**
* Prevents default behavior in the current scope
* @returns {Object} Current scope context
*/
preventDefault() {
const { current } = this;
current.prevent = true;
return current;
},
/**
* Gets a copy of the current scope stack
* @returns {Array} Copy of scope stack
*/
get state() {
return [...scopes];
},
/**
* Pushes a new scope to the stack
* @param {Object} [s={}] - Scope object to push
* @returns {number} New length of the scope stack
*/
push(s = {}) {
return scopes.push(oAssign({}, this.current, { prevent: false }, s));
},
/**
* Pushes the root scope to the stack
* @returns {number} New length of the scope stack
*/
pushRoot() {
return scopes.push(scopes[0]);
},
/**
* Pops the current scope from the stack
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
*/
pop() {
if (scopes.length === 1) return;
return scopes.pop();
}
};
function append(...els) {
this.appendOriginal(...els);
return this;
}
function chainableAppend(el) {
if (el.append === append) return el;
el.appendOriginal = el.append;
el.append = append;
return el;
}
var namespace;
function createElement(tag, attributes, ...addons) {
const s = signals(this);
let scoped = 0;
let el, el_host;
if (Object(attributes) !== attributes || s.isSignal(attributes))
attributes = { textContent: attributes };
switch (true) {
case typeof tag === "function": {
scoped = 1;
const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
scope.push({ scope: tag, host });
el = tag(attributes || void 0);
const is_fragment = isInstance(el, enviroment.F);
if (el.nodeName === "#comment") break;
const el_mark = createElement.mark({
type: "component",
name: tag.name,
host: is_fragment ? "this" : "parentElement"
});
el.prepend(el_mark);
if (is_fragment) el_host = el_mark;
break;
}
case tag === "#text":
el = assign.call(this, enviroment.D.createTextNode(""), attributes);
break;
case (tag === "<>" || !tag):
el = assign.call(this, enviroment.D.createDocumentFragment(), attributes);
break;
case Boolean(namespace):
el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes);
break;
case !el:
el = assign.call(this, enviroment.D.createElement(tag), attributes);
}
chainableAppend(el);
if (!el_host) el_host = el;
addons.forEach((c) => c(el_host));
if (scoped) scope.pop();
scoped = 2;
return el;
}
createElement.mark = function(attrs, is_open = false) {
attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" ");
const end = is_open ? "" : "/";
const out = enviroment.D.createComment(`<dde:mark ${attrs}${enviroment.ssr}${end}>`);
if (is_open) out.end = enviroment.D.createComment("</dde:mark>");
return out;
};
function createElementNS(ns) {
const _this = this;
return function createElementNSCurried(...rest) {
namespace = ns;
const el = createElement.call(_this, ...rest);
namespace = void 0;
return el;
};
}
function simulateSlots(element, root = element) {
const mark_e = "\xB9\u2070", mark_s = "\u2713";
const slots = Object.fromEntries(
Array.from(root.querySelectorAll("slot")).filter((s) => !s.name.endsWith(mark_e)).map((s) => [s.name += mark_e, s])
);
element.append = new Proxy(element.append, {
apply(orig, _, els) {
if (els[0] === root) return orig.apply(element, els);
for (const el of els) {
const name = (el.slot || "") + mark_e;
try {
elementAttribute(el, "remove", "slot");
} catch (_error) {
}
const slot = slots[name];
if (!slot) return;
if (!slot.name.startsWith(mark_s)) {
slot.childNodes.forEach((c) => c.remove());
slot.name = mark_s + name;
}
slot.append(el);
}
element.append = orig;
return element;
}
});
if (element !== root) {
const els = Array.from(element.childNodes);
element.append(...els);
}
return root;
}
var assign_context = /* @__PURE__ */ new WeakMap();
var { setDeleteAttr: setDeleteAttr2 } = enviroment;
function assign(element, ...attributes) {
if (!attributes.length) return element;
assign_context.set(element, assignContext(element, this));
for (const [key, value] of Object.entries(oAssign({}, ...attributes)))
assignAttribute.call(this, element, key, value);
assign_context.delete(element);
return element;
}
function assignAttribute(element, key, value) {
const { setRemoveAttr, s } = assignContext(element, this);
const _this = this;
value = s.processReactiveAttribute(
element,
key,
value,
(key2, value2) => assignAttribute.call(_this, element, key2, value2)
);
const [k] = key;
if ("=" === k) return setRemoveAttr(key.slice(1), value);
if ("." === k) return setDelete(element, key.slice(1), value);
if (/(aria|data)([A-Z])/.test(key)) {
key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
return setRemoveAttr(key, value);
}
if ("className" === key) key = "class";
switch (key) {
case "xlink:href":
return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink");
case "textContent":
return setDeleteAttr2(element, key, value);
case "style":
if (typeof value !== "object") break;
/* falls through */
case "dataset":
return forEachEntries(s, key, element, value, setDelete.bind(null, element[key]));
case "ariaset":
return forEachEntries(s, key, element, value, (key2, val) => setRemoveAttr("aria-" + key2, val));
case "classList":
return classListDeclarative.call(_this, element, value);
}
return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value);
}
function assignContext(element, _this) {
if (assign_context.has(element)) return assign_context.get(element);
const is_svg = isInstance(element, enviroment.S);
const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
const s = signals(_this);
return { setRemoveAttr, s };
}
function classListDeclarative(element, toggle) {
const s = signals(this);
forEachEntries(
s,
"classList",
element,
toggle,
(class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val))
);
return element;
}
function elementAttribute(element, op, key, value) {
if (isInstance(element, enviroment.H))
return element[op + "Attribute"](key, value);
return element[op + "AttributeNS"](null, key, value);
}
function isPropSetter(el, key) {
if (!(key in el)) return false;
const des = getPropDescriptor(el, key);
return !isUndef(des.set);
}
function getPropDescriptor(p, key) {
p = Object.getPrototypeOf(p);
if (!p) return {};
const des = Object.getOwnPropertyDescriptor(p, key);
if (!des) return getPropDescriptor(p, key);
return des;
}
function forEachEntries(s, target, element, obj, cb) {
const S = String;
if (typeof obj !== "object" || obj === null) return;
return Object.entries(obj).forEach(function process([key, val]) {
if (!key) return;
key = new S(key);
key.target = target;
val = s.processReactiveAttribute(element, key, val, cb);
cb(key, val);
});
}
function setRemove(obj, prop, key, val) {
return obj[(isUndef(val) ? "remove" : "set") + prop](key, val);
}
function setRemoveNS(obj, prop, key, val, ns = null) {
return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, val);
}
function setDelete(obj, key, val) {
Reflect.set(obj, key, val);
if (!isUndef(val)) return;
return Reflect.deleteProperty(obj, key);
}
// src/events-observer.js
var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
get() {
return () => {
};
}
});
function connectionsChangesObserverConstructor() {
const store = /* @__PURE__ */ new Map();
let is_observing = false;
const observerListener = (stop2) => function(mutations) {
for (const mutation of mutations) {
if (mutation.type !== "childList") continue;
if (observerAdded(mutation.addedNodes, true)) {
stop2();
continue;
}
if (observerRemoved(mutation.removedNodes, true))
stop2();
}
};
const observer = new enviroment.M(observerListener(stop));
return {
/**
* Creates an observer for a specific element
* @param {Element} element - Element to observe
* @returns {Function} Cleanup function
*/
observe(element) {
const o = new enviroment.M(observerListener(() => {
}));
o.observe(element, { childList: true, subtree: true });
return () => o.disconnect();
},
/**
* Register a connection listener for an element
* @param {Element} element - Element to watch
* @param {Function} listener - Callback for connection event
*/
onConnected(element, listener) {
start();
const listeners = getElementStore(element);
if (listeners.connected.has(listener)) return;
listeners.connected.add(listener);
listeners.length_c += 1;
},
/**
* Unregister a connection listener
* @param {Element} element - Element being watched
* @param {Function} listener - Callback to remove
*/
offConnected(element, listener) {
if (!store.has(element)) return;
const ls = store.get(element);
if (!ls.connected.has(listener)) return;
ls.connected.delete(listener);
ls.length_c -= 1;
cleanWhenOff(element, ls);
},
/**
* Register a disconnection listener for an element
* @param {Element} element - Element to watch
* @param {Function} listener - Callback for disconnection event
*/
onDisconnected(element, listener) {
start();
const listeners = getElementStore(element);
if (listeners.disconnected.has(listener)) return;
listeners.disconnected.add(listener);
listeners.length_d += 1;
},
/**
* Unregister a disconnection listener
* @param {Element} element - Element being watched
* @param {Function} listener - Callback to remove
*/
offDisconnected(element, listener) {
if (!store.has(element)) return;
const ls = store.get(element);
ls.disconnected.delete(listener);
ls.length_d -= 1;
cleanWhenOff(element, ls);
}
};
function cleanWhenOff(element, ls) {
if (ls.length_c || ls.length_d)
return;
store.delete(element);
stop();
}
function getElementStore(element) {
if (store.has(element)) return store.get(element);
const out = {
connected: /* @__PURE__ */ new WeakSet(),
length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0
};
store.set(element, out);
return out;
}
function start() {
if (is_observing) return;
is_observing = true;
observer.observe(enviroment.D.body, { childList: true, subtree: true });
}
function stop() {
if (!is_observing || store.size) return;
is_observing = false;
observer.disconnect();
}
function requestIdle() {
return new Promise(function(resolve) {
(requestIdleCallback || requestAnimationFrame)(resolve);
});
}
async function collectChildren(element) {
if (store.size > 30)
await requestIdle();
const out = [];
if (!isInstance(element, Node)) return out;
for (const el of store.keys()) {
if (el === element || !isInstance(el, Node)) continue;
if (element.contains(el))
out.push(el);
}
return out;
}
function observerAdded(addedNodes, is_root) {
let out = false;
for (const element of addedNodes) {
if (is_root) collectChildren(element).then(observerAdded);
if (!store.has(element)) continue;
const ls = store.get(element);
if (!ls.length_c) continue;
element.dispatchEvent(new Event(evc));
ls.connected = /* @__PURE__ */ new WeakSet();
ls.length_c = 0;
if (!ls.length_d) store.delete(element);
out = true;
}
return out;
}
function observerRemoved(removedNodes, is_root) {
let out = false;
for (const element of removedNodes) {
if (is_root) collectChildren(element).then(observerRemoved);
if (!store.has(element)) continue;
const ls = store.get(element);
if (!ls.length_d) continue;
(globalThis.queueMicrotask || setTimeout)(dispatchRemove(element));
out = true;
}
return out;
}
function dispatchRemove(element) {
return () => {
if (element.isConnected) return;
element.dispatchEvent(new Event(evd));
store.delete(element);
};
}
}
// src/customElement.js
function customElementRender(target, render, props = observedAttributes2) {
const custom_element = target.host || target;
scope.push({
scope: custom_element,
host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element
});
if (typeof props === "function") props = props.call(custom_element, custom_element);
const is_lte = custom_element[keyLTE];
if (!is_lte) lifecyclesToEvents(custom_element);
const out = render.call(custom_element, props);
if (!is_lte) custom_element.dispatchEvent(new Event(evc));
if (target.nodeType === 11 && typeof target.mode === "string")
custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true });
scope.pop();
return target.append(out);
}
function lifecyclesToEvents(class_declaration) {
wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) {
target.apply(thisArg, detail);
thisArg.dispatchEvent(new Event(evc));
});
wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) {
target.apply(thisArg, detail);
(globalThis.queueMicrotask || setTimeout)(
() => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd))
);
});
wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) {
const [attribute, , value] = detail;
thisArg.dispatchEvent(new CustomEvent(eva, {
detail: [attribute, value]
}));
target.apply(thisArg, detail);
});
class_declaration.prototype[keyLTE] = true;
return class_declaration;
}
function wrapMethod(obj, method, apply) {
obj[method] = new Proxy(obj[method] || (() => {
}), { apply });
}
function observedAttributes2(instance) {
return observedAttributes(instance, (i, n) => i.getAttribute(n));
}
// src/events.js
function dispatchEvent(name, options, host) {
if (typeof options === "function") {
host = options;
options = null;
}
if (!options) options = {};
return function dispatch(element, ...d) {
if (host) {
d.unshift(element);
element = typeof host === "function" ? host() : host;
}
const event = d.length ? new CustomEvent(name, oAssign({ detail: d[0] }, options)) : new Event(name, options);
return element.dispatchEvent(event);
};
}
function on(event, listener, options) {
return function registerElement(element) {
element.addEventListener(event, listener, options);
return element;
};
}
var lifeOptions = (obj) => oAssign({}, typeof obj === "object" ? obj : null, { once: true });
on.connected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evc, listener, options);
if (element[keyLTE]) return element;
if (element.isConnected) return element.dispatchEvent(new Event(evc)), element;
const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener));
if (c) c_ch_o.onConnected(element, listener);
return element;
};
};
on.disconnected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evd, listener, options);
if (element[keyLTE]) return element;
const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener));
if (c) c_ch_o.onDisconnected(element, listener);
return element;
};
};
var store_abort = /* @__PURE__ */ new WeakMap();
on.disconnectedAsAbort = function(host) {
if (store_abort.has(host)) return store_abort.get(host);
const a = new AbortController();
store_abort.set(host, a);
host(on.disconnected(() => a.abort()));
return a.signal;
};
var els_attribute_store = /* @__PURE__ */ new WeakSet();
on.attributeChanged = function(listener, options) {
if (typeof options !== "object")
options = {};
return function registerElement(element) {
element.addEventListener(eva, listener, options);
if (element[keyLTE] || els_attribute_store.has(element))
return element;
if (!enviroment.M) return element;
const observer = new enviroment.M(function(mutations) {
for (const { attributeName, target } of mutations)
target.dispatchEvent(
new CustomEvent(eva, { detail: [attributeName, target.getAttribute(attributeName)] })
);
});
const c = onAbort(options.signal, () => observer.disconnect());
if (c) observer.observe(element, { attributes: true });
return element;
};
};
globalThis.dde= {
assign,
assignAttribute,
chainableAppend,
classListDeclarative,
createElement,
createElementNS,
customElementRender,
customElementWithDDE: lifecyclesToEvents,
dispatchEvent,
el: createElement,
elNS: createElementNS,
elementAttribute,
lifecyclesToEvents,
observedAttributes: observedAttributes2,
on,
queue,
registerReactivity,
scope,
simulateSlots
};
})();

25
dist/dde.min.js vendored Normal file

File diff suppressed because one or more lines are too long

641
dist/esm-with-signals.d.min.ts vendored Normal file
View File

@ -0,0 +1,641 @@
declare global{ /* ddeSignal */ }
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap]
declare global {
type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node>= (element: El)=> any;
type ddeString= string | ddeSignal<string>
type ddeStringable= ddeString | number | ddeSignal<number>
}
type PascalCase=
`${Capitalize<string>}${string}`;
type AttrsModified= {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString
| Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }>
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string,-1|0|1|boolean|ddeSignal<-1|0|1|boolean>>,
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>,
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString>
& Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
type IsReadonly<T, K extends keyof T> =
T extends { readonly [P in K]: T[K] } ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
type ElementAttributes<T extends SupportedElement>= Partial<{
[K in keyof _fromElsInterfaces<T>]:
_fromElsInterfaces<T>[K] extends ((...p: any[])=> any)
? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>)=>
ddeSignal<ReturnType<_fromElsInterfaces<T>[K]>>)
: (IsReadonly<_fromElsInterfaces<T>, K> extends false
? _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]>
: ddeStringable)
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(
element: El,
classList: AttrsModified["classList"]
): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(
element: El,
attr: ATT,
value: ElementAttributes<El>[ATT]
): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(
attrs: { type: "component"|"reactive"|"later", name?: string, host?: "this"|"parentElement" },
is_open?: boolean
): Comment;
}
export function el<
A extends ddeComponentAttributes,
EL extends SupportedElement | ddeDocumentFragment
>(
component: (attr: A, ...rest: any[])=> EL,
attrs?: NoInfer<A>,
...addons: ddeElementAddon<EL>[]
): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? EL
: ( EL extends ddeDocumentFragment ? EL : ddeHTMLElement )
export function el<
A extends { textContent: ddeStringable },
EL extends SupportedElement | ddeDocumentFragment
>(
component: (attr: A, ...rest: any[])=> EL,
attrs?: NoInfer<A>["textContent"],
...addons: ddeElementAddon<EL>[]
): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? EL
: ( EL extends ddeDocumentFragment ? EL : ddeHTMLElement )
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap,
>(
tag_name: TAG,
attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable,
...addons: ddeElementAddon<
ExtendedHTMLElementTagNameMap[NoInfer<TAG>]
>[], // TODO: for now addons must have the same element
): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement
export function el(
tag_name?: "<>",
): ddeDocumentFragment
export function el(
tag_name: string,
attrs?: ElementAttributes<HTMLElement> | ddeStringable,
...addons: ddeElementAddon<HTMLElement>[]
): ddeHTMLElement
export { el as createElement }
export function elNS(
namespace: "http://www.w3.org/2000/svg"
): <
TAG extends keyof SVGElementTagNameMap & string,
EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ),
>(
tag_name: TAG,
attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement
export function elNS(
namespace: "http://www.w3.org/1998/Math/MathML"
): <
TAG extends keyof MathMLElementTagNameMap & string,
EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>(
tag_name: TAG,
attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | ddeSignal<EL[key]> | string | number | boolean
}>,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> ddeMathMLElement
export function elNS(
namespace: string
): (
tag_name: string,
attrs?: string | ddeStringable | Record<string, any>,
...addons: ddeElementAddon<SupportedElement>[]
)=> SupportedElement
export { elNS as createElementNS }
export function chainableAppend<EL extends SupportedElement>(el: EL): EL;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
root: EL,
): EL
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
el: HTMLElement,
body: EL,
): EL
export function dispatchEvent(name: keyof DocumentEventMap | string, element: SupportedElement):
(data?: any)=> void;
export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit):
(element: SupportedElement, data?: any)=> void;
export function dispatchEvent(
name: keyof DocumentEventMap | string,
options: EventInit | null,
element: SupportedElement | (()=> SupportedElement)
): (data?: any)=> void;
interface On{
/** Listens to the DOM event. See {@link Document.addEventListener} */
<
Event extends keyof DocumentEventMap,
EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
>(
type: Event,
listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: DocumentEventMap[Event]) => any,
options?: AddEventListenerOptions
) : EE;
<
EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
>(
type: string,
listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent ) => any,
options?: AddEventListenerOptions
) : EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
connected<
EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )
>(
listener: (this: El, event: CustomEvent<El>) => any,
options?: AddEventListenerOptions
) : EE;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
disconnected<
EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )
>(
listener: (this: El, event: CustomEvent<void>) => any,
options?: AddEventListenerOptions
) : EE;
/** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
attributeChanged<
EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )
>(
listener: (this: El, event: CustomEvent<[ string, string ]>) => any,
options?: AddEventListenerOptions
) : EE;
}
export const on: On;
type Scope= {
scope: Node | Function | Object,
host: ddeElementAddon<any>,
custom_element: false | HTMLElement,
prevent: boolean
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope,
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T,
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: (...addons: ddeElementAddon<SupportedElement>[])=> HTMLElement,
state: Scope[],
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
};
export function customElementRender<
EL extends HTMLElement,
P extends any = Record<string, string | ddeSignal<string>>
>(
target: ShadowRoot | EL,
render: (props: P)=> SupportedElement | DocumentFragment,
props?: P | ((el: EL)=> P)
): EL
export function customElementWithDDE<EL extends (new ()=> HTMLElement)>(custom_element: EL): EL
export function lifecyclesToEvents<EL extends (new ()=> HTMLElement)>(custom_element: EL): EL
export function observedAttributes(custom_element: HTMLElement): Record<string, string>
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/* TypeScript MEH */
declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }
interface ddeMathMLElement extends MathMLElement{ append: ddeAppend<ddeMathMLElement>; }
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; }
interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; }
interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; }
interface ddeHTMLBaseElement extends HTMLBaseElement{ append: ddeAppend<ddeHTMLBaseElement>; }
interface ddeHTMLQuoteElement extends HTMLQuoteElement{ append: ddeAppend<ddeHTMLQuoteElement>; }
interface ddeHTMLBodyElement extends HTMLBodyElement{ append: ddeAppend<ddeHTMLBodyElement>; }
interface ddeHTMLBRElement extends HTMLBRElement{ append: ddeAppend<ddeHTMLBRElement>; }
interface ddeHTMLButtonElement extends HTMLButtonElement{ append: ddeAppend<ddeHTMLButtonElement>; }
interface ddeHTMLCanvasElement extends HTMLCanvasElement{ append: ddeAppend<ddeHTMLCanvasElement>; }
interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement{ append: ddeAppend<ddeHTMLTableCaptionElement>; }
interface ddeHTMLTableColElement extends HTMLTableColElement{ append: ddeAppend<ddeHTMLTableColElement>; }
interface ddeHTMLTableColElement extends HTMLTableColElement{ append: ddeAppend<ddeHTMLTableColElement>; }
interface ddeHTMLDataElement extends HTMLDataElement{ append: ddeAppend<ddeHTMLDataElement>; }
interface ddeHTMLDataListElement extends HTMLDataListElement{ append: ddeAppend<ddeHTMLDataListElement>; }
interface ddeHTMLModElement extends HTMLModElement{ append: ddeAppend<ddeHTMLModElement>; }
interface ddeHTMLDetailsElement extends HTMLDetailsElement{ append: ddeAppend<ddeHTMLDetailsElement>; }
interface ddeHTMLDialogElement extends HTMLDialogElement{ append: ddeAppend<ddeHTMLDialogElement>; }
interface ddeHTMLDivElement extends HTMLDivElement{ append: ddeAppend<ddeHTMLDivElement>; }
interface ddeHTMLDListElement extends HTMLDListElement{ append: ddeAppend<ddeHTMLDListElement>; }
interface ddeHTMLEmbedElement extends HTMLEmbedElement{ append: ddeAppend<ddeHTMLEmbedElement>; }
interface ddeHTMLFieldSetElement extends HTMLFieldSetElement{ append: ddeAppend<ddeHTMLFieldSetElement>; }
interface ddeHTMLFormElement extends HTMLFormElement{ append: ddeAppend<ddeHTMLFormElement>; }
interface ddeHTMLHeadingElement extends HTMLHeadingElement{ append: ddeAppend<ddeHTMLHeadingElement>; }
interface ddeHTMLHeadElement extends HTMLHeadElement{ append: ddeAppend<ddeHTMLHeadElement>; }
interface ddeHTMLHRElement extends HTMLHRElement{ append: ddeAppend<ddeHTMLHRElement>; }
interface ddeHTMLHtmlElement extends HTMLHtmlElement{ append: ddeAppend<ddeHTMLHtmlElement>; }
interface ddeHTMLIFrameElement extends HTMLIFrameElement{ append: ddeAppend<ddeHTMLIFrameElement>; }
interface ddeHTMLImageElement extends HTMLImageElement{ append: ddeAppend<ddeHTMLImageElement>; }
interface ddeHTMLInputElement extends HTMLInputElement{ append: ddeAppend<ddeHTMLInputElement>; }
interface ddeHTMLLabelElement extends HTMLLabelElement{ append: ddeAppend<ddeHTMLLabelElement>; }
interface ddeHTMLLegendElement extends HTMLLegendElement{ append: ddeAppend<ddeHTMLLegendElement>; }
interface ddeHTMLLIElement extends HTMLLIElement{ append: ddeAppend<ddeHTMLLIElement>; }
interface ddeHTMLLinkElement extends HTMLLinkElement{ append: ddeAppend<ddeHTMLLinkElement>; }
interface ddeHTMLMapElement extends HTMLMapElement{ append: ddeAppend<ddeHTMLMapElement>; }
interface ddeHTMLMenuElement extends HTMLMenuElement{ append: ddeAppend<ddeHTMLMenuElement>; }
interface ddeHTMLMetaElement extends HTMLMetaElement{ append: ddeAppend<ddeHTMLMetaElement>; }
interface ddeHTMLMeterElement extends HTMLMeterElement{ append: ddeAppend<ddeHTMLMeterElement>; }
interface ddeHTMLObjectElement extends HTMLObjectElement{ append: ddeAppend<ddeHTMLObjectElement>; }
interface ddeHTMLOListElement extends HTMLOListElement{ append: ddeAppend<ddeHTMLOListElement>; }
interface ddeHTMLOptGroupElement extends HTMLOptGroupElement{ append: ddeAppend<ddeHTMLOptGroupElement>; }
interface ddeHTMLOptionElement extends HTMLOptionElement{ append: ddeAppend<ddeHTMLOptionElement>; }
interface ddeHTMLOutputElement extends HTMLOutputElement{ append: ddeAppend<ddeHTMLOutputElement>; }
interface ddeHTMLParagraphElement extends HTMLParagraphElement{ append: ddeAppend<ddeHTMLParagraphElement>; }
interface ddeHTMLPictureElement extends HTMLPictureElement{ append: ddeAppend<ddeHTMLPictureElement>; }
interface ddeHTMLPreElement extends HTMLPreElement{ append: ddeAppend<ddeHTMLPreElement>; }
interface ddeHTMLProgressElement extends HTMLProgressElement{ append: ddeAppend<ddeHTMLProgressElement>; }
interface ddeHTMLScriptElement extends HTMLScriptElement{ append: ddeAppend<ddeHTMLScriptElement>; }
interface ddeHTMLSelectElement extends HTMLSelectElement{ append: ddeAppend<ddeHTMLSelectElement>; }
interface ddeHTMLSlotElement extends HTMLSlotElement{ append: ddeAppend<ddeHTMLSlotElement>; }
interface ddeHTMLSourceElement extends HTMLSourceElement{ append: ddeAppend<ddeHTMLSourceElement>; }
interface ddeHTMLSpanElement extends HTMLSpanElement{ append: ddeAppend<ddeHTMLSpanElement>; }
interface ddeHTMLStyleElement extends HTMLStyleElement{ append: ddeAppend<ddeHTMLStyleElement>; }
interface ddeHTMLTableElement extends HTMLTableElement{ append: ddeAppend<ddeHTMLTableElement>; }
interface ddeHTMLTableSectionElement extends HTMLTableSectionElement{ append: ddeAppend<ddeHTMLTableSectionElement>; }
interface ddeHTMLTableCellElement extends HTMLTableCellElement{ append: ddeAppend<ddeHTMLTableCellElement>; }
interface ddeHTMLTemplateElement extends HTMLTemplateElement{ append: ddeAppend<ddeHTMLTemplateElement>; }
interface ddeHTMLTextAreaElement extends HTMLTextAreaElement{ append: ddeAppend<ddeHTMLTextAreaElement>; }
interface ddeHTMLTableCellElement extends HTMLTableCellElement{ append: ddeAppend<ddeHTMLTableCellElement>; }
interface ddeHTMLTimeElement extends HTMLTimeElement{ append: ddeAppend<ddeHTMLTimeElement>; }
interface ddeHTMLTitleElement extends HTMLTitleElement{ append: ddeAppend<ddeHTMLTitleElement>; }
interface ddeHTMLTableRowElement extends HTMLTableRowElement{ append: ddeAppend<ddeHTMLTableRowElement>; }
interface ddeHTMLTrackElement extends HTMLTrackElement{ append: ddeAppend<ddeHTMLTrackElement>; }
interface ddeHTMLUListElement extends HTMLUListElement{ append: ddeAppend<ddeHTMLUListElement>; }
interface ddeHTMLVideoElement extends HTMLVideoElement{ append: ddeAppend<ddeHTMLVideoElement>; }
interface ddeSVGAElement extends SVGAElement{ append: ddeAppend<ddeSVGAElement>; }
interface ddeSVGAnimateElement extends SVGAnimateElement{ append: ddeAppend<ddeSVGAnimateElement>; }
interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement{ append: ddeAppend<ddeSVGAnimateMotionElement>; }
interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement{ append: ddeAppend<ddeSVGAnimateTransformElement>; }
interface ddeSVGCircleElement extends SVGCircleElement{ append: ddeAppend<ddeSVGCircleElement>; }
interface ddeSVGClipPathElement extends SVGClipPathElement{ append: ddeAppend<ddeSVGClipPathElement>; }
interface ddeSVGDefsElement extends SVGDefsElement{ append: ddeAppend<ddeSVGDefsElement>; }
interface ddeSVGDescElement extends SVGDescElement{ append: ddeAppend<ddeSVGDescElement>; }
interface ddeSVGEllipseElement extends SVGEllipseElement{ append: ddeAppend<ddeSVGEllipseElement>; }
interface ddeSVGFEBlendElement extends SVGFEBlendElement{ append: ddeAppend<ddeSVGFEBlendElement>; }
interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement{ append: ddeAppend<ddeSVGFEColorMatrixElement>; }
interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement{ append: ddeAppend<ddeSVGFEComponentTransferElement>; }
interface ddeSVGFECompositeElement extends SVGFECompositeElement{ append: ddeAppend<ddeSVGFECompositeElement>; }
interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement{ append: ddeAppend<ddeSVGFEConvolveMatrixElement>; }
interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement{ append: ddeAppend<ddeSVGFEDiffuseLightingElement>; }
interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement{ append: ddeAppend<ddeSVGFEDisplacementMapElement>; }
interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement{ append: ddeAppend<ddeSVGFEDistantLightElement>; }
interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement{ append: ddeAppend<ddeSVGFEDropShadowElement>; }
interface ddeSVGFEFloodElement extends SVGFEFloodElement{ append: ddeAppend<ddeSVGFEFloodElement>; }
interface ddeSVGFEFuncAElement extends SVGFEFuncAElement{ append: ddeAppend<ddeSVGFEFuncAElement>; }
interface ddeSVGFEFuncBElement extends SVGFEFuncBElement{ append: ddeAppend<ddeSVGFEFuncBElement>; }
interface ddeSVGFEFuncGElement extends SVGFEFuncGElement{ append: ddeAppend<ddeSVGFEFuncGElement>; }
interface ddeSVGFEFuncRElement extends SVGFEFuncRElement{ append: ddeAppend<ddeSVGFEFuncRElement>; }
interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement{ append: ddeAppend<ddeSVGFEGaussianBlurElement>; }
interface ddeSVGFEImageElement extends SVGFEImageElement{ append: ddeAppend<ddeSVGFEImageElement>; }
interface ddeSVGFEMergeElement extends SVGFEMergeElement{ append: ddeAppend<ddeSVGFEMergeElement>; }
interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement{ append: ddeAppend<ddeSVGFEMergeNodeElement>; }
interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement{ append: ddeAppend<ddeSVGFEMorphologyElement>; }
interface ddeSVGFEOffsetElement extends SVGFEOffsetElement{ append: ddeAppend<ddeSVGFEOffsetElement>; }
interface ddeSVGFEPointLightElement extends SVGFEPointLightElement{ append: ddeAppend<ddeSVGFEPointLightElement>; }
interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement{ append: ddeAppend<ddeSVGFESpecularLightingElement>; }
interface ddeSVGFESpotLightElement extends SVGFESpotLightElement{ append: ddeAppend<ddeSVGFESpotLightElement>; }
interface ddeSVGFETileElement extends SVGFETileElement{ append: ddeAppend<ddeSVGFETileElement>; }
interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement{ append: ddeAppend<ddeSVGFETurbulenceElement>; }
interface ddeSVGFilterElement extends SVGFilterElement{ append: ddeAppend<ddeSVGFilterElement>; }
interface ddeSVGForeignObjectElement extends SVGForeignObjectElement{ append: ddeAppend<ddeSVGForeignObjectElement>; }
interface ddeSVGGElement extends SVGGElement{ append: ddeAppend<ddeSVGGElement>; }
interface ddeSVGImageElement extends SVGImageElement{ append: ddeAppend<ddeSVGImageElement>; }
interface ddeSVGLineElement extends SVGLineElement{ append: ddeAppend<ddeSVGLineElement>; }
interface ddeSVGLinearGradientElement extends SVGLinearGradientElement{ append: ddeAppend<ddeSVGLinearGradientElement>; }
interface ddeSVGMarkerElement extends SVGMarkerElement{ append: ddeAppend<ddeSVGMarkerElement>; }
interface ddeSVGMaskElement extends SVGMaskElement{ append: ddeAppend<ddeSVGMaskElement>; }
interface ddeSVGMetadataElement extends SVGMetadataElement{ append: ddeAppend<ddeSVGMetadataElement>; }
interface ddeSVGMPathElement extends SVGMPathElement{ append: ddeAppend<ddeSVGMPathElement>; }
interface ddeSVGPathElement extends SVGPathElement{ append: ddeAppend<ddeSVGPathElement>; }
interface ddeSVGPatternElement extends SVGPatternElement{ append: ddeAppend<ddeSVGPatternElement>; }
interface ddeSVGPolygonElement extends SVGPolygonElement{ append: ddeAppend<ddeSVGPolygonElement>; }
interface ddeSVGPolylineElement extends SVGPolylineElement{ append: ddeAppend<ddeSVGPolylineElement>; }
interface ddeSVGRadialGradientElement extends SVGRadialGradientElement{ append: ddeAppend<ddeSVGRadialGradientElement>; }
interface ddeSVGRectElement extends SVGRectElement{ append: ddeAppend<ddeSVGRectElement>; }
interface ddeSVGScriptElement extends SVGScriptElement{ append: ddeAppend<ddeSVGScriptElement>; }
interface ddeSVGSetElement extends SVGSetElement{ append: ddeAppend<ddeSVGSetElement>; }
interface ddeSVGStopElement extends SVGStopElement{ append: ddeAppend<ddeSVGStopElement>; }
interface ddeSVGStyleElement extends SVGStyleElement{ append: ddeAppend<ddeSVGStyleElement>; }
interface ddeSVGSVGElement extends SVGSVGElement{ append: ddeAppend<ddeSVGSVGElement>; }
interface ddeSVGSwitchElement extends SVGSwitchElement{ append: ddeAppend<ddeSVGSwitchElement>; }
interface ddeSVGSymbolElement extends SVGSymbolElement{ append: ddeAppend<ddeSVGSymbolElement>; }
interface ddeSVGTextElement extends SVGTextElement{ append: ddeAppend<ddeSVGTextElement>; }
interface ddeSVGTextPathElement extends SVGTextPathElement{ append: ddeAppend<ddeSVGTextPathElement>; }
interface ddeSVGTitleElement extends SVGTitleElement{ append: ddeAppend<ddeSVGTitleElement>; }
interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; }
interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; }
interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; }
// editorconfig-checker-enable
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V): V;
toJSON(): V;
valueOf(): V;
}
type Action<V>= (this: { value: V, stopPropagation(): void }, ...a: any[])=> typeof signal._ | void;
//type SymbolSignal= Symbol;
type SymbolOnclear= symbol;
type Actions<V>= Record<string | SymbolOnclear, Action<V>>;
type OnListenerOptions= Pick<AddEventListenerOptions, "signal"> & { first_time?: boolean };
interface signal{
_: Symbol
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends ()=> any>(computation: V): Signal<ReturnType<V>, {}>
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(
signal: S,
name: N,
...params: A[N] extends (...args: infer P)=> any ? P : never
): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T)=> void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
}
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S)=> Element | Element[] | DocumentFragment): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
export const signal: signal;
export const S: signal;
declare global {
type ddeSignal<T, A= {}>= Signal<T, A>;
type ddeAction<V>= Action<V>
type ddeActions<V>= Actions<V>
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,860 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V, force?: boolean): V;
toJSON(): V;
valueOf(): V;
}
export type Action<V> = (this: {
value: V;
stopPropagation(): void;
}, ...a: any[]) => typeof signal._ | void;
//type SymbolSignal= Symbol;
export type SymbolOnclear = symbol;
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
first_time?: boolean;
};
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
export interface signal {
_: Symbol;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
};
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
export const signal: signal;
export const S: signal;
declare global {
type ddeSignal<T, A = {}> = Signal<T, A>;
type ddeAction<V> = Action<V>;
type ddeActions<V> = Actions<V>;
}
export type CustomElementTagNameMap = {
"#text": Text;
"#comment": Comment;
};
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
declare global {
type ddeComponentAttributes = Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
type ddeString = string | Signal<string, {}>;
type ddeStringable = ddeString | number | Signal<number, {}>;
}
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
export type PascalCase = `${Capitalize<string>}${string}`;
export type AttrsModified = {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
}>;
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>;
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>;
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
export type IsReadonly<T, K extends keyof T> = T extends {
readonly [P in K]: T[K];
} ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
export type ElementAttributes<T extends SupportedElement> = Partial<{
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(attrs: {
type: "component" | "reactive" | "later";
name?: string;
host?: "this" | "parentElement";
}, is_open?: boolean): Comment;
}
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<A extends {
textContent: ddeStringable;
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
export function el(tag_name?: "<>"): ddeDocumentFragment;
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
export interface On {
/** Listens to the DOM event. See {@link Document.addEventListener} */
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
target: EL;
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/**
* Fires after the next tick of the Javascript event loop.
* This is handy for example to apply some property depending on the element content:
* ```js
* const selected= "Z";
* //...
* return el("form").append(
* el("select", null, on.defer(e=> e.value=selected)).append(
* el("option", { value: "A", textContent: "A" }),
* //...
* el("option", { value: "Z", textContent: "Z" }),
* ),
* );
* ```
* */
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
}
export const on: On;
export type Scope = {
scope: Node | Function | Object;
host: Host<SupportedElement>;
custom_element: false | HTMLElement;
prevent: boolean;
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope;
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T;
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: Host<SupportedElement>;
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
signal: AbortSignal;
state: Scope[];
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>;
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>;
};
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/**
* Memoization utility for caching DOM elements to improve performance.
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
*
* @param key - Unique identifier for the element (usually an ID or unique value)
* @param generator - Function that creates the element
* @returns The cached element if the key exists, otherwise the result of the generator function
*
* @example
* ```ts
* // Within S.el for list rendering
* S.el(itemsSignal, (items, memo) =>
* el("ul").append(
* ...items.map(item =>
* memo(item.id, () => el(ItemComponent, item))
* )
* )
* )
* ```
*/
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
/**
* Memo namespace containing utility functions for memoization.
*/
export namespace memo {
/**
* Checks if an object is a memo scope.
* @param obj - The object to check
* @returns True if the object is a memo scope
*/
export function isScope(obj: any): boolean;
/**
* Creates a memoized function with optional cleanup support.
*
* @param fun - The function to memoize
* @param options - Configuration options
* @param options.signal - AbortSignal for cleanup
* @param options.onlyLast - When true, only keeps the cache from the most recent call
* @returns A memoized version of the function with a .clear() method
*
* @example
* ```ts
* const renderItems = memo.scope(function(items) {
* return items.map(item =>
* memo(item.id, () => el("div", item.name))
* );
* }, {
* signal: controller.signal,
* onlyLast: true
* });
* ```
*/
export function scope<F extends Function>(fun: F, options?: {
signal?: AbortSignal;
onlyLast?: boolean;
}): F & {
clear: () => void;
};
}
/* TypeScript MEH */
declare global {
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
interface ddeDocumentFragment extends DocumentFragment {
append: ddeAppend<ddeDocumentFragment>;
}
interface ddeHTMLElement extends HTMLElement {
append: ddeAppend<ddeHTMLElement>;
}
interface ddeSVGElement extends SVGElement {
append: ddeAppend<ddeSVGElement>;
}
interface ddeMathMLElement extends MathMLElement {
append: ddeAppend<ddeMathMLElement>;
}
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
append: ddeAppend<ddeHTMLAnchorElement>;
}
export interface ddeHTMLAreaElement extends HTMLAreaElement {
append: ddeAppend<ddeHTMLAreaElement>;
}
export interface ddeHTMLAudioElement extends HTMLAudioElement {
append: ddeAppend<ddeHTMLAudioElement>;
}
export interface ddeHTMLBaseElement extends HTMLBaseElement {
append: ddeAppend<ddeHTMLBaseElement>;
}
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
append: ddeAppend<ddeHTMLQuoteElement>;
}
export interface ddeHTMLBodyElement extends HTMLBodyElement {
append: ddeAppend<ddeHTMLBodyElement>;
}
export interface ddeHTMLBRElement extends HTMLBRElement {
append: ddeAppend<ddeHTMLBRElement>;
}
export interface ddeHTMLButtonElement extends HTMLButtonElement {
append: ddeAppend<ddeHTMLButtonElement>;
}
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
append: ddeAppend<ddeHTMLCanvasElement>;
}
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
append: ddeAppend<ddeHTMLTableCaptionElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLDataElement extends HTMLDataElement {
append: ddeAppend<ddeHTMLDataElement>;
}
export interface ddeHTMLDataListElement extends HTMLDataListElement {
append: ddeAppend<ddeHTMLDataListElement>;
}
export interface ddeHTMLModElement extends HTMLModElement {
append: ddeAppend<ddeHTMLModElement>;
}
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
append: ddeAppend<ddeHTMLDetailsElement>;
}
export interface ddeHTMLDialogElement extends HTMLDialogElement {
append: ddeAppend<ddeHTMLDialogElement>;
}
export interface ddeHTMLDivElement extends HTMLDivElement {
append: ddeAppend<ddeHTMLDivElement>;
}
export interface ddeHTMLDListElement extends HTMLDListElement {
append: ddeAppend<ddeHTMLDListElement>;
}
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
append: ddeAppend<ddeHTMLEmbedElement>;
}
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
append: ddeAppend<ddeHTMLFieldSetElement>;
}
export interface ddeHTMLFormElement extends HTMLFormElement {
append: ddeAppend<ddeHTMLFormElement>;
}
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
append: ddeAppend<ddeHTMLHeadingElement>;
}
export interface ddeHTMLHeadElement extends HTMLHeadElement {
append: ddeAppend<ddeHTMLHeadElement>;
}
export interface ddeHTMLHRElement extends HTMLHRElement {
append: ddeAppend<ddeHTMLHRElement>;
}
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
append: ddeAppend<ddeHTMLHtmlElement>;
}
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
append: ddeAppend<ddeHTMLIFrameElement>;
}
export interface ddeHTMLImageElement extends HTMLImageElement {
append: ddeAppend<ddeHTMLImageElement>;
}
export interface ddeHTMLInputElement extends HTMLInputElement {
append: ddeAppend<ddeHTMLInputElement>;
}
export interface ddeHTMLLabelElement extends HTMLLabelElement {
append: ddeAppend<ddeHTMLLabelElement>;
}
export interface ddeHTMLLegendElement extends HTMLLegendElement {
append: ddeAppend<ddeHTMLLegendElement>;
}
export interface ddeHTMLLIElement extends HTMLLIElement {
append: ddeAppend<ddeHTMLLIElement>;
}
export interface ddeHTMLLinkElement extends HTMLLinkElement {
append: ddeAppend<ddeHTMLLinkElement>;
}
export interface ddeHTMLMapElement extends HTMLMapElement {
append: ddeAppend<ddeHTMLMapElement>;
}
export interface ddeHTMLMenuElement extends HTMLMenuElement {
append: ddeAppend<ddeHTMLMenuElement>;
}
export interface ddeHTMLMetaElement extends HTMLMetaElement {
append: ddeAppend<ddeHTMLMetaElement>;
}
export interface ddeHTMLMeterElement extends HTMLMeterElement {
append: ddeAppend<ddeHTMLMeterElement>;
}
export interface ddeHTMLObjectElement extends HTMLObjectElement {
append: ddeAppend<ddeHTMLObjectElement>;
}
export interface ddeHTMLOListElement extends HTMLOListElement {
append: ddeAppend<ddeHTMLOListElement>;
}
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
append: ddeAppend<ddeHTMLOptGroupElement>;
}
export interface ddeHTMLOptionElement extends HTMLOptionElement {
append: ddeAppend<ddeHTMLOptionElement>;
}
export interface ddeHTMLOutputElement extends HTMLOutputElement {
append: ddeAppend<ddeHTMLOutputElement>;
}
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
append: ddeAppend<ddeHTMLParagraphElement>;
}
export interface ddeHTMLPictureElement extends HTMLPictureElement {
append: ddeAppend<ddeHTMLPictureElement>;
}
export interface ddeHTMLPreElement extends HTMLPreElement {
append: ddeAppend<ddeHTMLPreElement>;
}
export interface ddeHTMLProgressElement extends HTMLProgressElement {
append: ddeAppend<ddeHTMLProgressElement>;
}
export interface ddeHTMLScriptElement extends HTMLScriptElement {
append: ddeAppend<ddeHTMLScriptElement>;
}
export interface ddeHTMLSelectElement extends HTMLSelectElement {
append: ddeAppend<ddeHTMLSelectElement>;
}
export interface ddeHTMLSlotElement extends HTMLSlotElement {
append: ddeAppend<ddeHTMLSlotElement>;
}
export interface ddeHTMLSourceElement extends HTMLSourceElement {
append: ddeAppend<ddeHTMLSourceElement>;
}
export interface ddeHTMLSpanElement extends HTMLSpanElement {
append: ddeAppend<ddeHTMLSpanElement>;
}
export interface ddeHTMLStyleElement extends HTMLStyleElement {
append: ddeAppend<ddeHTMLStyleElement>;
}
export interface ddeHTMLTableElement extends HTMLTableElement {
append: ddeAppend<ddeHTMLTableElement>;
}
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
append: ddeAppend<ddeHTMLTableSectionElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
append: ddeAppend<ddeHTMLTemplateElement>;
}
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
append: ddeAppend<ddeHTMLTextAreaElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTimeElement extends HTMLTimeElement {
append: ddeAppend<ddeHTMLTimeElement>;
}
export interface ddeHTMLTitleElement extends HTMLTitleElement {
append: ddeAppend<ddeHTMLTitleElement>;
}
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
append: ddeAppend<ddeHTMLTableRowElement>;
}
export interface ddeHTMLTrackElement extends HTMLTrackElement {
append: ddeAppend<ddeHTMLTrackElement>;
}
export interface ddeHTMLUListElement extends HTMLUListElement {
append: ddeAppend<ddeHTMLUListElement>;
}
export interface ddeHTMLVideoElement extends HTMLVideoElement {
append: ddeAppend<ddeHTMLVideoElement>;
}
export interface ddeSVGAElement extends SVGAElement {
append: ddeAppend<ddeSVGAElement>;
}
export interface ddeSVGAnimateElement extends SVGAnimateElement {
append: ddeAppend<ddeSVGAnimateElement>;
}
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
append: ddeAppend<ddeSVGAnimateMotionElement>;
}
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
append: ddeAppend<ddeSVGAnimateTransformElement>;
}
export interface ddeSVGCircleElement extends SVGCircleElement {
append: ddeAppend<ddeSVGCircleElement>;
}
export interface ddeSVGClipPathElement extends SVGClipPathElement {
append: ddeAppend<ddeSVGClipPathElement>;
}
export interface ddeSVGDefsElement extends SVGDefsElement {
append: ddeAppend<ddeSVGDefsElement>;
}
export interface ddeSVGDescElement extends SVGDescElement {
append: ddeAppend<ddeSVGDescElement>;
}
export interface ddeSVGEllipseElement extends SVGEllipseElement {
append: ddeAppend<ddeSVGEllipseElement>;
}
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
append: ddeAppend<ddeSVGFEBlendElement>;
}
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
append: ddeAppend<ddeSVGFEColorMatrixElement>;
}
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
append: ddeAppend<ddeSVGFEComponentTransferElement>;
}
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
append: ddeAppend<ddeSVGFECompositeElement>;
}
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
}
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
}
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
}
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
append: ddeAppend<ddeSVGFEDistantLightElement>;
}
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
append: ddeAppend<ddeSVGFEDropShadowElement>;
}
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
append: ddeAppend<ddeSVGFEFloodElement>;
}
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
append: ddeAppend<ddeSVGFEFuncAElement>;
}
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
append: ddeAppend<ddeSVGFEFuncBElement>;
}
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
append: ddeAppend<ddeSVGFEFuncGElement>;
}
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
append: ddeAppend<ddeSVGFEFuncRElement>;
}
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
}
export interface ddeSVGFEImageElement extends SVGFEImageElement {
append: ddeAppend<ddeSVGFEImageElement>;
}
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
append: ddeAppend<ddeSVGFEMergeElement>;
}
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
append: ddeAppend<ddeSVGFEMergeNodeElement>;
}
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
append: ddeAppend<ddeSVGFEMorphologyElement>;
}
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
append: ddeAppend<ddeSVGFEOffsetElement>;
}
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
append: ddeAppend<ddeSVGFEPointLightElement>;
}
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
append: ddeAppend<ddeSVGFESpecularLightingElement>;
}
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
append: ddeAppend<ddeSVGFESpotLightElement>;
}
export interface ddeSVGFETileElement extends SVGFETileElement {
append: ddeAppend<ddeSVGFETileElement>;
}
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
append: ddeAppend<ddeSVGFETurbulenceElement>;
}
export interface ddeSVGFilterElement extends SVGFilterElement {
append: ddeAppend<ddeSVGFilterElement>;
}
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
append: ddeAppend<ddeSVGForeignObjectElement>;
}
export interface ddeSVGGElement extends SVGGElement {
append: ddeAppend<ddeSVGGElement>;
}
export interface ddeSVGImageElement extends SVGImageElement {
append: ddeAppend<ddeSVGImageElement>;
}
export interface ddeSVGLineElement extends SVGLineElement {
append: ddeAppend<ddeSVGLineElement>;
}
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
append: ddeAppend<ddeSVGLinearGradientElement>;
}
export interface ddeSVGMarkerElement extends SVGMarkerElement {
append: ddeAppend<ddeSVGMarkerElement>;
}
export interface ddeSVGMaskElement extends SVGMaskElement {
append: ddeAppend<ddeSVGMaskElement>;
}
export interface ddeSVGMetadataElement extends SVGMetadataElement {
append: ddeAppend<ddeSVGMetadataElement>;
}
export interface ddeSVGMPathElement extends SVGMPathElement {
append: ddeAppend<ddeSVGMPathElement>;
}
export interface ddeSVGPathElement extends SVGPathElement {
append: ddeAppend<ddeSVGPathElement>;
}
export interface ddeSVGPatternElement extends SVGPatternElement {
append: ddeAppend<ddeSVGPatternElement>;
}
export interface ddeSVGPolygonElement extends SVGPolygonElement {
append: ddeAppend<ddeSVGPolygonElement>;
}
export interface ddeSVGPolylineElement extends SVGPolylineElement {
append: ddeAppend<ddeSVGPolylineElement>;
}
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
append: ddeAppend<ddeSVGRadialGradientElement>;
}
export interface ddeSVGRectElement extends SVGRectElement {
append: ddeAppend<ddeSVGRectElement>;
}
export interface ddeSVGScriptElement extends SVGScriptElement {
append: ddeAppend<ddeSVGScriptElement>;
}
export interface ddeSVGSetElement extends SVGSetElement {
append: ddeAppend<ddeSVGSetElement>;
}
export interface ddeSVGStopElement extends SVGStopElement {
append: ddeAppend<ddeSVGStopElement>;
}
export interface ddeSVGStyleElement extends SVGStyleElement {
append: ddeAppend<ddeSVGStyleElement>;
}
export interface ddeSVGSVGElement extends SVGSVGElement {
append: ddeAppend<ddeSVGSVGElement>;
}
export interface ddeSVGSwitchElement extends SVGSwitchElement {
append: ddeAppend<ddeSVGSwitchElement>;
}
export interface ddeSVGSymbolElement extends SVGSymbolElement {
append: ddeAppend<ddeSVGSymbolElement>;
}
export interface ddeSVGTextElement extends SVGTextElement {
append: ddeAppend<ddeSVGTextElement>;
}
export interface ddeSVGTextPathElement extends SVGTextPathElement {
append: ddeAppend<ddeSVGTextPathElement>;
}
export interface ddeSVGTitleElement extends SVGTitleElement {
append: ddeAppend<ddeSVGTitleElement>;
}
export interface ddeSVGTSpanElement extends SVGTSpanElement {
append: ddeAppend<ddeSVGTSpanElement>;
}
export interface ddeSVGUseElement extends SVGUseElement {
append: ddeAppend<ddeSVGUseElement>;
}
export interface ddeSVGViewElement extends SVGViewElement {
append: ddeAppend<ddeSVGViewElement>;
}
export {
dispatchEvent$1 as dispatchEvent,
el as createElement,
elNS as createElementNS,
};
export {};

File diff suppressed because one or more lines are too long

568
dist/esm.d.min.ts vendored Normal file
View File

@ -0,0 +1,568 @@
declare global{ /* ddeSignal */ }
type CustomElementTagNameMap= { '#text': Text, '#comment': Comment }
type SupportedElement=
HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
| SVGElementTagNameMap[keyof SVGElementTagNameMap]
| MathMLElementTagNameMap[keyof MathMLElementTagNameMap]
| CustomElementTagNameMap[keyof CustomElementTagNameMap]
declare global {
type ddeComponentAttributes= Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node>= (element: El)=> any;
type ddeString= string | ddeSignal<string>
type ddeStringable= ddeString | number | ddeSignal<number>
}
type PascalCase=
`${Capitalize<string>}${string}`;
type AttrsModified= {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString
| Partial<{ [K in keyof CSSStyleDeclaration]: ddeSignal<CSSStyleDeclaration[K]> }>
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string,-1|0|1|boolean|ddeSignal<-1|0|1|boolean>>,
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>,
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>,
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString>
& Record<`.${string}`, any>
type _fromElsInterfaces<EL extends SupportedElement>= Omit<EL, keyof AttrsModified>;
type IsReadonly<T, K extends keyof T> =
T extends { readonly [P in K]: T[K] } ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
type ElementAttributes<T extends SupportedElement>= Partial<{
[K in keyof _fromElsInterfaces<T>]:
_fromElsInterfaces<T>[K] extends ((...p: any[])=> any)
? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>)=>
ddeSignal<ReturnType<_fromElsInterfaces<T>[K]>>)
: (IsReadonly<_fromElsInterfaces<T>, K> extends false
? _fromElsInterfaces<T>[K] | ddeSignal<_fromElsInterfaces<T>[K]>
: ddeStringable)
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(
element: El,
classList: AttrsModified["classList"]
): El
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(
element: El,
attr: ATT,
value: ElementAttributes<El>[ATT]
): ElementAttributes<El>[ATT]
type ExtendedHTMLElementTagNameMap= HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(
attrs: { type: "component"|"reactive"|"later", name?: string, host?: "this"|"parentElement" },
is_open?: boolean
): Comment;
}
export function el<
A extends ddeComponentAttributes,
EL extends SupportedElement | ddeDocumentFragment
>(
component: (attr: A, ...rest: any[])=> EL,
attrs?: NoInfer<A>,
...addons: ddeElementAddon<EL>[]
): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? EL
: ( EL extends ddeDocumentFragment ? EL : ddeHTMLElement )
export function el<
A extends { textContent: ddeStringable },
EL extends SupportedElement | ddeDocumentFragment
>(
component: (attr: A, ...rest: any[])=> EL,
attrs?: NoInfer<A>["textContent"],
...addons: ddeElementAddon<EL>[]
): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap]
? EL
: ( EL extends ddeDocumentFragment ? EL : ddeHTMLElement )
export function el<
TAG extends keyof ExtendedHTMLElementTagNameMap,
>(
tag_name: TAG,
attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable,
...addons: ddeElementAddon<
ExtendedHTMLElementTagNameMap[NoInfer<TAG>]
>[], // TODO: for now addons must have the same element
): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement
export function el(
tag_name?: "<>",
): ddeDocumentFragment
export function el(
tag_name: string,
attrs?: ElementAttributes<HTMLElement> | ddeStringable,
...addons: ddeElementAddon<HTMLElement>[]
): ddeHTMLElement
export { el as createElement }
export function elNS(
namespace: "http://www.w3.org/2000/svg"
): <
TAG extends keyof SVGElementTagNameMap & string,
EL extends ( TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement ),
>(
tag_name: TAG,
attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement
export function elNS(
namespace: "http://www.w3.org/1998/Math/MathML"
): <
TAG extends keyof MathMLElementTagNameMap & string,
EL extends ( TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement ),
>(
tag_name: TAG,
attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | ddeSignal<EL[key]> | string | number | boolean
}>,
...addons: ddeElementAddon<NoInfer<EL>>[]
)=> ddeMathMLElement
export function elNS(
namespace: string
): (
tag_name: string,
attrs?: string | ddeStringable | Record<string, any>,
...addons: ddeElementAddon<SupportedElement>[]
)=> SupportedElement
export { elNS as createElementNS }
export function chainableAppend<EL extends SupportedElement>(el: EL): EL;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
root: EL,
): EL
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(
el: HTMLElement,
body: EL,
): EL
export function dispatchEvent(name: keyof DocumentEventMap | string, element: SupportedElement):
(data?: any)=> void;
export function dispatchEvent(name: keyof DocumentEventMap | string, options?: EventInit):
(element: SupportedElement, data?: any)=> void;
export function dispatchEvent(
name: keyof DocumentEventMap | string,
options: EventInit | null,
element: SupportedElement | (()=> SupportedElement)
): (data?: any)=> void;
interface On{
/** Listens to the DOM event. See {@link Document.addEventListener} */
<
Event extends keyof DocumentEventMap,
EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
>(
type: Event,
listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: DocumentEventMap[Event]) => any,
options?: AddEventListenerOptions
) : EE;
<
EE extends ddeElementAddon<SupportedElement>= ddeElementAddon<HTMLElement>,
>(
type: string,
listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent ) => any,
options?: AddEventListenerOptions
) : EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
connected<
EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )
>(
listener: (this: El, event: CustomEvent<El>) => any,
options?: AddEventListenerOptions
) : EE;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
disconnected<
EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )
>(
listener: (this: El, event: CustomEvent<void>) => any,
options?: AddEventListenerOptions
) : EE;
/** Listens to the element attribute changes. In case of custom elements uses [`attributeChangedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */// editorconfig-checker-disable-line
attributeChanged<
EE extends ddeElementAddon<SupportedElement>,
El extends ( EE extends ddeElementAddon<infer El> ? El : never )
>(
listener: (this: El, event: CustomEvent<[ string, string ]>) => any,
options?: AddEventListenerOptions
) : EE;
}
export const on: On;
type Scope= {
scope: Node | Function | Object,
host: ddeElementAddon<any>,
custom_element: false | HTMLElement,
prevent: boolean
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope,
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T,
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: (...addons: ddeElementAddon<SupportedElement>[])=> HTMLElement,
state: Scope[],
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>,
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>,
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>,
};
export function customElementRender<
EL extends HTMLElement,
P extends any = Record<string, string | ddeSignal<string>>
>(
target: ShadowRoot | EL,
render: (props: P)=> SupportedElement | DocumentFragment,
props?: P | ((el: EL)=> P)
): EL
export function customElementWithDDE<EL extends (new ()=> HTMLElement)>(custom_element: EL): EL
export function lifecyclesToEvents<EL extends (new ()=> HTMLElement)>(custom_element: EL): EL
export function observedAttributes(custom_element: HTMLElement): Record<string, string>
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/* TypeScript MEH */
declare global{
type ddeAppend<el>= (...nodes: (Node | string)[])=> el;
interface ddeDocumentFragment extends DocumentFragment{ append: ddeAppend<ddeDocumentFragment>; }
interface ddeHTMLElement extends HTMLElement{ append: ddeAppend<ddeHTMLElement>; }
interface ddeSVGElement extends SVGElement{ append: ddeAppend<ddeSVGElement>; }
interface ddeMathMLElement extends MathMLElement{ append: ddeAppend<ddeMathMLElement>; }
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
interface ddeHTMLAnchorElement extends HTMLAnchorElement{ append: ddeAppend<ddeHTMLAnchorElement>; }
interface ddeHTMLAreaElement extends HTMLAreaElement{ append: ddeAppend<ddeHTMLAreaElement>; }
interface ddeHTMLAudioElement extends HTMLAudioElement{ append: ddeAppend<ddeHTMLAudioElement>; }
interface ddeHTMLBaseElement extends HTMLBaseElement{ append: ddeAppend<ddeHTMLBaseElement>; }
interface ddeHTMLQuoteElement extends HTMLQuoteElement{ append: ddeAppend<ddeHTMLQuoteElement>; }
interface ddeHTMLBodyElement extends HTMLBodyElement{ append: ddeAppend<ddeHTMLBodyElement>; }
interface ddeHTMLBRElement extends HTMLBRElement{ append: ddeAppend<ddeHTMLBRElement>; }
interface ddeHTMLButtonElement extends HTMLButtonElement{ append: ddeAppend<ddeHTMLButtonElement>; }
interface ddeHTMLCanvasElement extends HTMLCanvasElement{ append: ddeAppend<ddeHTMLCanvasElement>; }
interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement{ append: ddeAppend<ddeHTMLTableCaptionElement>; }
interface ddeHTMLTableColElement extends HTMLTableColElement{ append: ddeAppend<ddeHTMLTableColElement>; }
interface ddeHTMLTableColElement extends HTMLTableColElement{ append: ddeAppend<ddeHTMLTableColElement>; }
interface ddeHTMLDataElement extends HTMLDataElement{ append: ddeAppend<ddeHTMLDataElement>; }
interface ddeHTMLDataListElement extends HTMLDataListElement{ append: ddeAppend<ddeHTMLDataListElement>; }
interface ddeHTMLModElement extends HTMLModElement{ append: ddeAppend<ddeHTMLModElement>; }
interface ddeHTMLDetailsElement extends HTMLDetailsElement{ append: ddeAppend<ddeHTMLDetailsElement>; }
interface ddeHTMLDialogElement extends HTMLDialogElement{ append: ddeAppend<ddeHTMLDialogElement>; }
interface ddeHTMLDivElement extends HTMLDivElement{ append: ddeAppend<ddeHTMLDivElement>; }
interface ddeHTMLDListElement extends HTMLDListElement{ append: ddeAppend<ddeHTMLDListElement>; }
interface ddeHTMLEmbedElement extends HTMLEmbedElement{ append: ddeAppend<ddeHTMLEmbedElement>; }
interface ddeHTMLFieldSetElement extends HTMLFieldSetElement{ append: ddeAppend<ddeHTMLFieldSetElement>; }
interface ddeHTMLFormElement extends HTMLFormElement{ append: ddeAppend<ddeHTMLFormElement>; }
interface ddeHTMLHeadingElement extends HTMLHeadingElement{ append: ddeAppend<ddeHTMLHeadingElement>; }
interface ddeHTMLHeadElement extends HTMLHeadElement{ append: ddeAppend<ddeHTMLHeadElement>; }
interface ddeHTMLHRElement extends HTMLHRElement{ append: ddeAppend<ddeHTMLHRElement>; }
interface ddeHTMLHtmlElement extends HTMLHtmlElement{ append: ddeAppend<ddeHTMLHtmlElement>; }
interface ddeHTMLIFrameElement extends HTMLIFrameElement{ append: ddeAppend<ddeHTMLIFrameElement>; }
interface ddeHTMLImageElement extends HTMLImageElement{ append: ddeAppend<ddeHTMLImageElement>; }
interface ddeHTMLInputElement extends HTMLInputElement{ append: ddeAppend<ddeHTMLInputElement>; }
interface ddeHTMLLabelElement extends HTMLLabelElement{ append: ddeAppend<ddeHTMLLabelElement>; }
interface ddeHTMLLegendElement extends HTMLLegendElement{ append: ddeAppend<ddeHTMLLegendElement>; }
interface ddeHTMLLIElement extends HTMLLIElement{ append: ddeAppend<ddeHTMLLIElement>; }
interface ddeHTMLLinkElement extends HTMLLinkElement{ append: ddeAppend<ddeHTMLLinkElement>; }
interface ddeHTMLMapElement extends HTMLMapElement{ append: ddeAppend<ddeHTMLMapElement>; }
interface ddeHTMLMenuElement extends HTMLMenuElement{ append: ddeAppend<ddeHTMLMenuElement>; }
interface ddeHTMLMetaElement extends HTMLMetaElement{ append: ddeAppend<ddeHTMLMetaElement>; }
interface ddeHTMLMeterElement extends HTMLMeterElement{ append: ddeAppend<ddeHTMLMeterElement>; }
interface ddeHTMLObjectElement extends HTMLObjectElement{ append: ddeAppend<ddeHTMLObjectElement>; }
interface ddeHTMLOListElement extends HTMLOListElement{ append: ddeAppend<ddeHTMLOListElement>; }
interface ddeHTMLOptGroupElement extends HTMLOptGroupElement{ append: ddeAppend<ddeHTMLOptGroupElement>; }
interface ddeHTMLOptionElement extends HTMLOptionElement{ append: ddeAppend<ddeHTMLOptionElement>; }
interface ddeHTMLOutputElement extends HTMLOutputElement{ append: ddeAppend<ddeHTMLOutputElement>; }
interface ddeHTMLParagraphElement extends HTMLParagraphElement{ append: ddeAppend<ddeHTMLParagraphElement>; }
interface ddeHTMLPictureElement extends HTMLPictureElement{ append: ddeAppend<ddeHTMLPictureElement>; }
interface ddeHTMLPreElement extends HTMLPreElement{ append: ddeAppend<ddeHTMLPreElement>; }
interface ddeHTMLProgressElement extends HTMLProgressElement{ append: ddeAppend<ddeHTMLProgressElement>; }
interface ddeHTMLScriptElement extends HTMLScriptElement{ append: ddeAppend<ddeHTMLScriptElement>; }
interface ddeHTMLSelectElement extends HTMLSelectElement{ append: ddeAppend<ddeHTMLSelectElement>; }
interface ddeHTMLSlotElement extends HTMLSlotElement{ append: ddeAppend<ddeHTMLSlotElement>; }
interface ddeHTMLSourceElement extends HTMLSourceElement{ append: ddeAppend<ddeHTMLSourceElement>; }
interface ddeHTMLSpanElement extends HTMLSpanElement{ append: ddeAppend<ddeHTMLSpanElement>; }
interface ddeHTMLStyleElement extends HTMLStyleElement{ append: ddeAppend<ddeHTMLStyleElement>; }
interface ddeHTMLTableElement extends HTMLTableElement{ append: ddeAppend<ddeHTMLTableElement>; }
interface ddeHTMLTableSectionElement extends HTMLTableSectionElement{ append: ddeAppend<ddeHTMLTableSectionElement>; }
interface ddeHTMLTableCellElement extends HTMLTableCellElement{ append: ddeAppend<ddeHTMLTableCellElement>; }
interface ddeHTMLTemplateElement extends HTMLTemplateElement{ append: ddeAppend<ddeHTMLTemplateElement>; }
interface ddeHTMLTextAreaElement extends HTMLTextAreaElement{ append: ddeAppend<ddeHTMLTextAreaElement>; }
interface ddeHTMLTableCellElement extends HTMLTableCellElement{ append: ddeAppend<ddeHTMLTableCellElement>; }
interface ddeHTMLTimeElement extends HTMLTimeElement{ append: ddeAppend<ddeHTMLTimeElement>; }
interface ddeHTMLTitleElement extends HTMLTitleElement{ append: ddeAppend<ddeHTMLTitleElement>; }
interface ddeHTMLTableRowElement extends HTMLTableRowElement{ append: ddeAppend<ddeHTMLTableRowElement>; }
interface ddeHTMLTrackElement extends HTMLTrackElement{ append: ddeAppend<ddeHTMLTrackElement>; }
interface ddeHTMLUListElement extends HTMLUListElement{ append: ddeAppend<ddeHTMLUListElement>; }
interface ddeHTMLVideoElement extends HTMLVideoElement{ append: ddeAppend<ddeHTMLVideoElement>; }
interface ddeSVGAElement extends SVGAElement{ append: ddeAppend<ddeSVGAElement>; }
interface ddeSVGAnimateElement extends SVGAnimateElement{ append: ddeAppend<ddeSVGAnimateElement>; }
interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement{ append: ddeAppend<ddeSVGAnimateMotionElement>; }
interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement{ append: ddeAppend<ddeSVGAnimateTransformElement>; }
interface ddeSVGCircleElement extends SVGCircleElement{ append: ddeAppend<ddeSVGCircleElement>; }
interface ddeSVGClipPathElement extends SVGClipPathElement{ append: ddeAppend<ddeSVGClipPathElement>; }
interface ddeSVGDefsElement extends SVGDefsElement{ append: ddeAppend<ddeSVGDefsElement>; }
interface ddeSVGDescElement extends SVGDescElement{ append: ddeAppend<ddeSVGDescElement>; }
interface ddeSVGEllipseElement extends SVGEllipseElement{ append: ddeAppend<ddeSVGEllipseElement>; }
interface ddeSVGFEBlendElement extends SVGFEBlendElement{ append: ddeAppend<ddeSVGFEBlendElement>; }
interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement{ append: ddeAppend<ddeSVGFEColorMatrixElement>; }
interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement{ append: ddeAppend<ddeSVGFEComponentTransferElement>; }
interface ddeSVGFECompositeElement extends SVGFECompositeElement{ append: ddeAppend<ddeSVGFECompositeElement>; }
interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement{ append: ddeAppend<ddeSVGFEConvolveMatrixElement>; }
interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement{ append: ddeAppend<ddeSVGFEDiffuseLightingElement>; }
interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement{ append: ddeAppend<ddeSVGFEDisplacementMapElement>; }
interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement{ append: ddeAppend<ddeSVGFEDistantLightElement>; }
interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement{ append: ddeAppend<ddeSVGFEDropShadowElement>; }
interface ddeSVGFEFloodElement extends SVGFEFloodElement{ append: ddeAppend<ddeSVGFEFloodElement>; }
interface ddeSVGFEFuncAElement extends SVGFEFuncAElement{ append: ddeAppend<ddeSVGFEFuncAElement>; }
interface ddeSVGFEFuncBElement extends SVGFEFuncBElement{ append: ddeAppend<ddeSVGFEFuncBElement>; }
interface ddeSVGFEFuncGElement extends SVGFEFuncGElement{ append: ddeAppend<ddeSVGFEFuncGElement>; }
interface ddeSVGFEFuncRElement extends SVGFEFuncRElement{ append: ddeAppend<ddeSVGFEFuncRElement>; }
interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement{ append: ddeAppend<ddeSVGFEGaussianBlurElement>; }
interface ddeSVGFEImageElement extends SVGFEImageElement{ append: ddeAppend<ddeSVGFEImageElement>; }
interface ddeSVGFEMergeElement extends SVGFEMergeElement{ append: ddeAppend<ddeSVGFEMergeElement>; }
interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement{ append: ddeAppend<ddeSVGFEMergeNodeElement>; }
interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement{ append: ddeAppend<ddeSVGFEMorphologyElement>; }
interface ddeSVGFEOffsetElement extends SVGFEOffsetElement{ append: ddeAppend<ddeSVGFEOffsetElement>; }
interface ddeSVGFEPointLightElement extends SVGFEPointLightElement{ append: ddeAppend<ddeSVGFEPointLightElement>; }
interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement{ append: ddeAppend<ddeSVGFESpecularLightingElement>; }
interface ddeSVGFESpotLightElement extends SVGFESpotLightElement{ append: ddeAppend<ddeSVGFESpotLightElement>; }
interface ddeSVGFETileElement extends SVGFETileElement{ append: ddeAppend<ddeSVGFETileElement>; }
interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement{ append: ddeAppend<ddeSVGFETurbulenceElement>; }
interface ddeSVGFilterElement extends SVGFilterElement{ append: ddeAppend<ddeSVGFilterElement>; }
interface ddeSVGForeignObjectElement extends SVGForeignObjectElement{ append: ddeAppend<ddeSVGForeignObjectElement>; }
interface ddeSVGGElement extends SVGGElement{ append: ddeAppend<ddeSVGGElement>; }
interface ddeSVGImageElement extends SVGImageElement{ append: ddeAppend<ddeSVGImageElement>; }
interface ddeSVGLineElement extends SVGLineElement{ append: ddeAppend<ddeSVGLineElement>; }
interface ddeSVGLinearGradientElement extends SVGLinearGradientElement{ append: ddeAppend<ddeSVGLinearGradientElement>; }
interface ddeSVGMarkerElement extends SVGMarkerElement{ append: ddeAppend<ddeSVGMarkerElement>; }
interface ddeSVGMaskElement extends SVGMaskElement{ append: ddeAppend<ddeSVGMaskElement>; }
interface ddeSVGMetadataElement extends SVGMetadataElement{ append: ddeAppend<ddeSVGMetadataElement>; }
interface ddeSVGMPathElement extends SVGMPathElement{ append: ddeAppend<ddeSVGMPathElement>; }
interface ddeSVGPathElement extends SVGPathElement{ append: ddeAppend<ddeSVGPathElement>; }
interface ddeSVGPatternElement extends SVGPatternElement{ append: ddeAppend<ddeSVGPatternElement>; }
interface ddeSVGPolygonElement extends SVGPolygonElement{ append: ddeAppend<ddeSVGPolygonElement>; }
interface ddeSVGPolylineElement extends SVGPolylineElement{ append: ddeAppend<ddeSVGPolylineElement>; }
interface ddeSVGRadialGradientElement extends SVGRadialGradientElement{ append: ddeAppend<ddeSVGRadialGradientElement>; }
interface ddeSVGRectElement extends SVGRectElement{ append: ddeAppend<ddeSVGRectElement>; }
interface ddeSVGScriptElement extends SVGScriptElement{ append: ddeAppend<ddeSVGScriptElement>; }
interface ddeSVGSetElement extends SVGSetElement{ append: ddeAppend<ddeSVGSetElement>; }
interface ddeSVGStopElement extends SVGStopElement{ append: ddeAppend<ddeSVGStopElement>; }
interface ddeSVGStyleElement extends SVGStyleElement{ append: ddeAppend<ddeSVGStyleElement>; }
interface ddeSVGSVGElement extends SVGSVGElement{ append: ddeAppend<ddeSVGSVGElement>; }
interface ddeSVGSwitchElement extends SVGSwitchElement{ append: ddeAppend<ddeSVGSwitchElement>; }
interface ddeSVGSymbolElement extends SVGSymbolElement{ append: ddeAppend<ddeSVGSymbolElement>; }
interface ddeSVGTextElement extends SVGTextElement{ append: ddeAppend<ddeSVGTextElement>; }
interface ddeSVGTextPathElement extends SVGTextPathElement{ append: ddeAppend<ddeSVGTextPathElement>; }
interface ddeSVGTitleElement extends SVGTitleElement{ append: ddeAppend<ddeSVGTitleElement>; }
interface ddeSVGTSpanElement extends SVGTSpanElement{ append: ddeAppend<ddeSVGTSpanElement>; }
interface ddeSVGUseElement extends SVGUseElement{ append: ddeAppend<ddeSVGUseElement>; }
interface ddeSVGViewElement extends SVGViewElement{ append: ddeAppend<ddeSVGViewElement>; }
// editorconfig-checker-enable

975
dist/esm.d.ts vendored

File diff suppressed because it is too large Load Diff

806
dist/esm.js vendored
View File

@ -1,5 +1,4 @@
// src/helpers.js
var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a);
function isUndef(value) {
return typeof value === "undefined";
}
@ -9,9 +8,6 @@ function isInstance(obj, cls) {
function isProtoFrom(obj, cls) {
return Object.prototype.isPrototypeOf.call(cls, obj);
}
function oCreate(proto = null, p = {}) {
return Object.create(proto, p);
}
function oAssign(...o) {
return Object.assign(...o);
}
@ -25,18 +21,53 @@ function onAbort(signal, listener) {
signal.removeEventListener("abort", listener);
};
}
function requestIdle() {
return new Promise(function(resolve) {
(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
});
function observedAttributes(instance, observedAttribute) {
const { observedAttributes: observedAttributes3 = [] } = instance.constructor;
return observedAttributes3.reduce(function(out, name) {
out[kebabToCamel(name)] = observedAttribute(instance, name);
return out;
}, {});
}
function kebabToCamel(name) {
return name.replace(/-./g, (x) => x[1].toUpperCase());
}
// src/dom-lib/common.js
// src/signals-lib/common.js
var signals_global = {
/**
* Checks if a value is a signal
* @param {any} attributes - Value to check
* @returns {boolean} Whether the value is a signal
*/
isSignal(attributes) {
return false;
},
/**
* Processes an attribute that might be reactive
* @param {Element} obj - Element that owns the attribute
* @param {string} key - Attribute name
* @param {any} attr - Attribute value
* @param {Function} set - Function to set the attribute
* @returns {any} Processed attribute value
*/
processReactiveAttribute(obj, key, attr, set) {
return attr;
}
};
function registerReactivity(def, global = true) {
if (global) return oAssign(signals_global, def);
Object.setPrototypeOf(def, signals_global);
return def;
}
function signals(_this) {
return isProtoFrom(_this, signals_global) && _this !== signals_global ? _this : signals_global;
}
// src/dom-common.js
var enviroment = {
setDeleteAttr,
ssr: "",
D: globalThis.document,
N: globalThis.Node,
F: globalThis.DocumentFragment,
H: globalThis.HTMLElement,
S: globalThis.SVGElement,
@ -57,7 +88,276 @@ var evc = "dde:connected";
var evd = "dde:disconnected";
var eva = "dde:attributeChanged";
// src/dom-lib/events-observer.js
// src/dom.js
function queue(promise) {
return enviroment.q(promise);
}
var scopes = [{
get scope() {
return enviroment.D.body;
},
host: (c) => c ? c(enviroment.D.body) : enviroment.D.body,
prevent: true
}];
var scope = {
/**
* Gets the current scope
* @returns {Object} Current scope context
*/
get current() {
return scopes[scopes.length - 1];
},
/**
* Gets the host element of the current scope
* @returns {Function} Host accessor function
*/
get host() {
return this.current.host;
},
/**
* Prevents default behavior in the current scope
* @returns {Object} Current scope context
*/
preventDefault() {
const { current } = this;
current.prevent = true;
return current;
},
/**
* Gets a copy of the current scope stack
* @returns {Array} Copy of scope stack
*/
get state() {
return [...scopes];
},
/**
* Pushes a new scope to the stack
* @param {Object} [s={}] - Scope object to push
* @returns {number} New length of the scope stack
*/
push(s = {}) {
return scopes.push(oAssign({}, this.current, { prevent: false }, s));
},
/**
* Pushes the root scope to the stack
* @returns {number} New length of the scope stack
*/
pushRoot() {
return scopes.push(scopes[0]);
},
/**
* Pops the current scope from the stack
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
*/
pop() {
if (scopes.length === 1) return;
return scopes.pop();
}
};
function append(...els) {
this.appendOriginal(...els);
return this;
}
function chainableAppend(el) {
if (el.append === append) return el;
el.appendOriginal = el.append;
el.append = append;
return el;
}
var namespace;
function createElement(tag, attributes, ...addons) {
const s = signals(this);
let scoped = 0;
let el, el_host;
if (Object(attributes) !== attributes || s.isSignal(attributes))
attributes = { textContent: attributes };
switch (true) {
case typeof tag === "function": {
scoped = 1;
const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
scope.push({ scope: tag, host });
el = tag(attributes || void 0);
const is_fragment = isInstance(el, enviroment.F);
if (el.nodeName === "#comment") break;
const el_mark = createElement.mark({
type: "component",
name: tag.name,
host: is_fragment ? "this" : "parentElement"
});
el.prepend(el_mark);
if (is_fragment) el_host = el_mark;
break;
}
case tag === "#text":
el = assign.call(this, enviroment.D.createTextNode(""), attributes);
break;
case (tag === "<>" || !tag):
el = assign.call(this, enviroment.D.createDocumentFragment(), attributes);
break;
case Boolean(namespace):
el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes);
break;
case !el:
el = assign.call(this, enviroment.D.createElement(tag), attributes);
}
chainableAppend(el);
if (!el_host) el_host = el;
addons.forEach((c) => c(el_host));
if (scoped) scope.pop();
scoped = 2;
return el;
}
createElement.mark = function(attrs, is_open = false) {
attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" ");
const end = is_open ? "" : "/";
const out = enviroment.D.createComment(`<dde:mark ${attrs}${enviroment.ssr}${end}>`);
if (is_open) out.end = enviroment.D.createComment("</dde:mark>");
return out;
};
function createElementNS(ns) {
const _this = this;
return function createElementNSCurried(...rest) {
namespace = ns;
const el = createElement.call(_this, ...rest);
namespace = void 0;
return el;
};
}
function simulateSlots(element, root = element) {
const mark_e = "\xB9\u2070", mark_s = "\u2713";
const slots = Object.fromEntries(
Array.from(root.querySelectorAll("slot")).filter((s) => !s.name.endsWith(mark_e)).map((s) => [s.name += mark_e, s])
);
element.append = new Proxy(element.append, {
apply(orig, _, els) {
if (els[0] === root) return orig.apply(element, els);
for (const el of els) {
const name = (el.slot || "") + mark_e;
try {
elementAttribute(el, "remove", "slot");
} catch (_error) {
}
const slot = slots[name];
if (!slot) return;
if (!slot.name.startsWith(mark_s)) {
slot.childNodes.forEach((c) => c.remove());
slot.name = mark_s + name;
}
slot.append(el);
}
element.append = orig;
return element;
}
});
if (element !== root) {
const els = Array.from(element.childNodes);
element.append(...els);
}
return root;
}
var assign_context = /* @__PURE__ */ new WeakMap();
var { setDeleteAttr: setDeleteAttr2 } = enviroment;
function assign(element, ...attributes) {
if (!attributes.length) return element;
assign_context.set(element, assignContext(element, this));
for (const [key, value] of Object.entries(oAssign({}, ...attributes)))
assignAttribute.call(this, element, key, value);
assign_context.delete(element);
return element;
}
function assignAttribute(element, key, value) {
const { setRemoveAttr, s } = assignContext(element, this);
const _this = this;
value = s.processReactiveAttribute(
element,
key,
value,
(key2, value2) => assignAttribute.call(_this, element, key2, value2)
);
const [k] = key;
if ("=" === k) return setRemoveAttr(key.slice(1), value);
if ("." === k) return setDelete(element, key.slice(1), value);
if (/(aria|data)([A-Z])/.test(key)) {
key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
return setRemoveAttr(key, value);
}
if ("className" === key) key = "class";
switch (key) {
case "xlink:href":
return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink");
case "textContent":
return setDeleteAttr2(element, key, value);
case "style":
if (typeof value !== "object") break;
/* falls through */
case "dataset":
return forEachEntries(s, key, element, value, setDelete.bind(null, element[key]));
case "ariaset":
return forEachEntries(s, key, element, value, (key2, val) => setRemoveAttr("aria-" + key2, val));
case "classList":
return classListDeclarative.call(_this, element, value);
}
return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value);
}
function assignContext(element, _this) {
if (assign_context.has(element)) return assign_context.get(element);
const is_svg = isInstance(element, enviroment.S);
const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
const s = signals(_this);
return { setRemoveAttr, s };
}
function classListDeclarative(element, toggle) {
const s = signals(this);
forEachEntries(
s,
"classList",
element,
toggle,
(class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val))
);
return element;
}
function elementAttribute(element, op, key, value) {
if (isInstance(element, enviroment.H))
return element[op + "Attribute"](key, value);
return element[op + "AttributeNS"](null, key, value);
}
function isPropSetter(el, key) {
if (!(key in el)) return false;
const des = getPropDescriptor(el, key);
return !isUndef(des.set);
}
function getPropDescriptor(p, key) {
p = Object.getPrototypeOf(p);
if (!p) return {};
const des = Object.getOwnPropertyDescriptor(p, key);
if (!des) return getPropDescriptor(p, key);
return des;
}
function forEachEntries(s, target, element, obj, cb) {
const S = String;
if (typeof obj !== "object" || obj === null) return;
return Object.entries(obj).forEach(function process([key, val]) {
if (!key) return;
key = new S(key);
key.target = target;
val = s.processReactiveAttribute(element, key, val, cb);
cb(key, val);
});
}
function setRemove(obj, prop, key, val) {
return obj[(isUndef(val) ? "remove" : "set") + prop](key, val);
}
function setRemoveNS(obj, prop, key, val, ns = null) {
return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, val);
}
function setDelete(obj, key, val) {
Reflect.set(obj, key, val);
if (!isUndef(val)) return;
return Reflect.deleteProperty(obj, key);
}
// src/events-observer.js
var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
get() {
return () => {
@ -168,13 +468,18 @@ function connectionsChangesObserverConstructor() {
is_observing = false;
observer.disconnect();
}
function requestIdle() {
return new Promise(function(resolve) {
(requestIdleCallback || requestAnimationFrame)(resolve);
});
}
async function collectChildren(element) {
if (store.size > 30)
await requestIdle();
const out = [];
if (!isInstance(element, enviroment.N)) return out;
if (!isInstance(element, Node)) return out;
for (const el of store.keys()) {
if (el === element || !isInstance(el, enviroment.N)) continue;
if (el === element || !isInstance(el, Node)) continue;
if (element.contains(el))
out.push(el);
}
@ -216,371 +521,8 @@ function connectionsChangesObserverConstructor() {
}
}
// src/dom-lib/events.js
function dispatchEvent(name, options, host) {
if (typeof options === "function") {
host = options;
options = null;
}
if (!options) options = {};
return function dispatch(element, ...d) {
if (host) {
d.unshift(element);
element = typeof host === "function" ? host() : host;
}
const event = d.length ? new CustomEvent(name, oAssign({ detail: d[0] }, options)) : new Event(name, options);
return element.dispatchEvent(event);
};
}
function on(event, listener, options) {
return function registerElement(element) {
element.addEventListener(event, listener, options);
return element;
};
}
on.defer = (fn) => setTimeout.bind(null, fn, 0);
var lifeOptions = (obj) => oAssign({}, typeof obj === "object" ? obj : null, { once: true });
on.connected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evc, listener, options);
if (element[keyLTE]) return element;
if (element.isConnected) return element.dispatchEvent(new Event(evc)), element;
const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener));
if (c) c_ch_o.onConnected(element, listener);
return element;
};
};
on.disconnected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evd, listener, options);
if (element[keyLTE]) return element;
const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener));
if (c) c_ch_o.onDisconnected(element, listener);
return element;
};
};
// src/dom-lib/scopes.js
var scopes = [{
get scope() {
return enviroment.D.body;
},
host: (c) => c ? c(enviroment.D.body) : enviroment.D.body,
prevent: true
}];
var store_abort = /* @__PURE__ */ new WeakMap();
var scope = {
/**
* Gets the current scope
* @returns {typeof scopes[number]} Current scope context
*/
get current() {
return scopes[scopes.length - 1];
},
/**
* Gets the host element of the current scope
* @returns {Function} Host accessor function
*/
get host() {
return this.current.host;
},
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
get signal() {
const { host } = this;
if (store_abort.has(host)) return store_abort.get(host);
const a = new AbortController();
store_abort.set(host, a);
host(on.disconnected(() => a.abort()));
return a.signal;
},
/**
* Prevents default behavior in the current scope
* @returns {Object} Current scope context
*/
preventDefault() {
const { current } = this;
current.prevent = true;
return current;
},
/**
* Gets a copy of the current scope stack
* @returns {Array} Copy of scope stack
*/
get state() {
return [...scopes];
},
/**
* Pushes a new scope to the stack
* @param {Object} [s={}] - Scope object to push
* @returns {number} New length of the scope stack
*/
push(s = {}) {
return scopes.push(oAssign({}, this.current, { prevent: false }, s));
},
/**
* Pushes the root scope to the stack
* @returns {number} New length of the scope stack
*/
pushRoot() {
return scopes.push(scopes[0]);
},
/**
* Pops the current scope from the stack
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
*/
pop() {
if (scopes.length === 1) return;
return scopes.pop();
}
};
// src/signals-lib/common.js
var signals_global = {
/**
* Checks if a value is a signal
* @param {any} attributes - Value to check
* @returns {boolean} Whether the value is a signal
*/
isSignal(attributes) {
return false;
},
/**
* Processes an attribute that might be reactive
* @param {Element} obj - Element that owns the attribute
* @param {string} key - Attribute name
* @param {any} attr - Attribute value
* @param {Function} set - Function to set the attribute
* @returns {any} Processed attribute value
*/
processReactiveAttribute(obj, key, attr, set) {
return attr;
}
};
function registerReactivity(def, global = true) {
if (global) return oAssign(signals_global, def);
Object.setPrototypeOf(def, signals_global);
return def;
}
function signals(_this) {
return isProtoFrom(_this, signals_global) && _this !== signals_global ? _this : signals_global;
}
// src/dom-lib/helpers.js
function setRemove(obj, prop, key, val) {
return obj[(isUndef(val) ? "remove" : "set") + prop](key, val);
}
function setRemoveNS(obj, prop, key, val, ns = null) {
return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, val);
}
function setDelete(obj, key, val) {
Reflect.set(obj, key, val);
if (!isUndef(val)) return;
return Reflect.deleteProperty(obj, key);
}
function elementAttribute(element, op, key, value) {
if (isInstance(element, enviroment.H))
return element[op + "Attribute"](key, value);
return element[op + "AttributeNS"](null, key, value);
}
// src/dom-lib/el.js
function queue(promise) {
return enviroment.q(promise);
}
function append(...els) {
this.appendOriginal(...els);
return this;
}
function chainableAppend(el) {
if (el.append === append) return el;
el.appendOriginal = el.append;
el.append = append;
return el;
}
var namespace;
function createElement(tag, attributes, ...addons) {
const s = signals(this);
let scoped = 0;
let el, el_host;
const att_type = typeof attributes;
if (att_type === "string" || att_type === "number" || s.isSignal(attributes))
attributes = { textContent: attributes };
switch (true) {
case typeof tag === "function": {
scoped = 1;
const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
scope.push({ scope: tag, host });
el = tag(attributes || void 0);
const is_fragment = isInstance(el, enviroment.F);
if (el.nodeName === "#comment") break;
const el_mark = createElement.mark({
type: "component",
name: tag.name,
host: is_fragment ? "this" : "parentElement"
});
el.prepend(el_mark);
if (is_fragment) el_host = el_mark;
break;
}
case tag === "#text":
el = assign.call(this, enviroment.D.createTextNode(""), attributes);
break;
case (tag === "<>" || !tag):
el = assign.call(this, enviroment.D.createDocumentFragment(), attributes);
break;
case Boolean(namespace):
el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes);
break;
case !el:
el = assign.call(this, enviroment.D.createElement(tag), attributes);
}
chainableAppend(el);
if (!el_host) el_host = el;
addons.forEach((c) => c(el_host));
if (scoped) scope.pop();
scoped = 2;
return el;
}
createElement.mark = function(attrs, is_open = false) {
attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" ");
const end = is_open ? "" : "/";
const out = enviroment.D.createComment(`<dde:mark ${attrs}${enviroment.ssr}${end}>`);
if (is_open) out.end = enviroment.D.createComment("</dde:mark>");
return out;
};
function createElementNS(ns) {
const _this = this;
return function createElementNSCurried(...rest) {
namespace = ns;
const el = createElement.call(_this, ...rest);
namespace = void 0;
return el;
};
}
var assign_context = /* @__PURE__ */ new WeakMap();
var { setDeleteAttr: setDeleteAttr2 } = enviroment;
function assign(element, ...attributes) {
if (!attributes.length) return element;
assign_context.set(element, assignContext(element, this));
for (const [key, value] of Object.entries(oAssign({}, ...attributes)))
assignAttribute.call(this, element, key, value);
assign_context.delete(element);
return element;
}
function assignAttribute(element, key, value) {
const { setRemoveAttr, s } = assignContext(element, this);
const _this = this;
value = s.processReactiveAttribute(
element,
key,
value,
(key2, value2) => assignAttribute.call(_this, element, key2, value2)
);
const [k] = key;
if ("=" === k) return setRemoveAttr(key.slice(1), value);
if ("." === k) return setDelete(element, key.slice(1), value);
if (/(aria|data)([A-Z])/.test(key)) {
key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
return setRemoveAttr(key, value);
}
if ("className" === key) key = "class";
switch (key) {
case "xlink:href":
return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink");
case "textContent":
return setDeleteAttr2(element, key, value);
case "style":
if (typeof value !== "object") break;
/* falls through */
case "dataset":
return forEachEntries(s, key, element, value, setDelete.bind(null, element[key]));
case "ariaset":
return forEachEntries(s, key, element, value, (key2, val) => setRemoveAttr("aria-" + key2, val));
case "classList":
return classListDeclarative.call(_this, element, value);
}
return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value);
}
function assignContext(element, _this) {
if (assign_context.has(element)) return assign_context.get(element);
const is_svg = isInstance(element, enviroment.S);
const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
const s = signals(_this);
return { setRemoveAttr, s };
}
function classListDeclarative(element, toggle) {
const s = signals(this);
forEachEntries(
s,
"classList",
element,
toggle,
(class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val))
);
return element;
}
function isPropSetter(el, key) {
if (!(key in el)) return false;
const des = getPropDescriptor(el, key);
return !isUndef(des.set);
}
function getPropDescriptor(p, key) {
p = Object.getPrototypeOf(p);
if (!p) return {};
const des = Object.getOwnPropertyDescriptor(p, key);
if (!des) return getPropDescriptor(p, key);
return des;
}
function forEachEntries(s, target, element, obj, cb) {
const S = String;
if (typeof obj !== "object" || obj === null) return;
return Object.entries(obj).forEach(function process([key, val]) {
if (!key) return;
key = new S(key);
key.target = target;
val = s.processReactiveAttribute(element, key, val, cb);
cb(key, val);
});
}
// src/dom-lib/customElement.js
function simulateSlots(element, root = element) {
const mark_e = "\xB9\u2070", mark_s = "\u2713";
const slots = Object.fromEntries(
Array.from(root.querySelectorAll("slot")).filter((s) => !s.name.endsWith(mark_e)).map((s) => [s.name += mark_e, s])
);
element.append = new Proxy(element.append, {
apply(orig, _, els) {
if (els[0] === root) return orig.apply(element, els);
for (const el of els) {
const name = (el.slot || "") + mark_e;
try {
elementAttribute(el, "remove", "slot");
} catch (_error) {
}
const slot = slots[name];
if (!slot) return;
if (!slot.name.startsWith(mark_s)) {
slot.childNodes.forEach((c) => c.remove());
slot.name = mark_s + name;
}
slot.append(el);
}
element.append = orig;
return element;
}
});
if (element !== root) {
const els = Array.from(element.childNodes);
element.append(...els);
}
return root;
}
function customElementRender(target, render, props = {}) {
// src/customElement.js
function customElementRender(target, render, props = observedAttributes2) {
const custom_element = target.host || target;
scope.push({
scope: custom_element,
@ -621,40 +563,81 @@ function wrapMethod(obj, method, apply) {
obj[method] = new Proxy(obj[method] || (() => {
}), { apply });
}
// src/memo.js
var memoMark = "__dde_memo";
var memo_scope = [];
function memo(key, generator) {
if (!memo_scope.length) return generator(key);
const k = typeof key === "object" ? JSON.stringify(key) : key;
const [{ cache, after }] = memo_scope;
return after(k, hasOwn(cache, k) ? cache[k] : generator(key));
function observedAttributes2(instance) {
return observedAttributes(instance, (i, n) => i.getAttribute(n));
}
memo.isScope = function(obj) {
return obj[memoMark];
};
memo.scope = function memoScopeCreate(fun, { signal, onlyLast } = {}) {
let cache = oCreate();
function memoScope(...args) {
if (signal && signal.aborted)
return fun.apply(this, args);
let cache_local = onlyLast ? cache : oCreate();
memo_scope.unshift({
cache,
after(key, val) {
return cache_local[key] = val;
}
});
const out = fun.apply(this, args);
memo_scope.shift();
cache = cache_local;
return out;
// src/events.js
function dispatchEvent(name, options, host) {
if (typeof options === "function") {
host = options;
options = null;
}
memoScope[memoMark] = true;
memoScope.clear = () => cache = oCreate();
if (signal) signal.addEventListener("abort", memoScope.clear);
return memoScope;
if (!options) options = {};
return function dispatch(element, ...d) {
if (host) {
d.unshift(element);
element = typeof host === "function" ? host() : host;
}
const event = d.length ? new CustomEvent(name, oAssign({ detail: d[0] }, options)) : new Event(name, options);
return element.dispatchEvent(event);
};
}
function on(event, listener, options) {
return function registerElement(element) {
element.addEventListener(event, listener, options);
return element;
};
}
var lifeOptions = (obj) => oAssign({}, typeof obj === "object" ? obj : null, { once: true });
on.connected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evc, listener, options);
if (element[keyLTE]) return element;
if (element.isConnected) return element.dispatchEvent(new Event(evc)), element;
const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener));
if (c) c_ch_o.onConnected(element, listener);
return element;
};
};
on.disconnected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evd, listener, options);
if (element[keyLTE]) return element;
const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener));
if (c) c_ch_o.onDisconnected(element, listener);
return element;
};
};
var store_abort = /* @__PURE__ */ new WeakMap();
on.disconnectedAsAbort = function(host) {
if (store_abort.has(host)) return store_abort.get(host);
const a = new AbortController();
store_abort.set(host, a);
host(on.disconnected(() => a.abort()));
return a.signal;
};
var els_attribute_store = /* @__PURE__ */ new WeakSet();
on.attributeChanged = function(listener, options) {
if (typeof options !== "object")
options = {};
return function registerElement(element) {
element.addEventListener(eva, listener, options);
if (element[keyLTE] || els_attribute_store.has(element))
return element;
if (!enviroment.M) return element;
const observer = new enviroment.M(function(mutations) {
for (const { attributeName, target } of mutations)
target.dispatchEvent(
new CustomEvent(eva, { detail: [attributeName, target.getAttribute(attributeName)] })
);
});
const c = onAbort(options.signal, () => observer.disconnect());
if (c) observer.observe(element, { attributes: true });
return element;
};
};
export {
assign,
@ -668,8 +651,9 @@ export {
dispatchEvent,
createElement as el,
createElementNS as elNS,
elementAttribute,
lifecyclesToEvents,
memo,
observedAttributes2 as observedAttributes,
on,
queue,
registerReactivity,

859
dist/esm.min.d.ts vendored
View File

@ -1,859 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V, force?: boolean): V;
toJSON(): V;
valueOf(): V;
}
export type Action<V> = (this: {
value: V;
stopPropagation(): void;
}, ...a: any[]) => typeof signal._ | void;
//type SymbolSignal= Symbol;
export type SymbolOnclear = symbol;
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
first_time?: boolean;
};
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
export interface signal {
_: Symbol;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
};
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
declare const signal: signal;
declare global {
type ddeSignal<T, A = {}> = Signal<T, A>;
type ddeAction<V> = Action<V>;
type ddeActions<V> = Actions<V>;
}
export type CustomElementTagNameMap = {
"#text": Text;
"#comment": Comment;
};
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
declare global {
type ddeComponentAttributes = Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
type ddeString = string | Signal<string, {}>;
type ddeStringable = ddeString | number | Signal<number, {}>;
}
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
export type PascalCase = `${Capitalize<string>}${string}`;
export type AttrsModified = {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
}>;
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>;
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>;
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
export type IsReadonly<T, K extends keyof T> = T extends {
readonly [P in K]: T[K];
} ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
export type ElementAttributes<T extends SupportedElement> = Partial<{
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(attrs: {
type: "component" | "reactive" | "later";
name?: string;
host?: "this" | "parentElement";
}, is_open?: boolean): Comment;
}
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<A extends {
textContent: ddeStringable;
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
export function el(tag_name?: "<>"): ddeDocumentFragment;
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
export interface On {
/** Listens to the DOM event. See {@link Document.addEventListener} */
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
target: EL;
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/**
* Fires after the next tick of the Javascript event loop.
* This is handy for example to apply some property depending on the element content:
* ```js
* const selected= "Z";
* //...
* return el("form").append(
* el("select", null, on.defer(e=> e.value=selected)).append(
* el("option", { value: "A", textContent: "A" }),
* //...
* el("option", { value: "Z", textContent: "Z" }),
* ),
* );
* ```
* */
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
}
export const on: On;
export type Scope = {
scope: Node | Function | Object;
host: Host<SupportedElement>;
custom_element: false | HTMLElement;
prevent: boolean;
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope;
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T;
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: Host<SupportedElement>;
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
signal: AbortSignal;
state: Scope[];
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>;
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>;
};
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/**
* Memoization utility for caching DOM elements to improve performance.
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
*
* @param key - Unique identifier for the element (usually an ID or unique value)
* @param generator - Function that creates the element
* @returns The cached element if the key exists, otherwise the result of the generator function
*
* @example
* ```ts
* // Within S.el for list rendering
* S.el(itemsSignal, (items, memo) =>
* el("ul").append(
* ...items.map(item =>
* memo(item.id, () => el(ItemComponent, item))
* )
* )
* )
* ```
*/
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
/**
* Memo namespace containing utility functions for memoization.
*/
export namespace memo {
/**
* Checks if an object is a memo scope.
* @param obj - The object to check
* @returns True if the object is a memo scope
*/
export function isScope(obj: any): boolean;
/**
* Creates a memoized function with optional cleanup support.
*
* @param fun - The function to memoize
* @param options - Configuration options
* @param options.signal - AbortSignal for cleanup
* @param options.onlyLast - When true, only keeps the cache from the most recent call
* @returns A memoized version of the function with a .clear() method
*
* @example
* ```ts
* const renderItems = memo.scope(function(items) {
* return items.map(item =>
* memo(item.id, () => el("div", item.name))
* );
* }, {
* signal: controller.signal,
* onlyLast: true
* });
* ```
*/
export function scope<F extends Function>(fun: F, options?: {
signal?: AbortSignal;
onlyLast?: boolean;
}): F & {
clear: () => void;
};
}
/* TypeScript MEH */
declare global {
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
interface ddeDocumentFragment extends DocumentFragment {
append: ddeAppend<ddeDocumentFragment>;
}
interface ddeHTMLElement extends HTMLElement {
append: ddeAppend<ddeHTMLElement>;
}
interface ddeSVGElement extends SVGElement {
append: ddeAppend<ddeSVGElement>;
}
interface ddeMathMLElement extends MathMLElement {
append: ddeAppend<ddeMathMLElement>;
}
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
append: ddeAppend<ddeHTMLAnchorElement>;
}
export interface ddeHTMLAreaElement extends HTMLAreaElement {
append: ddeAppend<ddeHTMLAreaElement>;
}
export interface ddeHTMLAudioElement extends HTMLAudioElement {
append: ddeAppend<ddeHTMLAudioElement>;
}
export interface ddeHTMLBaseElement extends HTMLBaseElement {
append: ddeAppend<ddeHTMLBaseElement>;
}
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
append: ddeAppend<ddeHTMLQuoteElement>;
}
export interface ddeHTMLBodyElement extends HTMLBodyElement {
append: ddeAppend<ddeHTMLBodyElement>;
}
export interface ddeHTMLBRElement extends HTMLBRElement {
append: ddeAppend<ddeHTMLBRElement>;
}
export interface ddeHTMLButtonElement extends HTMLButtonElement {
append: ddeAppend<ddeHTMLButtonElement>;
}
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
append: ddeAppend<ddeHTMLCanvasElement>;
}
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
append: ddeAppend<ddeHTMLTableCaptionElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLDataElement extends HTMLDataElement {
append: ddeAppend<ddeHTMLDataElement>;
}
export interface ddeHTMLDataListElement extends HTMLDataListElement {
append: ddeAppend<ddeHTMLDataListElement>;
}
export interface ddeHTMLModElement extends HTMLModElement {
append: ddeAppend<ddeHTMLModElement>;
}
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
append: ddeAppend<ddeHTMLDetailsElement>;
}
export interface ddeHTMLDialogElement extends HTMLDialogElement {
append: ddeAppend<ddeHTMLDialogElement>;
}
export interface ddeHTMLDivElement extends HTMLDivElement {
append: ddeAppend<ddeHTMLDivElement>;
}
export interface ddeHTMLDListElement extends HTMLDListElement {
append: ddeAppend<ddeHTMLDListElement>;
}
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
append: ddeAppend<ddeHTMLEmbedElement>;
}
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
append: ddeAppend<ddeHTMLFieldSetElement>;
}
export interface ddeHTMLFormElement extends HTMLFormElement {
append: ddeAppend<ddeHTMLFormElement>;
}
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
append: ddeAppend<ddeHTMLHeadingElement>;
}
export interface ddeHTMLHeadElement extends HTMLHeadElement {
append: ddeAppend<ddeHTMLHeadElement>;
}
export interface ddeHTMLHRElement extends HTMLHRElement {
append: ddeAppend<ddeHTMLHRElement>;
}
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
append: ddeAppend<ddeHTMLHtmlElement>;
}
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
append: ddeAppend<ddeHTMLIFrameElement>;
}
export interface ddeHTMLImageElement extends HTMLImageElement {
append: ddeAppend<ddeHTMLImageElement>;
}
export interface ddeHTMLInputElement extends HTMLInputElement {
append: ddeAppend<ddeHTMLInputElement>;
}
export interface ddeHTMLLabelElement extends HTMLLabelElement {
append: ddeAppend<ddeHTMLLabelElement>;
}
export interface ddeHTMLLegendElement extends HTMLLegendElement {
append: ddeAppend<ddeHTMLLegendElement>;
}
export interface ddeHTMLLIElement extends HTMLLIElement {
append: ddeAppend<ddeHTMLLIElement>;
}
export interface ddeHTMLLinkElement extends HTMLLinkElement {
append: ddeAppend<ddeHTMLLinkElement>;
}
export interface ddeHTMLMapElement extends HTMLMapElement {
append: ddeAppend<ddeHTMLMapElement>;
}
export interface ddeHTMLMenuElement extends HTMLMenuElement {
append: ddeAppend<ddeHTMLMenuElement>;
}
export interface ddeHTMLMetaElement extends HTMLMetaElement {
append: ddeAppend<ddeHTMLMetaElement>;
}
export interface ddeHTMLMeterElement extends HTMLMeterElement {
append: ddeAppend<ddeHTMLMeterElement>;
}
export interface ddeHTMLObjectElement extends HTMLObjectElement {
append: ddeAppend<ddeHTMLObjectElement>;
}
export interface ddeHTMLOListElement extends HTMLOListElement {
append: ddeAppend<ddeHTMLOListElement>;
}
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
append: ddeAppend<ddeHTMLOptGroupElement>;
}
export interface ddeHTMLOptionElement extends HTMLOptionElement {
append: ddeAppend<ddeHTMLOptionElement>;
}
export interface ddeHTMLOutputElement extends HTMLOutputElement {
append: ddeAppend<ddeHTMLOutputElement>;
}
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
append: ddeAppend<ddeHTMLParagraphElement>;
}
export interface ddeHTMLPictureElement extends HTMLPictureElement {
append: ddeAppend<ddeHTMLPictureElement>;
}
export interface ddeHTMLPreElement extends HTMLPreElement {
append: ddeAppend<ddeHTMLPreElement>;
}
export interface ddeHTMLProgressElement extends HTMLProgressElement {
append: ddeAppend<ddeHTMLProgressElement>;
}
export interface ddeHTMLScriptElement extends HTMLScriptElement {
append: ddeAppend<ddeHTMLScriptElement>;
}
export interface ddeHTMLSelectElement extends HTMLSelectElement {
append: ddeAppend<ddeHTMLSelectElement>;
}
export interface ddeHTMLSlotElement extends HTMLSlotElement {
append: ddeAppend<ddeHTMLSlotElement>;
}
export interface ddeHTMLSourceElement extends HTMLSourceElement {
append: ddeAppend<ddeHTMLSourceElement>;
}
export interface ddeHTMLSpanElement extends HTMLSpanElement {
append: ddeAppend<ddeHTMLSpanElement>;
}
export interface ddeHTMLStyleElement extends HTMLStyleElement {
append: ddeAppend<ddeHTMLStyleElement>;
}
export interface ddeHTMLTableElement extends HTMLTableElement {
append: ddeAppend<ddeHTMLTableElement>;
}
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
append: ddeAppend<ddeHTMLTableSectionElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
append: ddeAppend<ddeHTMLTemplateElement>;
}
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
append: ddeAppend<ddeHTMLTextAreaElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTimeElement extends HTMLTimeElement {
append: ddeAppend<ddeHTMLTimeElement>;
}
export interface ddeHTMLTitleElement extends HTMLTitleElement {
append: ddeAppend<ddeHTMLTitleElement>;
}
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
append: ddeAppend<ddeHTMLTableRowElement>;
}
export interface ddeHTMLTrackElement extends HTMLTrackElement {
append: ddeAppend<ddeHTMLTrackElement>;
}
export interface ddeHTMLUListElement extends HTMLUListElement {
append: ddeAppend<ddeHTMLUListElement>;
}
export interface ddeHTMLVideoElement extends HTMLVideoElement {
append: ddeAppend<ddeHTMLVideoElement>;
}
export interface ddeSVGAElement extends SVGAElement {
append: ddeAppend<ddeSVGAElement>;
}
export interface ddeSVGAnimateElement extends SVGAnimateElement {
append: ddeAppend<ddeSVGAnimateElement>;
}
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
append: ddeAppend<ddeSVGAnimateMotionElement>;
}
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
append: ddeAppend<ddeSVGAnimateTransformElement>;
}
export interface ddeSVGCircleElement extends SVGCircleElement {
append: ddeAppend<ddeSVGCircleElement>;
}
export interface ddeSVGClipPathElement extends SVGClipPathElement {
append: ddeAppend<ddeSVGClipPathElement>;
}
export interface ddeSVGDefsElement extends SVGDefsElement {
append: ddeAppend<ddeSVGDefsElement>;
}
export interface ddeSVGDescElement extends SVGDescElement {
append: ddeAppend<ddeSVGDescElement>;
}
export interface ddeSVGEllipseElement extends SVGEllipseElement {
append: ddeAppend<ddeSVGEllipseElement>;
}
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
append: ddeAppend<ddeSVGFEBlendElement>;
}
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
append: ddeAppend<ddeSVGFEColorMatrixElement>;
}
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
append: ddeAppend<ddeSVGFEComponentTransferElement>;
}
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
append: ddeAppend<ddeSVGFECompositeElement>;
}
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
}
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
}
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
}
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
append: ddeAppend<ddeSVGFEDistantLightElement>;
}
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
append: ddeAppend<ddeSVGFEDropShadowElement>;
}
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
append: ddeAppend<ddeSVGFEFloodElement>;
}
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
append: ddeAppend<ddeSVGFEFuncAElement>;
}
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
append: ddeAppend<ddeSVGFEFuncBElement>;
}
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
append: ddeAppend<ddeSVGFEFuncGElement>;
}
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
append: ddeAppend<ddeSVGFEFuncRElement>;
}
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
}
export interface ddeSVGFEImageElement extends SVGFEImageElement {
append: ddeAppend<ddeSVGFEImageElement>;
}
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
append: ddeAppend<ddeSVGFEMergeElement>;
}
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
append: ddeAppend<ddeSVGFEMergeNodeElement>;
}
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
append: ddeAppend<ddeSVGFEMorphologyElement>;
}
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
append: ddeAppend<ddeSVGFEOffsetElement>;
}
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
append: ddeAppend<ddeSVGFEPointLightElement>;
}
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
append: ddeAppend<ddeSVGFESpecularLightingElement>;
}
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
append: ddeAppend<ddeSVGFESpotLightElement>;
}
export interface ddeSVGFETileElement extends SVGFETileElement {
append: ddeAppend<ddeSVGFETileElement>;
}
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
append: ddeAppend<ddeSVGFETurbulenceElement>;
}
export interface ddeSVGFilterElement extends SVGFilterElement {
append: ddeAppend<ddeSVGFilterElement>;
}
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
append: ddeAppend<ddeSVGForeignObjectElement>;
}
export interface ddeSVGGElement extends SVGGElement {
append: ddeAppend<ddeSVGGElement>;
}
export interface ddeSVGImageElement extends SVGImageElement {
append: ddeAppend<ddeSVGImageElement>;
}
export interface ddeSVGLineElement extends SVGLineElement {
append: ddeAppend<ddeSVGLineElement>;
}
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
append: ddeAppend<ddeSVGLinearGradientElement>;
}
export interface ddeSVGMarkerElement extends SVGMarkerElement {
append: ddeAppend<ddeSVGMarkerElement>;
}
export interface ddeSVGMaskElement extends SVGMaskElement {
append: ddeAppend<ddeSVGMaskElement>;
}
export interface ddeSVGMetadataElement extends SVGMetadataElement {
append: ddeAppend<ddeSVGMetadataElement>;
}
export interface ddeSVGMPathElement extends SVGMPathElement {
append: ddeAppend<ddeSVGMPathElement>;
}
export interface ddeSVGPathElement extends SVGPathElement {
append: ddeAppend<ddeSVGPathElement>;
}
export interface ddeSVGPatternElement extends SVGPatternElement {
append: ddeAppend<ddeSVGPatternElement>;
}
export interface ddeSVGPolygonElement extends SVGPolygonElement {
append: ddeAppend<ddeSVGPolygonElement>;
}
export interface ddeSVGPolylineElement extends SVGPolylineElement {
append: ddeAppend<ddeSVGPolylineElement>;
}
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
append: ddeAppend<ddeSVGRadialGradientElement>;
}
export interface ddeSVGRectElement extends SVGRectElement {
append: ddeAppend<ddeSVGRectElement>;
}
export interface ddeSVGScriptElement extends SVGScriptElement {
append: ddeAppend<ddeSVGScriptElement>;
}
export interface ddeSVGSetElement extends SVGSetElement {
append: ddeAppend<ddeSVGSetElement>;
}
export interface ddeSVGStopElement extends SVGStopElement {
append: ddeAppend<ddeSVGStopElement>;
}
export interface ddeSVGStyleElement extends SVGStyleElement {
append: ddeAppend<ddeSVGStyleElement>;
}
export interface ddeSVGSVGElement extends SVGSVGElement {
append: ddeAppend<ddeSVGSVGElement>;
}
export interface ddeSVGSwitchElement extends SVGSwitchElement {
append: ddeAppend<ddeSVGSwitchElement>;
}
export interface ddeSVGSymbolElement extends SVGSymbolElement {
append: ddeAppend<ddeSVGSymbolElement>;
}
export interface ddeSVGTextElement extends SVGTextElement {
append: ddeAppend<ddeSVGTextElement>;
}
export interface ddeSVGTextPathElement extends SVGTextPathElement {
append: ddeAppend<ddeSVGTextPathElement>;
}
export interface ddeSVGTitleElement extends SVGTitleElement {
append: ddeAppend<ddeSVGTitleElement>;
}
export interface ddeSVGTSpanElement extends SVGTSpanElement {
append: ddeAppend<ddeSVGTSpanElement>;
}
export interface ddeSVGUseElement extends SVGUseElement {
append: ddeAppend<ddeSVGUseElement>;
}
export interface ddeSVGViewElement extends SVGViewElement {
append: ddeAppend<ddeSVGViewElement>;
}
export {
dispatchEvent$1 as dispatchEvent,
el as createElement,
elNS as createElementNS,
};
export {};

2
dist/esm.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,862 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V, force?: boolean): V;
toJSON(): V;
valueOf(): V;
}
export type Action<V> = (this: {
value: V;
stopPropagation(): void;
}, ...a: any[]) => typeof signal._ | void;
//type SymbolSignal= Symbol;
export type SymbolOnclear = symbol;
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
first_time?: boolean;
};
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
export interface signal {
_: Symbol;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
};
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
export const signal: signal;
export const S: signal;
declare global {
type ddeSignal<T, A = {}> = Signal<T, A>;
type ddeAction<V> = Action<V>;
type ddeActions<V> = Actions<V>;
}
export type CustomElementTagNameMap = {
"#text": Text;
"#comment": Comment;
};
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
declare global {
type ddeComponentAttributes = Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
type ddeString = string | Signal<string, {}>;
type ddeStringable = ddeString | number | Signal<number, {}>;
}
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
export type PascalCase = `${Capitalize<string>}${string}`;
export type AttrsModified = {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
}>;
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>;
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>;
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
export type IsReadonly<T, K extends keyof T> = T extends {
readonly [P in K]: T[K];
} ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
export type ElementAttributes<T extends SupportedElement> = Partial<{
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(attrs: {
type: "component" | "reactive" | "later";
name?: string;
host?: "this" | "parentElement";
}, is_open?: boolean): Comment;
}
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<A extends {
textContent: ddeStringable;
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
export function el(tag_name?: "<>"): ddeDocumentFragment;
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
export interface On {
/** Listens to the DOM event. See {@link Document.addEventListener} */
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
target: EL;
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/**
* Fires after the next tick of the Javascript event loop.
* This is handy for example to apply some property depending on the element content:
* ```js
* const selected= "Z";
* //...
* return el("form").append(
* el("select", null, on.defer(e=> e.value=selected)).append(
* el("option", { value: "A", textContent: "A" }),
* //...
* el("option", { value: "Z", textContent: "Z" }),
* ),
* );
* ```
* */
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
}
export const on: On;
export type Scope = {
scope: Node | Function | Object;
host: Host<SupportedElement>;
custom_element: false | HTMLElement;
prevent: boolean;
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope;
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T;
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: Host<SupportedElement>;
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
signal: AbortSignal;
state: Scope[];
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>;
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>;
};
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/**
* Memoization utility for caching DOM elements to improve performance.
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
*
* @param key - Unique identifier for the element (usually an ID or unique value)
* @param generator - Function that creates the element
* @returns The cached element if the key exists, otherwise the result of the generator function
*
* @example
* ```ts
* // Within S.el for list rendering
* S.el(itemsSignal, (items, memo) =>
* el("ul").append(
* ...items.map(item =>
* memo(item.id, () => el(ItemComponent, item))
* )
* )
* )
* ```
*/
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
/**
* Memo namespace containing utility functions for memoization.
*/
export namespace memo {
/**
* Checks if an object is a memo scope.
* @param obj - The object to check
* @returns True if the object is a memo scope
*/
export function isScope(obj: any): boolean;
/**
* Creates a memoized function with optional cleanup support.
*
* @param fun - The function to memoize
* @param options - Configuration options
* @param options.signal - AbortSignal for cleanup
* @param options.onlyLast - When true, only keeps the cache from the most recent call
* @returns A memoized version of the function with a .clear() method
*
* @example
* ```ts
* const renderItems = memo.scope(function(items) {
* return items.map(item =>
* memo(item.id, () => el("div", item.name))
* );
* }, {
* signal: controller.signal,
* onlyLast: true
* });
* ```
*/
export function scope<F extends Function>(fun: F, options?: {
signal?: AbortSignal;
onlyLast?: boolean;
}): F & {
clear: () => void;
};
}
/* TypeScript MEH */
declare global {
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
interface ddeDocumentFragment extends DocumentFragment {
append: ddeAppend<ddeDocumentFragment>;
}
interface ddeHTMLElement extends HTMLElement {
append: ddeAppend<ddeHTMLElement>;
}
interface ddeSVGElement extends SVGElement {
append: ddeAppend<ddeSVGElement>;
}
interface ddeMathMLElement extends MathMLElement {
append: ddeAppend<ddeMathMLElement>;
}
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
append: ddeAppend<ddeHTMLAnchorElement>;
}
export interface ddeHTMLAreaElement extends HTMLAreaElement {
append: ddeAppend<ddeHTMLAreaElement>;
}
export interface ddeHTMLAudioElement extends HTMLAudioElement {
append: ddeAppend<ddeHTMLAudioElement>;
}
export interface ddeHTMLBaseElement extends HTMLBaseElement {
append: ddeAppend<ddeHTMLBaseElement>;
}
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
append: ddeAppend<ddeHTMLQuoteElement>;
}
export interface ddeHTMLBodyElement extends HTMLBodyElement {
append: ddeAppend<ddeHTMLBodyElement>;
}
export interface ddeHTMLBRElement extends HTMLBRElement {
append: ddeAppend<ddeHTMLBRElement>;
}
export interface ddeHTMLButtonElement extends HTMLButtonElement {
append: ddeAppend<ddeHTMLButtonElement>;
}
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
append: ddeAppend<ddeHTMLCanvasElement>;
}
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
append: ddeAppend<ddeHTMLTableCaptionElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLDataElement extends HTMLDataElement {
append: ddeAppend<ddeHTMLDataElement>;
}
export interface ddeHTMLDataListElement extends HTMLDataListElement {
append: ddeAppend<ddeHTMLDataListElement>;
}
export interface ddeHTMLModElement extends HTMLModElement {
append: ddeAppend<ddeHTMLModElement>;
}
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
append: ddeAppend<ddeHTMLDetailsElement>;
}
export interface ddeHTMLDialogElement extends HTMLDialogElement {
append: ddeAppend<ddeHTMLDialogElement>;
}
export interface ddeHTMLDivElement extends HTMLDivElement {
append: ddeAppend<ddeHTMLDivElement>;
}
export interface ddeHTMLDListElement extends HTMLDListElement {
append: ddeAppend<ddeHTMLDListElement>;
}
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
append: ddeAppend<ddeHTMLEmbedElement>;
}
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
append: ddeAppend<ddeHTMLFieldSetElement>;
}
export interface ddeHTMLFormElement extends HTMLFormElement {
append: ddeAppend<ddeHTMLFormElement>;
}
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
append: ddeAppend<ddeHTMLHeadingElement>;
}
export interface ddeHTMLHeadElement extends HTMLHeadElement {
append: ddeAppend<ddeHTMLHeadElement>;
}
export interface ddeHTMLHRElement extends HTMLHRElement {
append: ddeAppend<ddeHTMLHRElement>;
}
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
append: ddeAppend<ddeHTMLHtmlElement>;
}
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
append: ddeAppend<ddeHTMLIFrameElement>;
}
export interface ddeHTMLImageElement extends HTMLImageElement {
append: ddeAppend<ddeHTMLImageElement>;
}
export interface ddeHTMLInputElement extends HTMLInputElement {
append: ddeAppend<ddeHTMLInputElement>;
}
export interface ddeHTMLLabelElement extends HTMLLabelElement {
append: ddeAppend<ddeHTMLLabelElement>;
}
export interface ddeHTMLLegendElement extends HTMLLegendElement {
append: ddeAppend<ddeHTMLLegendElement>;
}
export interface ddeHTMLLIElement extends HTMLLIElement {
append: ddeAppend<ddeHTMLLIElement>;
}
export interface ddeHTMLLinkElement extends HTMLLinkElement {
append: ddeAppend<ddeHTMLLinkElement>;
}
export interface ddeHTMLMapElement extends HTMLMapElement {
append: ddeAppend<ddeHTMLMapElement>;
}
export interface ddeHTMLMenuElement extends HTMLMenuElement {
append: ddeAppend<ddeHTMLMenuElement>;
}
export interface ddeHTMLMetaElement extends HTMLMetaElement {
append: ddeAppend<ddeHTMLMetaElement>;
}
export interface ddeHTMLMeterElement extends HTMLMeterElement {
append: ddeAppend<ddeHTMLMeterElement>;
}
export interface ddeHTMLObjectElement extends HTMLObjectElement {
append: ddeAppend<ddeHTMLObjectElement>;
}
export interface ddeHTMLOListElement extends HTMLOListElement {
append: ddeAppend<ddeHTMLOListElement>;
}
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
append: ddeAppend<ddeHTMLOptGroupElement>;
}
export interface ddeHTMLOptionElement extends HTMLOptionElement {
append: ddeAppend<ddeHTMLOptionElement>;
}
export interface ddeHTMLOutputElement extends HTMLOutputElement {
append: ddeAppend<ddeHTMLOutputElement>;
}
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
append: ddeAppend<ddeHTMLParagraphElement>;
}
export interface ddeHTMLPictureElement extends HTMLPictureElement {
append: ddeAppend<ddeHTMLPictureElement>;
}
export interface ddeHTMLPreElement extends HTMLPreElement {
append: ddeAppend<ddeHTMLPreElement>;
}
export interface ddeHTMLProgressElement extends HTMLProgressElement {
append: ddeAppend<ddeHTMLProgressElement>;
}
export interface ddeHTMLScriptElement extends HTMLScriptElement {
append: ddeAppend<ddeHTMLScriptElement>;
}
export interface ddeHTMLSelectElement extends HTMLSelectElement {
append: ddeAppend<ddeHTMLSelectElement>;
}
export interface ddeHTMLSlotElement extends HTMLSlotElement {
append: ddeAppend<ddeHTMLSlotElement>;
}
export interface ddeHTMLSourceElement extends HTMLSourceElement {
append: ddeAppend<ddeHTMLSourceElement>;
}
export interface ddeHTMLSpanElement extends HTMLSpanElement {
append: ddeAppend<ddeHTMLSpanElement>;
}
export interface ddeHTMLStyleElement extends HTMLStyleElement {
append: ddeAppend<ddeHTMLStyleElement>;
}
export interface ddeHTMLTableElement extends HTMLTableElement {
append: ddeAppend<ddeHTMLTableElement>;
}
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
append: ddeAppend<ddeHTMLTableSectionElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
append: ddeAppend<ddeHTMLTemplateElement>;
}
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
append: ddeAppend<ddeHTMLTextAreaElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTimeElement extends HTMLTimeElement {
append: ddeAppend<ddeHTMLTimeElement>;
}
export interface ddeHTMLTitleElement extends HTMLTitleElement {
append: ddeAppend<ddeHTMLTitleElement>;
}
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
append: ddeAppend<ddeHTMLTableRowElement>;
}
export interface ddeHTMLTrackElement extends HTMLTrackElement {
append: ddeAppend<ddeHTMLTrackElement>;
}
export interface ddeHTMLUListElement extends HTMLUListElement {
append: ddeAppend<ddeHTMLUListElement>;
}
export interface ddeHTMLVideoElement extends HTMLVideoElement {
append: ddeAppend<ddeHTMLVideoElement>;
}
export interface ddeSVGAElement extends SVGAElement {
append: ddeAppend<ddeSVGAElement>;
}
export interface ddeSVGAnimateElement extends SVGAnimateElement {
append: ddeAppend<ddeSVGAnimateElement>;
}
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
append: ddeAppend<ddeSVGAnimateMotionElement>;
}
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
append: ddeAppend<ddeSVGAnimateTransformElement>;
}
export interface ddeSVGCircleElement extends SVGCircleElement {
append: ddeAppend<ddeSVGCircleElement>;
}
export interface ddeSVGClipPathElement extends SVGClipPathElement {
append: ddeAppend<ddeSVGClipPathElement>;
}
export interface ddeSVGDefsElement extends SVGDefsElement {
append: ddeAppend<ddeSVGDefsElement>;
}
export interface ddeSVGDescElement extends SVGDescElement {
append: ddeAppend<ddeSVGDescElement>;
}
export interface ddeSVGEllipseElement extends SVGEllipseElement {
append: ddeAppend<ddeSVGEllipseElement>;
}
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
append: ddeAppend<ddeSVGFEBlendElement>;
}
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
append: ddeAppend<ddeSVGFEColorMatrixElement>;
}
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
append: ddeAppend<ddeSVGFEComponentTransferElement>;
}
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
append: ddeAppend<ddeSVGFECompositeElement>;
}
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
}
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
}
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
}
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
append: ddeAppend<ddeSVGFEDistantLightElement>;
}
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
append: ddeAppend<ddeSVGFEDropShadowElement>;
}
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
append: ddeAppend<ddeSVGFEFloodElement>;
}
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
append: ddeAppend<ddeSVGFEFuncAElement>;
}
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
append: ddeAppend<ddeSVGFEFuncBElement>;
}
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
append: ddeAppend<ddeSVGFEFuncGElement>;
}
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
append: ddeAppend<ddeSVGFEFuncRElement>;
}
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
}
export interface ddeSVGFEImageElement extends SVGFEImageElement {
append: ddeAppend<ddeSVGFEImageElement>;
}
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
append: ddeAppend<ddeSVGFEMergeElement>;
}
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
append: ddeAppend<ddeSVGFEMergeNodeElement>;
}
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
append: ddeAppend<ddeSVGFEMorphologyElement>;
}
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
append: ddeAppend<ddeSVGFEOffsetElement>;
}
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
append: ddeAppend<ddeSVGFEPointLightElement>;
}
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
append: ddeAppend<ddeSVGFESpecularLightingElement>;
}
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
append: ddeAppend<ddeSVGFESpotLightElement>;
}
export interface ddeSVGFETileElement extends SVGFETileElement {
append: ddeAppend<ddeSVGFETileElement>;
}
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
append: ddeAppend<ddeSVGFETurbulenceElement>;
}
export interface ddeSVGFilterElement extends SVGFilterElement {
append: ddeAppend<ddeSVGFilterElement>;
}
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
append: ddeAppend<ddeSVGForeignObjectElement>;
}
export interface ddeSVGGElement extends SVGGElement {
append: ddeAppend<ddeSVGGElement>;
}
export interface ddeSVGImageElement extends SVGImageElement {
append: ddeAppend<ddeSVGImageElement>;
}
export interface ddeSVGLineElement extends SVGLineElement {
append: ddeAppend<ddeSVGLineElement>;
}
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
append: ddeAppend<ddeSVGLinearGradientElement>;
}
export interface ddeSVGMarkerElement extends SVGMarkerElement {
append: ddeAppend<ddeSVGMarkerElement>;
}
export interface ddeSVGMaskElement extends SVGMaskElement {
append: ddeAppend<ddeSVGMaskElement>;
}
export interface ddeSVGMetadataElement extends SVGMetadataElement {
append: ddeAppend<ddeSVGMetadataElement>;
}
export interface ddeSVGMPathElement extends SVGMPathElement {
append: ddeAppend<ddeSVGMPathElement>;
}
export interface ddeSVGPathElement extends SVGPathElement {
append: ddeAppend<ddeSVGPathElement>;
}
export interface ddeSVGPatternElement extends SVGPatternElement {
append: ddeAppend<ddeSVGPatternElement>;
}
export interface ddeSVGPolygonElement extends SVGPolygonElement {
append: ddeAppend<ddeSVGPolygonElement>;
}
export interface ddeSVGPolylineElement extends SVGPolylineElement {
append: ddeAppend<ddeSVGPolylineElement>;
}
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
append: ddeAppend<ddeSVGRadialGradientElement>;
}
export interface ddeSVGRectElement extends SVGRectElement {
append: ddeAppend<ddeSVGRectElement>;
}
export interface ddeSVGScriptElement extends SVGScriptElement {
append: ddeAppend<ddeSVGScriptElement>;
}
export interface ddeSVGSetElement extends SVGSetElement {
append: ddeAppend<ddeSVGSetElement>;
}
export interface ddeSVGStopElement extends SVGStopElement {
append: ddeAppend<ddeSVGStopElement>;
}
export interface ddeSVGStyleElement extends SVGStyleElement {
append: ddeAppend<ddeSVGStyleElement>;
}
export interface ddeSVGSVGElement extends SVGSVGElement {
append: ddeAppend<ddeSVGSVGElement>;
}
export interface ddeSVGSwitchElement extends SVGSwitchElement {
append: ddeAppend<ddeSVGSwitchElement>;
}
export interface ddeSVGSymbolElement extends SVGSymbolElement {
append: ddeAppend<ddeSVGSymbolElement>;
}
export interface ddeSVGTextElement extends SVGTextElement {
append: ddeAppend<ddeSVGTextElement>;
}
export interface ddeSVGTextPathElement extends SVGTextPathElement {
append: ddeAppend<ddeSVGTextPathElement>;
}
export interface ddeSVGTitleElement extends SVGTitleElement {
append: ddeAppend<ddeSVGTitleElement>;
}
export interface ddeSVGTSpanElement extends SVGTSpanElement {
append: ddeAppend<ddeSVGTSpanElement>;
}
export interface ddeSVGUseElement extends SVGUseElement {
append: ddeAppend<ddeSVGUseElement>;
}
export interface ddeSVGViewElement extends SVGViewElement {
append: ddeAppend<ddeSVGViewElement>;
}
export {
dispatchEvent$1 as dispatchEvent,
el as createElement,
elNS as createElementNS,
};
export as namespace iife;
export {};

File diff suppressed because it is too large Load Diff

View File

@ -1,862 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V, force?: boolean): V;
toJSON(): V;
valueOf(): V;
}
export type Action<V> = (this: {
value: V;
stopPropagation(): void;
}, ...a: any[]) => typeof signal._ | void;
//type SymbolSignal= Symbol;
export type SymbolOnclear = symbol;
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
first_time?: boolean;
};
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
export interface signal {
_: Symbol;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
};
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
export const signal: signal;
export const S: signal;
declare global {
type ddeSignal<T, A = {}> = Signal<T, A>;
type ddeAction<V> = Action<V>;
type ddeActions<V> = Actions<V>;
}
export type CustomElementTagNameMap = {
"#text": Text;
"#comment": Comment;
};
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
declare global {
type ddeComponentAttributes = Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
type ddeString = string | Signal<string, {}>;
type ddeStringable = ddeString | number | Signal<number, {}>;
}
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
export type PascalCase = `${Capitalize<string>}${string}`;
export type AttrsModified = {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
}>;
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>;
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>;
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
export type IsReadonly<T, K extends keyof T> = T extends {
readonly [P in K]: T[K];
} ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
export type ElementAttributes<T extends SupportedElement> = Partial<{
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(attrs: {
type: "component" | "reactive" | "later";
name?: string;
host?: "this" | "parentElement";
}, is_open?: boolean): Comment;
}
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<A extends {
textContent: ddeStringable;
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
export function el(tag_name?: "<>"): ddeDocumentFragment;
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
export interface On {
/** Listens to the DOM event. See {@link Document.addEventListener} */
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
target: EL;
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/**
* Fires after the next tick of the Javascript event loop.
* This is handy for example to apply some property depending on the element content:
* ```js
* const selected= "Z";
* //...
* return el("form").append(
* el("select", null, on.defer(e=> e.value=selected)).append(
* el("option", { value: "A", textContent: "A" }),
* //...
* el("option", { value: "Z", textContent: "Z" }),
* ),
* );
* ```
* */
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
}
export const on: On;
export type Scope = {
scope: Node | Function | Object;
host: Host<SupportedElement>;
custom_element: false | HTMLElement;
prevent: boolean;
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope;
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T;
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: Host<SupportedElement>;
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
signal: AbortSignal;
state: Scope[];
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>;
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>;
};
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/**
* Memoization utility for caching DOM elements to improve performance.
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
*
* @param key - Unique identifier for the element (usually an ID or unique value)
* @param generator - Function that creates the element
* @returns The cached element if the key exists, otherwise the result of the generator function
*
* @example
* ```ts
* // Within S.el for list rendering
* S.el(itemsSignal, (items, memo) =>
* el("ul").append(
* ...items.map(item =>
* memo(item.id, () => el(ItemComponent, item))
* )
* )
* )
* ```
*/
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
/**
* Memo namespace containing utility functions for memoization.
*/
export namespace memo {
/**
* Checks if an object is a memo scope.
* @param obj - The object to check
* @returns True if the object is a memo scope
*/
export function isScope(obj: any): boolean;
/**
* Creates a memoized function with optional cleanup support.
*
* @param fun - The function to memoize
* @param options - Configuration options
* @param options.signal - AbortSignal for cleanup
* @param options.onlyLast - When true, only keeps the cache from the most recent call
* @returns A memoized version of the function with a .clear() method
*
* @example
* ```ts
* const renderItems = memo.scope(function(items) {
* return items.map(item =>
* memo(item.id, () => el("div", item.name))
* );
* }, {
* signal: controller.signal,
* onlyLast: true
* });
* ```
*/
export function scope<F extends Function>(fun: F, options?: {
signal?: AbortSignal;
onlyLast?: boolean;
}): F & {
clear: () => void;
};
}
/* TypeScript MEH */
declare global {
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
interface ddeDocumentFragment extends DocumentFragment {
append: ddeAppend<ddeDocumentFragment>;
}
interface ddeHTMLElement extends HTMLElement {
append: ddeAppend<ddeHTMLElement>;
}
interface ddeSVGElement extends SVGElement {
append: ddeAppend<ddeSVGElement>;
}
interface ddeMathMLElement extends MathMLElement {
append: ddeAppend<ddeMathMLElement>;
}
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
append: ddeAppend<ddeHTMLAnchorElement>;
}
export interface ddeHTMLAreaElement extends HTMLAreaElement {
append: ddeAppend<ddeHTMLAreaElement>;
}
export interface ddeHTMLAudioElement extends HTMLAudioElement {
append: ddeAppend<ddeHTMLAudioElement>;
}
export interface ddeHTMLBaseElement extends HTMLBaseElement {
append: ddeAppend<ddeHTMLBaseElement>;
}
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
append: ddeAppend<ddeHTMLQuoteElement>;
}
export interface ddeHTMLBodyElement extends HTMLBodyElement {
append: ddeAppend<ddeHTMLBodyElement>;
}
export interface ddeHTMLBRElement extends HTMLBRElement {
append: ddeAppend<ddeHTMLBRElement>;
}
export interface ddeHTMLButtonElement extends HTMLButtonElement {
append: ddeAppend<ddeHTMLButtonElement>;
}
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
append: ddeAppend<ddeHTMLCanvasElement>;
}
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
append: ddeAppend<ddeHTMLTableCaptionElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLDataElement extends HTMLDataElement {
append: ddeAppend<ddeHTMLDataElement>;
}
export interface ddeHTMLDataListElement extends HTMLDataListElement {
append: ddeAppend<ddeHTMLDataListElement>;
}
export interface ddeHTMLModElement extends HTMLModElement {
append: ddeAppend<ddeHTMLModElement>;
}
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
append: ddeAppend<ddeHTMLDetailsElement>;
}
export interface ddeHTMLDialogElement extends HTMLDialogElement {
append: ddeAppend<ddeHTMLDialogElement>;
}
export interface ddeHTMLDivElement extends HTMLDivElement {
append: ddeAppend<ddeHTMLDivElement>;
}
export interface ddeHTMLDListElement extends HTMLDListElement {
append: ddeAppend<ddeHTMLDListElement>;
}
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
append: ddeAppend<ddeHTMLEmbedElement>;
}
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
append: ddeAppend<ddeHTMLFieldSetElement>;
}
export interface ddeHTMLFormElement extends HTMLFormElement {
append: ddeAppend<ddeHTMLFormElement>;
}
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
append: ddeAppend<ddeHTMLHeadingElement>;
}
export interface ddeHTMLHeadElement extends HTMLHeadElement {
append: ddeAppend<ddeHTMLHeadElement>;
}
export interface ddeHTMLHRElement extends HTMLHRElement {
append: ddeAppend<ddeHTMLHRElement>;
}
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
append: ddeAppend<ddeHTMLHtmlElement>;
}
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
append: ddeAppend<ddeHTMLIFrameElement>;
}
export interface ddeHTMLImageElement extends HTMLImageElement {
append: ddeAppend<ddeHTMLImageElement>;
}
export interface ddeHTMLInputElement extends HTMLInputElement {
append: ddeAppend<ddeHTMLInputElement>;
}
export interface ddeHTMLLabelElement extends HTMLLabelElement {
append: ddeAppend<ddeHTMLLabelElement>;
}
export interface ddeHTMLLegendElement extends HTMLLegendElement {
append: ddeAppend<ddeHTMLLegendElement>;
}
export interface ddeHTMLLIElement extends HTMLLIElement {
append: ddeAppend<ddeHTMLLIElement>;
}
export interface ddeHTMLLinkElement extends HTMLLinkElement {
append: ddeAppend<ddeHTMLLinkElement>;
}
export interface ddeHTMLMapElement extends HTMLMapElement {
append: ddeAppend<ddeHTMLMapElement>;
}
export interface ddeHTMLMenuElement extends HTMLMenuElement {
append: ddeAppend<ddeHTMLMenuElement>;
}
export interface ddeHTMLMetaElement extends HTMLMetaElement {
append: ddeAppend<ddeHTMLMetaElement>;
}
export interface ddeHTMLMeterElement extends HTMLMeterElement {
append: ddeAppend<ddeHTMLMeterElement>;
}
export interface ddeHTMLObjectElement extends HTMLObjectElement {
append: ddeAppend<ddeHTMLObjectElement>;
}
export interface ddeHTMLOListElement extends HTMLOListElement {
append: ddeAppend<ddeHTMLOListElement>;
}
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
append: ddeAppend<ddeHTMLOptGroupElement>;
}
export interface ddeHTMLOptionElement extends HTMLOptionElement {
append: ddeAppend<ddeHTMLOptionElement>;
}
export interface ddeHTMLOutputElement extends HTMLOutputElement {
append: ddeAppend<ddeHTMLOutputElement>;
}
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
append: ddeAppend<ddeHTMLParagraphElement>;
}
export interface ddeHTMLPictureElement extends HTMLPictureElement {
append: ddeAppend<ddeHTMLPictureElement>;
}
export interface ddeHTMLPreElement extends HTMLPreElement {
append: ddeAppend<ddeHTMLPreElement>;
}
export interface ddeHTMLProgressElement extends HTMLProgressElement {
append: ddeAppend<ddeHTMLProgressElement>;
}
export interface ddeHTMLScriptElement extends HTMLScriptElement {
append: ddeAppend<ddeHTMLScriptElement>;
}
export interface ddeHTMLSelectElement extends HTMLSelectElement {
append: ddeAppend<ddeHTMLSelectElement>;
}
export interface ddeHTMLSlotElement extends HTMLSlotElement {
append: ddeAppend<ddeHTMLSlotElement>;
}
export interface ddeHTMLSourceElement extends HTMLSourceElement {
append: ddeAppend<ddeHTMLSourceElement>;
}
export interface ddeHTMLSpanElement extends HTMLSpanElement {
append: ddeAppend<ddeHTMLSpanElement>;
}
export interface ddeHTMLStyleElement extends HTMLStyleElement {
append: ddeAppend<ddeHTMLStyleElement>;
}
export interface ddeHTMLTableElement extends HTMLTableElement {
append: ddeAppend<ddeHTMLTableElement>;
}
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
append: ddeAppend<ddeHTMLTableSectionElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
append: ddeAppend<ddeHTMLTemplateElement>;
}
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
append: ddeAppend<ddeHTMLTextAreaElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTimeElement extends HTMLTimeElement {
append: ddeAppend<ddeHTMLTimeElement>;
}
export interface ddeHTMLTitleElement extends HTMLTitleElement {
append: ddeAppend<ddeHTMLTitleElement>;
}
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
append: ddeAppend<ddeHTMLTableRowElement>;
}
export interface ddeHTMLTrackElement extends HTMLTrackElement {
append: ddeAppend<ddeHTMLTrackElement>;
}
export interface ddeHTMLUListElement extends HTMLUListElement {
append: ddeAppend<ddeHTMLUListElement>;
}
export interface ddeHTMLVideoElement extends HTMLVideoElement {
append: ddeAppend<ddeHTMLVideoElement>;
}
export interface ddeSVGAElement extends SVGAElement {
append: ddeAppend<ddeSVGAElement>;
}
export interface ddeSVGAnimateElement extends SVGAnimateElement {
append: ddeAppend<ddeSVGAnimateElement>;
}
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
append: ddeAppend<ddeSVGAnimateMotionElement>;
}
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
append: ddeAppend<ddeSVGAnimateTransformElement>;
}
export interface ddeSVGCircleElement extends SVGCircleElement {
append: ddeAppend<ddeSVGCircleElement>;
}
export interface ddeSVGClipPathElement extends SVGClipPathElement {
append: ddeAppend<ddeSVGClipPathElement>;
}
export interface ddeSVGDefsElement extends SVGDefsElement {
append: ddeAppend<ddeSVGDefsElement>;
}
export interface ddeSVGDescElement extends SVGDescElement {
append: ddeAppend<ddeSVGDescElement>;
}
export interface ddeSVGEllipseElement extends SVGEllipseElement {
append: ddeAppend<ddeSVGEllipseElement>;
}
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
append: ddeAppend<ddeSVGFEBlendElement>;
}
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
append: ddeAppend<ddeSVGFEColorMatrixElement>;
}
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
append: ddeAppend<ddeSVGFEComponentTransferElement>;
}
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
append: ddeAppend<ddeSVGFECompositeElement>;
}
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
}
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
}
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
}
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
append: ddeAppend<ddeSVGFEDistantLightElement>;
}
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
append: ddeAppend<ddeSVGFEDropShadowElement>;
}
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
append: ddeAppend<ddeSVGFEFloodElement>;
}
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
append: ddeAppend<ddeSVGFEFuncAElement>;
}
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
append: ddeAppend<ddeSVGFEFuncBElement>;
}
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
append: ddeAppend<ddeSVGFEFuncGElement>;
}
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
append: ddeAppend<ddeSVGFEFuncRElement>;
}
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
}
export interface ddeSVGFEImageElement extends SVGFEImageElement {
append: ddeAppend<ddeSVGFEImageElement>;
}
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
append: ddeAppend<ddeSVGFEMergeElement>;
}
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
append: ddeAppend<ddeSVGFEMergeNodeElement>;
}
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
append: ddeAppend<ddeSVGFEMorphologyElement>;
}
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
append: ddeAppend<ddeSVGFEOffsetElement>;
}
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
append: ddeAppend<ddeSVGFEPointLightElement>;
}
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
append: ddeAppend<ddeSVGFESpecularLightingElement>;
}
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
append: ddeAppend<ddeSVGFESpotLightElement>;
}
export interface ddeSVGFETileElement extends SVGFETileElement {
append: ddeAppend<ddeSVGFETileElement>;
}
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
append: ddeAppend<ddeSVGFETurbulenceElement>;
}
export interface ddeSVGFilterElement extends SVGFilterElement {
append: ddeAppend<ddeSVGFilterElement>;
}
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
append: ddeAppend<ddeSVGForeignObjectElement>;
}
export interface ddeSVGGElement extends SVGGElement {
append: ddeAppend<ddeSVGGElement>;
}
export interface ddeSVGImageElement extends SVGImageElement {
append: ddeAppend<ddeSVGImageElement>;
}
export interface ddeSVGLineElement extends SVGLineElement {
append: ddeAppend<ddeSVGLineElement>;
}
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
append: ddeAppend<ddeSVGLinearGradientElement>;
}
export interface ddeSVGMarkerElement extends SVGMarkerElement {
append: ddeAppend<ddeSVGMarkerElement>;
}
export interface ddeSVGMaskElement extends SVGMaskElement {
append: ddeAppend<ddeSVGMaskElement>;
}
export interface ddeSVGMetadataElement extends SVGMetadataElement {
append: ddeAppend<ddeSVGMetadataElement>;
}
export interface ddeSVGMPathElement extends SVGMPathElement {
append: ddeAppend<ddeSVGMPathElement>;
}
export interface ddeSVGPathElement extends SVGPathElement {
append: ddeAppend<ddeSVGPathElement>;
}
export interface ddeSVGPatternElement extends SVGPatternElement {
append: ddeAppend<ddeSVGPatternElement>;
}
export interface ddeSVGPolygonElement extends SVGPolygonElement {
append: ddeAppend<ddeSVGPolygonElement>;
}
export interface ddeSVGPolylineElement extends SVGPolylineElement {
append: ddeAppend<ddeSVGPolylineElement>;
}
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
append: ddeAppend<ddeSVGRadialGradientElement>;
}
export interface ddeSVGRectElement extends SVGRectElement {
append: ddeAppend<ddeSVGRectElement>;
}
export interface ddeSVGScriptElement extends SVGScriptElement {
append: ddeAppend<ddeSVGScriptElement>;
}
export interface ddeSVGSetElement extends SVGSetElement {
append: ddeAppend<ddeSVGSetElement>;
}
export interface ddeSVGStopElement extends SVGStopElement {
append: ddeAppend<ddeSVGStopElement>;
}
export interface ddeSVGStyleElement extends SVGStyleElement {
append: ddeAppend<ddeSVGStyleElement>;
}
export interface ddeSVGSVGElement extends SVGSVGElement {
append: ddeAppend<ddeSVGSVGElement>;
}
export interface ddeSVGSwitchElement extends SVGSwitchElement {
append: ddeAppend<ddeSVGSwitchElement>;
}
export interface ddeSVGSymbolElement extends SVGSymbolElement {
append: ddeAppend<ddeSVGSymbolElement>;
}
export interface ddeSVGTextElement extends SVGTextElement {
append: ddeAppend<ddeSVGTextElement>;
}
export interface ddeSVGTextPathElement extends SVGTextPathElement {
append: ddeAppend<ddeSVGTextPathElement>;
}
export interface ddeSVGTitleElement extends SVGTitleElement {
append: ddeAppend<ddeSVGTitleElement>;
}
export interface ddeSVGTSpanElement extends SVGTSpanElement {
append: ddeAppend<ddeSVGTSpanElement>;
}
export interface ddeSVGUseElement extends SVGUseElement {
append: ddeAppend<ddeSVGUseElement>;
}
export interface ddeSVGViewElement extends SVGViewElement {
append: ddeAppend<ddeSVGViewElement>;
}
export {
dispatchEvent$1 as dispatchEvent,
el as createElement,
elNS as createElementNS,
};
export as namespace iife;
export {};

File diff suppressed because one or more lines are too long

861
dist/iife.d.ts vendored
View File

@ -1,861 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V, force?: boolean): V;
toJSON(): V;
valueOf(): V;
}
export type Action<V> = (this: {
value: V;
stopPropagation(): void;
}, ...a: any[]) => typeof signal._ | void;
//type SymbolSignal= Symbol;
export type SymbolOnclear = symbol;
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
first_time?: boolean;
};
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
export interface signal {
_: Symbol;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
};
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
declare const signal: signal;
declare global {
type ddeSignal<T, A = {}> = Signal<T, A>;
type ddeAction<V> = Action<V>;
type ddeActions<V> = Actions<V>;
}
export type CustomElementTagNameMap = {
"#text": Text;
"#comment": Comment;
};
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
declare global {
type ddeComponentAttributes = Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
type ddeString = string | Signal<string, {}>;
type ddeStringable = ddeString | number | Signal<number, {}>;
}
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
export type PascalCase = `${Capitalize<string>}${string}`;
export type AttrsModified = {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
}>;
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>;
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>;
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
export type IsReadonly<T, K extends keyof T> = T extends {
readonly [P in K]: T[K];
} ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
export type ElementAttributes<T extends SupportedElement> = Partial<{
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(attrs: {
type: "component" | "reactive" | "later";
name?: string;
host?: "this" | "parentElement";
}, is_open?: boolean): Comment;
}
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<A extends {
textContent: ddeStringable;
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
export function el(tag_name?: "<>"): ddeDocumentFragment;
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
export interface On {
/** Listens to the DOM event. See {@link Document.addEventListener} */
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
target: EL;
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/**
* Fires after the next tick of the Javascript event loop.
* This is handy for example to apply some property depending on the element content:
* ```js
* const selected= "Z";
* //...
* return el("form").append(
* el("select", null, on.defer(e=> e.value=selected)).append(
* el("option", { value: "A", textContent: "A" }),
* //...
* el("option", { value: "Z", textContent: "Z" }),
* ),
* );
* ```
* */
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
}
export const on: On;
export type Scope = {
scope: Node | Function | Object;
host: Host<SupportedElement>;
custom_element: false | HTMLElement;
prevent: boolean;
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope;
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T;
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: Host<SupportedElement>;
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
signal: AbortSignal;
state: Scope[];
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>;
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>;
};
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/**
* Memoization utility for caching DOM elements to improve performance.
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
*
* @param key - Unique identifier for the element (usually an ID or unique value)
* @param generator - Function that creates the element
* @returns The cached element if the key exists, otherwise the result of the generator function
*
* @example
* ```ts
* // Within S.el for list rendering
* S.el(itemsSignal, (items, memo) =>
* el("ul").append(
* ...items.map(item =>
* memo(item.id, () => el(ItemComponent, item))
* )
* )
* )
* ```
*/
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
/**
* Memo namespace containing utility functions for memoization.
*/
export namespace memo {
/**
* Checks if an object is a memo scope.
* @param obj - The object to check
* @returns True if the object is a memo scope
*/
export function isScope(obj: any): boolean;
/**
* Creates a memoized function with optional cleanup support.
*
* @param fun - The function to memoize
* @param options - Configuration options
* @param options.signal - AbortSignal for cleanup
* @param options.onlyLast - When true, only keeps the cache from the most recent call
* @returns A memoized version of the function with a .clear() method
*
* @example
* ```ts
* const renderItems = memo.scope(function(items) {
* return items.map(item =>
* memo(item.id, () => el("div", item.name))
* );
* }, {
* signal: controller.signal,
* onlyLast: true
* });
* ```
*/
export function scope<F extends Function>(fun: F, options?: {
signal?: AbortSignal;
onlyLast?: boolean;
}): F & {
clear: () => void;
};
}
/* TypeScript MEH */
declare global {
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
interface ddeDocumentFragment extends DocumentFragment {
append: ddeAppend<ddeDocumentFragment>;
}
interface ddeHTMLElement extends HTMLElement {
append: ddeAppend<ddeHTMLElement>;
}
interface ddeSVGElement extends SVGElement {
append: ddeAppend<ddeSVGElement>;
}
interface ddeMathMLElement extends MathMLElement {
append: ddeAppend<ddeMathMLElement>;
}
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
append: ddeAppend<ddeHTMLAnchorElement>;
}
export interface ddeHTMLAreaElement extends HTMLAreaElement {
append: ddeAppend<ddeHTMLAreaElement>;
}
export interface ddeHTMLAudioElement extends HTMLAudioElement {
append: ddeAppend<ddeHTMLAudioElement>;
}
export interface ddeHTMLBaseElement extends HTMLBaseElement {
append: ddeAppend<ddeHTMLBaseElement>;
}
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
append: ddeAppend<ddeHTMLQuoteElement>;
}
export interface ddeHTMLBodyElement extends HTMLBodyElement {
append: ddeAppend<ddeHTMLBodyElement>;
}
export interface ddeHTMLBRElement extends HTMLBRElement {
append: ddeAppend<ddeHTMLBRElement>;
}
export interface ddeHTMLButtonElement extends HTMLButtonElement {
append: ddeAppend<ddeHTMLButtonElement>;
}
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
append: ddeAppend<ddeHTMLCanvasElement>;
}
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
append: ddeAppend<ddeHTMLTableCaptionElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLDataElement extends HTMLDataElement {
append: ddeAppend<ddeHTMLDataElement>;
}
export interface ddeHTMLDataListElement extends HTMLDataListElement {
append: ddeAppend<ddeHTMLDataListElement>;
}
export interface ddeHTMLModElement extends HTMLModElement {
append: ddeAppend<ddeHTMLModElement>;
}
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
append: ddeAppend<ddeHTMLDetailsElement>;
}
export interface ddeHTMLDialogElement extends HTMLDialogElement {
append: ddeAppend<ddeHTMLDialogElement>;
}
export interface ddeHTMLDivElement extends HTMLDivElement {
append: ddeAppend<ddeHTMLDivElement>;
}
export interface ddeHTMLDListElement extends HTMLDListElement {
append: ddeAppend<ddeHTMLDListElement>;
}
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
append: ddeAppend<ddeHTMLEmbedElement>;
}
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
append: ddeAppend<ddeHTMLFieldSetElement>;
}
export interface ddeHTMLFormElement extends HTMLFormElement {
append: ddeAppend<ddeHTMLFormElement>;
}
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
append: ddeAppend<ddeHTMLHeadingElement>;
}
export interface ddeHTMLHeadElement extends HTMLHeadElement {
append: ddeAppend<ddeHTMLHeadElement>;
}
export interface ddeHTMLHRElement extends HTMLHRElement {
append: ddeAppend<ddeHTMLHRElement>;
}
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
append: ddeAppend<ddeHTMLHtmlElement>;
}
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
append: ddeAppend<ddeHTMLIFrameElement>;
}
export interface ddeHTMLImageElement extends HTMLImageElement {
append: ddeAppend<ddeHTMLImageElement>;
}
export interface ddeHTMLInputElement extends HTMLInputElement {
append: ddeAppend<ddeHTMLInputElement>;
}
export interface ddeHTMLLabelElement extends HTMLLabelElement {
append: ddeAppend<ddeHTMLLabelElement>;
}
export interface ddeHTMLLegendElement extends HTMLLegendElement {
append: ddeAppend<ddeHTMLLegendElement>;
}
export interface ddeHTMLLIElement extends HTMLLIElement {
append: ddeAppend<ddeHTMLLIElement>;
}
export interface ddeHTMLLinkElement extends HTMLLinkElement {
append: ddeAppend<ddeHTMLLinkElement>;
}
export interface ddeHTMLMapElement extends HTMLMapElement {
append: ddeAppend<ddeHTMLMapElement>;
}
export interface ddeHTMLMenuElement extends HTMLMenuElement {
append: ddeAppend<ddeHTMLMenuElement>;
}
export interface ddeHTMLMetaElement extends HTMLMetaElement {
append: ddeAppend<ddeHTMLMetaElement>;
}
export interface ddeHTMLMeterElement extends HTMLMeterElement {
append: ddeAppend<ddeHTMLMeterElement>;
}
export interface ddeHTMLObjectElement extends HTMLObjectElement {
append: ddeAppend<ddeHTMLObjectElement>;
}
export interface ddeHTMLOListElement extends HTMLOListElement {
append: ddeAppend<ddeHTMLOListElement>;
}
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
append: ddeAppend<ddeHTMLOptGroupElement>;
}
export interface ddeHTMLOptionElement extends HTMLOptionElement {
append: ddeAppend<ddeHTMLOptionElement>;
}
export interface ddeHTMLOutputElement extends HTMLOutputElement {
append: ddeAppend<ddeHTMLOutputElement>;
}
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
append: ddeAppend<ddeHTMLParagraphElement>;
}
export interface ddeHTMLPictureElement extends HTMLPictureElement {
append: ddeAppend<ddeHTMLPictureElement>;
}
export interface ddeHTMLPreElement extends HTMLPreElement {
append: ddeAppend<ddeHTMLPreElement>;
}
export interface ddeHTMLProgressElement extends HTMLProgressElement {
append: ddeAppend<ddeHTMLProgressElement>;
}
export interface ddeHTMLScriptElement extends HTMLScriptElement {
append: ddeAppend<ddeHTMLScriptElement>;
}
export interface ddeHTMLSelectElement extends HTMLSelectElement {
append: ddeAppend<ddeHTMLSelectElement>;
}
export interface ddeHTMLSlotElement extends HTMLSlotElement {
append: ddeAppend<ddeHTMLSlotElement>;
}
export interface ddeHTMLSourceElement extends HTMLSourceElement {
append: ddeAppend<ddeHTMLSourceElement>;
}
export interface ddeHTMLSpanElement extends HTMLSpanElement {
append: ddeAppend<ddeHTMLSpanElement>;
}
export interface ddeHTMLStyleElement extends HTMLStyleElement {
append: ddeAppend<ddeHTMLStyleElement>;
}
export interface ddeHTMLTableElement extends HTMLTableElement {
append: ddeAppend<ddeHTMLTableElement>;
}
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
append: ddeAppend<ddeHTMLTableSectionElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
append: ddeAppend<ddeHTMLTemplateElement>;
}
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
append: ddeAppend<ddeHTMLTextAreaElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTimeElement extends HTMLTimeElement {
append: ddeAppend<ddeHTMLTimeElement>;
}
export interface ddeHTMLTitleElement extends HTMLTitleElement {
append: ddeAppend<ddeHTMLTitleElement>;
}
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
append: ddeAppend<ddeHTMLTableRowElement>;
}
export interface ddeHTMLTrackElement extends HTMLTrackElement {
append: ddeAppend<ddeHTMLTrackElement>;
}
export interface ddeHTMLUListElement extends HTMLUListElement {
append: ddeAppend<ddeHTMLUListElement>;
}
export interface ddeHTMLVideoElement extends HTMLVideoElement {
append: ddeAppend<ddeHTMLVideoElement>;
}
export interface ddeSVGAElement extends SVGAElement {
append: ddeAppend<ddeSVGAElement>;
}
export interface ddeSVGAnimateElement extends SVGAnimateElement {
append: ddeAppend<ddeSVGAnimateElement>;
}
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
append: ddeAppend<ddeSVGAnimateMotionElement>;
}
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
append: ddeAppend<ddeSVGAnimateTransformElement>;
}
export interface ddeSVGCircleElement extends SVGCircleElement {
append: ddeAppend<ddeSVGCircleElement>;
}
export interface ddeSVGClipPathElement extends SVGClipPathElement {
append: ddeAppend<ddeSVGClipPathElement>;
}
export interface ddeSVGDefsElement extends SVGDefsElement {
append: ddeAppend<ddeSVGDefsElement>;
}
export interface ddeSVGDescElement extends SVGDescElement {
append: ddeAppend<ddeSVGDescElement>;
}
export interface ddeSVGEllipseElement extends SVGEllipseElement {
append: ddeAppend<ddeSVGEllipseElement>;
}
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
append: ddeAppend<ddeSVGFEBlendElement>;
}
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
append: ddeAppend<ddeSVGFEColorMatrixElement>;
}
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
append: ddeAppend<ddeSVGFEComponentTransferElement>;
}
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
append: ddeAppend<ddeSVGFECompositeElement>;
}
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
}
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
}
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
}
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
append: ddeAppend<ddeSVGFEDistantLightElement>;
}
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
append: ddeAppend<ddeSVGFEDropShadowElement>;
}
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
append: ddeAppend<ddeSVGFEFloodElement>;
}
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
append: ddeAppend<ddeSVGFEFuncAElement>;
}
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
append: ddeAppend<ddeSVGFEFuncBElement>;
}
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
append: ddeAppend<ddeSVGFEFuncGElement>;
}
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
append: ddeAppend<ddeSVGFEFuncRElement>;
}
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
}
export interface ddeSVGFEImageElement extends SVGFEImageElement {
append: ddeAppend<ddeSVGFEImageElement>;
}
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
append: ddeAppend<ddeSVGFEMergeElement>;
}
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
append: ddeAppend<ddeSVGFEMergeNodeElement>;
}
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
append: ddeAppend<ddeSVGFEMorphologyElement>;
}
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
append: ddeAppend<ddeSVGFEOffsetElement>;
}
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
append: ddeAppend<ddeSVGFEPointLightElement>;
}
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
append: ddeAppend<ddeSVGFESpecularLightingElement>;
}
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
append: ddeAppend<ddeSVGFESpotLightElement>;
}
export interface ddeSVGFETileElement extends SVGFETileElement {
append: ddeAppend<ddeSVGFETileElement>;
}
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
append: ddeAppend<ddeSVGFETurbulenceElement>;
}
export interface ddeSVGFilterElement extends SVGFilterElement {
append: ddeAppend<ddeSVGFilterElement>;
}
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
append: ddeAppend<ddeSVGForeignObjectElement>;
}
export interface ddeSVGGElement extends SVGGElement {
append: ddeAppend<ddeSVGGElement>;
}
export interface ddeSVGImageElement extends SVGImageElement {
append: ddeAppend<ddeSVGImageElement>;
}
export interface ddeSVGLineElement extends SVGLineElement {
append: ddeAppend<ddeSVGLineElement>;
}
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
append: ddeAppend<ddeSVGLinearGradientElement>;
}
export interface ddeSVGMarkerElement extends SVGMarkerElement {
append: ddeAppend<ddeSVGMarkerElement>;
}
export interface ddeSVGMaskElement extends SVGMaskElement {
append: ddeAppend<ddeSVGMaskElement>;
}
export interface ddeSVGMetadataElement extends SVGMetadataElement {
append: ddeAppend<ddeSVGMetadataElement>;
}
export interface ddeSVGMPathElement extends SVGMPathElement {
append: ddeAppend<ddeSVGMPathElement>;
}
export interface ddeSVGPathElement extends SVGPathElement {
append: ddeAppend<ddeSVGPathElement>;
}
export interface ddeSVGPatternElement extends SVGPatternElement {
append: ddeAppend<ddeSVGPatternElement>;
}
export interface ddeSVGPolygonElement extends SVGPolygonElement {
append: ddeAppend<ddeSVGPolygonElement>;
}
export interface ddeSVGPolylineElement extends SVGPolylineElement {
append: ddeAppend<ddeSVGPolylineElement>;
}
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
append: ddeAppend<ddeSVGRadialGradientElement>;
}
export interface ddeSVGRectElement extends SVGRectElement {
append: ddeAppend<ddeSVGRectElement>;
}
export interface ddeSVGScriptElement extends SVGScriptElement {
append: ddeAppend<ddeSVGScriptElement>;
}
export interface ddeSVGSetElement extends SVGSetElement {
append: ddeAppend<ddeSVGSetElement>;
}
export interface ddeSVGStopElement extends SVGStopElement {
append: ddeAppend<ddeSVGStopElement>;
}
export interface ddeSVGStyleElement extends SVGStyleElement {
append: ddeAppend<ddeSVGStyleElement>;
}
export interface ddeSVGSVGElement extends SVGSVGElement {
append: ddeAppend<ddeSVGSVGElement>;
}
export interface ddeSVGSwitchElement extends SVGSwitchElement {
append: ddeAppend<ddeSVGSwitchElement>;
}
export interface ddeSVGSymbolElement extends SVGSymbolElement {
append: ddeAppend<ddeSVGSymbolElement>;
}
export interface ddeSVGTextElement extends SVGTextElement {
append: ddeAppend<ddeSVGTextElement>;
}
export interface ddeSVGTextPathElement extends SVGTextPathElement {
append: ddeAppend<ddeSVGTextPathElement>;
}
export interface ddeSVGTitleElement extends SVGTitleElement {
append: ddeAppend<ddeSVGTitleElement>;
}
export interface ddeSVGTSpanElement extends SVGTSpanElement {
append: ddeAppend<ddeSVGTSpanElement>;
}
export interface ddeSVGUseElement extends SVGUseElement {
append: ddeAppend<ddeSVGUseElement>;
}
export interface ddeSVGViewElement extends SVGViewElement {
append: ddeAppend<ddeSVGViewElement>;
}
export {
dispatchEvent$1 as dispatchEvent,
el as createElement,
elNS as createElementNS,
};
export as namespace iife;
export {};

702
dist/iife.js vendored
View File

@ -1,702 +0,0 @@
var DDE = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.js
var index_exports = {};
__export(index_exports, {
assign: () => assign,
assignAttribute: () => assignAttribute,
chainableAppend: () => chainableAppend,
classListDeclarative: () => classListDeclarative,
createElement: () => createElement,
createElementNS: () => createElementNS,
customElementRender: () => customElementRender,
customElementWithDDE: () => lifecyclesToEvents,
dispatchEvent: () => dispatchEvent,
el: () => createElement,
elNS: () => createElementNS,
lifecyclesToEvents: () => lifecyclesToEvents,
memo: () => memo,
on: () => on,
queue: () => queue,
registerReactivity: () => registerReactivity,
scope: () => scope,
simulateSlots: () => simulateSlots
});
// src/helpers.js
var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a);
function isUndef(value) {
return typeof value === "undefined";
}
function isInstance(obj, cls) {
return obj instanceof cls;
}
function isProtoFrom(obj, cls) {
return Object.prototype.isPrototypeOf.call(cls, obj);
}
function oCreate(proto = null, p = {}) {
return Object.create(proto, p);
}
function oAssign(...o) {
return Object.assign(...o);
}
function onAbort(signal, listener) {
if (!signal || !isInstance(signal, AbortSignal))
return true;
if (signal.aborted)
return;
signal.addEventListener("abort", listener);
return function cleanUp() {
signal.removeEventListener("abort", listener);
};
}
function requestIdle() {
return new Promise(function(resolve) {
(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
});
}
// src/dom-lib/common.js
var enviroment = {
setDeleteAttr,
ssr: "",
D: globalThis.document,
N: globalThis.Node,
F: globalThis.DocumentFragment,
H: globalThis.HTMLElement,
S: globalThis.SVGElement,
M: globalThis.MutationObserver,
q: (p) => p || Promise.resolve()
};
function setDeleteAttr(obj, prop, val) {
Reflect.set(obj, prop, val);
if (!isUndef(val)) return;
Reflect.deleteProperty(obj, prop);
if (isInstance(obj, enviroment.H) && obj.getAttribute(prop) === "undefined")
return obj.removeAttribute(prop);
if (Reflect.get(obj, prop) === "undefined")
return Reflect.set(obj, prop, "");
}
var keyLTE = "__dde_lifecyclesToEvents";
var evc = "dde:connected";
var evd = "dde:disconnected";
var eva = "dde:attributeChanged";
// src/dom-lib/events-observer.js
var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
get() {
return () => {
};
}
});
function connectionsChangesObserverConstructor() {
const store = /* @__PURE__ */ new Map();
let is_observing = false;
const observerListener = (stop2) => function(mutations) {
for (const mutation of mutations) {
if (mutation.type !== "childList") continue;
if (observerAdded(mutation.addedNodes, true)) {
stop2();
continue;
}
if (observerRemoved(mutation.removedNodes, true))
stop2();
}
};
const observer = new enviroment.M(observerListener(stop));
return {
/**
* Creates an observer for a specific element
* @param {Element} element - Element to observe
* @returns {Function} Cleanup function
*/
observe(element) {
const o = new enviroment.M(observerListener(() => {
}));
o.observe(element, { childList: true, subtree: true });
return () => o.disconnect();
},
/**
* Register a connection listener for an element
* @param {Element} element - Element to watch
* @param {Function} listener - Callback for connection event
*/
onConnected(element, listener) {
start();
const listeners = getElementStore(element);
if (listeners.connected.has(listener)) return;
listeners.connected.add(listener);
listeners.length_c += 1;
},
/**
* Unregister a connection listener
* @param {Element} element - Element being watched
* @param {Function} listener - Callback to remove
*/
offConnected(element, listener) {
if (!store.has(element)) return;
const ls = store.get(element);
if (!ls.connected.has(listener)) return;
ls.connected.delete(listener);
ls.length_c -= 1;
cleanWhenOff(element, ls);
},
/**
* Register a disconnection listener for an element
* @param {Element} element - Element to watch
* @param {Function} listener - Callback for disconnection event
*/
onDisconnected(element, listener) {
start();
const listeners = getElementStore(element);
if (listeners.disconnected.has(listener)) return;
listeners.disconnected.add(listener);
listeners.length_d += 1;
},
/**
* Unregister a disconnection listener
* @param {Element} element - Element being watched
* @param {Function} listener - Callback to remove
*/
offDisconnected(element, listener) {
if (!store.has(element)) return;
const ls = store.get(element);
ls.disconnected.delete(listener);
ls.length_d -= 1;
cleanWhenOff(element, ls);
}
};
function cleanWhenOff(element, ls) {
if (ls.length_c || ls.length_d)
return;
store.delete(element);
stop();
}
function getElementStore(element) {
if (store.has(element)) return store.get(element);
const out = {
connected: /* @__PURE__ */ new WeakSet(),
length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0
};
store.set(element, out);
return out;
}
function start() {
if (is_observing) return;
is_observing = true;
observer.observe(enviroment.D.body, { childList: true, subtree: true });
}
function stop() {
if (!is_observing || store.size) return;
is_observing = false;
observer.disconnect();
}
async function collectChildren(element) {
if (store.size > 30)
await requestIdle();
const out = [];
if (!isInstance(element, enviroment.N)) return out;
for (const el of store.keys()) {
if (el === element || !isInstance(el, enviroment.N)) continue;
if (element.contains(el))
out.push(el);
}
return out;
}
function observerAdded(addedNodes, is_root) {
let out = false;
for (const element of addedNodes) {
if (is_root) collectChildren(element).then(observerAdded);
if (!store.has(element)) continue;
const ls = store.get(element);
if (!ls.length_c) continue;
element.dispatchEvent(new Event(evc));
ls.connected = /* @__PURE__ */ new WeakSet();
ls.length_c = 0;
if (!ls.length_d) store.delete(element);
out = true;
}
return out;
}
function observerRemoved(removedNodes, is_root) {
let out = false;
for (const element of removedNodes) {
if (is_root) collectChildren(element).then(observerRemoved);
if (!store.has(element)) continue;
const ls = store.get(element);
if (!ls.length_d) continue;
(globalThis.queueMicrotask || setTimeout)(dispatchRemove(element));
out = true;
}
return out;
}
function dispatchRemove(element) {
return () => {
if (element.isConnected) return;
element.dispatchEvent(new Event(evd));
store.delete(element);
};
}
}
// src/dom-lib/events.js
function dispatchEvent(name, options, host) {
if (typeof options === "function") {
host = options;
options = null;
}
if (!options) options = {};
return function dispatch(element, ...d) {
if (host) {
d.unshift(element);
element = typeof host === "function" ? host() : host;
}
const event = d.length ? new CustomEvent(name, oAssign({ detail: d[0] }, options)) : new Event(name, options);
return element.dispatchEvent(event);
};
}
function on(event, listener, options) {
return function registerElement(element) {
element.addEventListener(event, listener, options);
return element;
};
}
on.defer = (fn) => setTimeout.bind(null, fn, 0);
var lifeOptions = (obj) => oAssign({}, typeof obj === "object" ? obj : null, { once: true });
on.connected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evc, listener, options);
if (element[keyLTE]) return element;
if (element.isConnected) return element.dispatchEvent(new Event(evc)), element;
const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener));
if (c) c_ch_o.onConnected(element, listener);
return element;
};
};
on.disconnected = function(listener, options) {
options = lifeOptions(options);
return function registerElement(element) {
element.addEventListener(evd, listener, options);
if (element[keyLTE]) return element;
const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener));
if (c) c_ch_o.onDisconnected(element, listener);
return element;
};
};
// src/dom-lib/scopes.js
var scopes = [{
get scope() {
return enviroment.D.body;
},
host: (c) => c ? c(enviroment.D.body) : enviroment.D.body,
prevent: true
}];
var store_abort = /* @__PURE__ */ new WeakMap();
var scope = {
/**
* Gets the current scope
* @returns {typeof scopes[number]} Current scope context
*/
get current() {
return scopes[scopes.length - 1];
},
/**
* Gets the host element of the current scope
* @returns {Function} Host accessor function
*/
get host() {
return this.current.host;
},
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
get signal() {
const { host } = this;
if (store_abort.has(host)) return store_abort.get(host);
const a = new AbortController();
store_abort.set(host, a);
host(on.disconnected(() => a.abort()));
return a.signal;
},
/**
* Prevents default behavior in the current scope
* @returns {Object} Current scope context
*/
preventDefault() {
const { current } = this;
current.prevent = true;
return current;
},
/**
* Gets a copy of the current scope stack
* @returns {Array} Copy of scope stack
*/
get state() {
return [...scopes];
},
/**
* Pushes a new scope to the stack
* @param {Object} [s={}] - Scope object to push
* @returns {number} New length of the scope stack
*/
push(s = {}) {
return scopes.push(oAssign({}, this.current, { prevent: false }, s));
},
/**
* Pushes the root scope to the stack
* @returns {number} New length of the scope stack
*/
pushRoot() {
return scopes.push(scopes[0]);
},
/**
* Pops the current scope from the stack
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
*/
pop() {
if (scopes.length === 1) return;
return scopes.pop();
}
};
// src/signals-lib/common.js
var signals_global = {
/**
* Checks if a value is a signal
* @param {any} attributes - Value to check
* @returns {boolean} Whether the value is a signal
*/
isSignal(attributes) {
return false;
},
/**
* Processes an attribute that might be reactive
* @param {Element} obj - Element that owns the attribute
* @param {string} key - Attribute name
* @param {any} attr - Attribute value
* @param {Function} set - Function to set the attribute
* @returns {any} Processed attribute value
*/
processReactiveAttribute(obj, key, attr, set) {
return attr;
}
};
function registerReactivity(def, global = true) {
if (global) return oAssign(signals_global, def);
Object.setPrototypeOf(def, signals_global);
return def;
}
function signals(_this) {
return isProtoFrom(_this, signals_global) && _this !== signals_global ? _this : signals_global;
}
// src/dom-lib/helpers.js
function setRemove(obj, prop, key, val) {
return obj[(isUndef(val) ? "remove" : "set") + prop](key, val);
}
function setRemoveNS(obj, prop, key, val, ns = null) {
return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, val);
}
function setDelete(obj, key, val) {
Reflect.set(obj, key, val);
if (!isUndef(val)) return;
return Reflect.deleteProperty(obj, key);
}
function elementAttribute(element, op, key, value) {
if (isInstance(element, enviroment.H))
return element[op + "Attribute"](key, value);
return element[op + "AttributeNS"](null, key, value);
}
// src/dom-lib/el.js
function queue(promise) {
return enviroment.q(promise);
}
function append(...els) {
this.appendOriginal(...els);
return this;
}
function chainableAppend(el) {
if (el.append === append) return el;
el.appendOriginal = el.append;
el.append = append;
return el;
}
var namespace;
function createElement(tag, attributes, ...addons) {
const s = signals(this);
let scoped = 0;
let el, el_host;
const att_type = typeof attributes;
if (att_type === "string" || att_type === "number" || s.isSignal(attributes))
attributes = { textContent: attributes };
switch (true) {
case typeof tag === "function": {
scoped = 1;
const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
scope.push({ scope: tag, host });
el = tag(attributes || void 0);
const is_fragment = isInstance(el, enviroment.F);
if (el.nodeName === "#comment") break;
const el_mark = createElement.mark({
type: "component",
name: tag.name,
host: is_fragment ? "this" : "parentElement"
});
el.prepend(el_mark);
if (is_fragment) el_host = el_mark;
break;
}
case tag === "#text":
el = assign.call(this, enviroment.D.createTextNode(""), attributes);
break;
case (tag === "<>" || !tag):
el = assign.call(this, enviroment.D.createDocumentFragment(), attributes);
break;
case Boolean(namespace):
el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes);
break;
case !el:
el = assign.call(this, enviroment.D.createElement(tag), attributes);
}
chainableAppend(el);
if (!el_host) el_host = el;
addons.forEach((c) => c(el_host));
if (scoped) scope.pop();
scoped = 2;
return el;
}
createElement.mark = function(attrs, is_open = false) {
attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" ");
const end = is_open ? "" : "/";
const out = enviroment.D.createComment(`<dde:mark ${attrs}${enviroment.ssr}${end}>`);
if (is_open) out.end = enviroment.D.createComment("</dde:mark>");
return out;
};
function createElementNS(ns) {
const _this = this;
return function createElementNSCurried(...rest) {
namespace = ns;
const el = createElement.call(_this, ...rest);
namespace = void 0;
return el;
};
}
var assign_context = /* @__PURE__ */ new WeakMap();
var { setDeleteAttr: setDeleteAttr2 } = enviroment;
function assign(element, ...attributes) {
if (!attributes.length) return element;
assign_context.set(element, assignContext(element, this));
for (const [key, value] of Object.entries(oAssign({}, ...attributes)))
assignAttribute.call(this, element, key, value);
assign_context.delete(element);
return element;
}
function assignAttribute(element, key, value) {
const { setRemoveAttr, s } = assignContext(element, this);
const _this = this;
value = s.processReactiveAttribute(
element,
key,
value,
(key2, value2) => assignAttribute.call(_this, element, key2, value2)
);
const [k] = key;
if ("=" === k) return setRemoveAttr(key.slice(1), value);
if ("." === k) return setDelete(element, key.slice(1), value);
if (/(aria|data)([A-Z])/.test(key)) {
key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
return setRemoveAttr(key, value);
}
if ("className" === key) key = "class";
switch (key) {
case "xlink:href":
return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink");
case "textContent":
return setDeleteAttr2(element, key, value);
case "style":
if (typeof value !== "object") break;
/* falls through */
case "dataset":
return forEachEntries(s, key, element, value, setDelete.bind(null, element[key]));
case "ariaset":
return forEachEntries(s, key, element, value, (key2, val) => setRemoveAttr("aria-" + key2, val));
case "classList":
return classListDeclarative.call(_this, element, value);
}
return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value);
}
function assignContext(element, _this) {
if (assign_context.has(element)) return assign_context.get(element);
const is_svg = isInstance(element, enviroment.S);
const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
const s = signals(_this);
return { setRemoveAttr, s };
}
function classListDeclarative(element, toggle) {
const s = signals(this);
forEachEntries(
s,
"classList",
element,
toggle,
(class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val))
);
return element;
}
function isPropSetter(el, key) {
if (!(key in el)) return false;
const des = getPropDescriptor(el, key);
return !isUndef(des.set);
}
function getPropDescriptor(p, key) {
p = Object.getPrototypeOf(p);
if (!p) return {};
const des = Object.getOwnPropertyDescriptor(p, key);
if (!des) return getPropDescriptor(p, key);
return des;
}
function forEachEntries(s, target, element, obj, cb) {
const S = String;
if (typeof obj !== "object" || obj === null) return;
return Object.entries(obj).forEach(function process([key, val]) {
if (!key) return;
key = new S(key);
key.target = target;
val = s.processReactiveAttribute(element, key, val, cb);
cb(key, val);
});
}
// src/dom-lib/customElement.js
function simulateSlots(element, root = element) {
const mark_e = "\xB9\u2070", mark_s = "\u2713";
const slots = Object.fromEntries(
Array.from(root.querySelectorAll("slot")).filter((s) => !s.name.endsWith(mark_e)).map((s) => [s.name += mark_e, s])
);
element.append = new Proxy(element.append, {
apply(orig, _, els) {
if (els[0] === root) return orig.apply(element, els);
for (const el of els) {
const name = (el.slot || "") + mark_e;
try {
elementAttribute(el, "remove", "slot");
} catch (_error) {
}
const slot = slots[name];
if (!slot) return;
if (!slot.name.startsWith(mark_s)) {
slot.childNodes.forEach((c) => c.remove());
slot.name = mark_s + name;
}
slot.append(el);
}
element.append = orig;
return element;
}
});
if (element !== root) {
const els = Array.from(element.childNodes);
element.append(...els);
}
return root;
}
function customElementRender(target, render, props = {}) {
const custom_element = target.host || target;
scope.push({
scope: custom_element,
host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element
});
if (typeof props === "function") props = props.call(custom_element, custom_element);
const is_lte = custom_element[keyLTE];
if (!is_lte) lifecyclesToEvents(custom_element);
const out = render.call(custom_element, props);
if (!is_lte) custom_element.dispatchEvent(new Event(evc));
if (target.nodeType === 11 && typeof target.mode === "string")
custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true });
scope.pop();
return target.append(out);
}
function lifecyclesToEvents(class_declaration) {
wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) {
target.apply(thisArg, detail);
thisArg.dispatchEvent(new Event(evc));
});
wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) {
target.apply(thisArg, detail);
(globalThis.queueMicrotask || setTimeout)(
() => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd))
);
});
wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) {
const [attribute, , value] = detail;
thisArg.dispatchEvent(new CustomEvent(eva, {
detail: [attribute, value]
}));
target.apply(thisArg, detail);
});
class_declaration.prototype[keyLTE] = true;
return class_declaration;
}
function wrapMethod(obj, method, apply) {
obj[method] = new Proxy(obj[method] || (() => {
}), { apply });
}
// src/memo.js
var memoMark = "__dde_memo";
var memo_scope = [];
function memo(key, generator) {
if (!memo_scope.length) return generator(key);
const k = typeof key === "object" ? JSON.stringify(key) : key;
const [{ cache, after }] = memo_scope;
return after(k, hasOwn(cache, k) ? cache[k] : generator(key));
}
memo.isScope = function(obj) {
return obj[memoMark];
};
memo.scope = function memoScopeCreate(fun, { signal, onlyLast } = {}) {
let cache = oCreate();
function memoScope(...args) {
if (signal && signal.aborted)
return fun.apply(this, args);
let cache_local = onlyLast ? cache : oCreate();
memo_scope.unshift({
cache,
after(key, val) {
return cache_local[key] = val;
}
});
const out = fun.apply(this, args);
memo_scope.shift();
cache = cache_local;
return out;
}
memoScope[memoMark] = true;
memoScope.clear = () => cache = oCreate();
if (signal) signal.addEventListener("abort", memoScope.clear);
return memoScope;
};
return __toCommonJS(index_exports);
})();

861
dist/iife.min.d.ts vendored
View File

@ -1,861 +0,0 @@
// Generated by dts-bundle-generator v9.5.1
export interface Signal<V, A> {
/** The current value of the signal */
get(): V;
/** Set new value of the signal */
set(value: V, force?: boolean): V;
toJSON(): V;
valueOf(): V;
}
export type Action<V> = (this: {
value: V;
stopPropagation(): void;
}, ...a: any[]) => typeof signal._ | void;
//type SymbolSignal= Symbol;
export type SymbolOnclear = symbol;
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
first_time?: boolean;
};
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
export interface signal {
_: Symbol;
/**
* Computations signal. This creates a signal which is computed from other signals.
* */
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
/**
* Simple example:
* ```js
* const hello= S("Hello Signal");
* ```
* …simple todo signal:
* ```js
* const todos= S([], {
* add(v){ this.value.push(S(v)); },
* remove(i){ this.value.splice(i, 1); },
* [S.symbols.onclear](){ S.clear(...this.value); },
* });
* ```
* …computed signal:
* ```js
* const name= S("Jan");
* const surname= S("Andrle");
* const fullname= S(()=> name.get()+" "+surname.get());
* ```
* @param value Initial signal value. Or function computing value from other signals.
* @param actions Use to define actions on the signal. Such as add item to the array.
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
* by `S.clear`.
* */
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
clear(...signals: Signal<any, any>[]): void;
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
symbols: {
//signal: SymbolSignal;
onclear: SymbolOnclear;
};
/**
* Reactive element, which is rendered based on the given signal.
* ```js
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
* S.el(listS, list=> list.map(li=> el("li", li)));
* ```
* */
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
}
declare const signal: signal;
declare global {
type ddeSignal<T, A = {}> = Signal<T, A>;
type ddeAction<V> = Action<V>;
type ddeActions<V> = Actions<V>;
}
export type CustomElementTagNameMap = {
"#text": Text;
"#comment": Comment;
};
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
declare global {
type ddeComponentAttributes = Record<any, any> | undefined;
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
type ddeString = string | Signal<string, {}>;
type ddeStringable = ddeString | number | Signal<number, {}>;
}
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
export type PascalCase = `${Capitalize<string>}${string}`;
export type AttrsModified = {
/**
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
*/
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
}>;
/**
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
* for others.
*/
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
/**
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
* Values are converted to string (see {@link DOMStringMap}).
*
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
* */
dataset: Record<string, ddeStringable>;
/**
* Sets `aria-*` simiraly to `dataset`
* */
ariaset: Record<string, ddeString>;
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
export type IsReadonly<T, K extends keyof T> = T extends {
readonly [P in K]: T[K];
} ? true : false;
/**
* Just element attributtes
*
* In most cases, you can use native propertie such as
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
*
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
* @private
*/
export type ElementAttributes<T extends SupportedElement> = Partial<{
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
} & AttrsModified> & Record<string, any>;
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
export namespace el {
/**
* Creates a marker comment for elements
*
* @param attrs - Marker attributes
* @param [is_open=false] - Whether the marker is open-ended
* @returns Comment node marker
*/
export function mark(attrs: {
type: "component" | "reactive" | "later";
name?: string;
host?: "this" | "parentElement";
}, is_open?: boolean): Comment;
}
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<A extends {
textContent: ddeStringable;
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
export function el(tag_name?: "<>"): ddeDocumentFragment;
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
/** Simulate slots for ddeComponents */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
/**
* Simulate slots in Custom Elements without using `shadowRoot`.
* @param el Custom Element root element
* @param body Body of the custom element
* */
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
export interface On {
/** Listens to the DOM event. See {@link Document.addEventListener} */
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
target: EL;
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
/**
* Fires after the next tick of the Javascript event loop.
* This is handy for example to apply some property depending on the element content:
* ```js
* const selected= "Z";
* //...
* return el("form").append(
* el("select", null, on.defer(e=> e.value=selected)).append(
* el("option", { value: "A", textContent: "A" }),
* //...
* el("option", { value: "Z", textContent: "Z" }),
* ),
* );
* ```
* */
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
}
export const on: On;
export type Scope = {
scope: Node | Function | Object;
host: Host<SupportedElement>;
custom_element: false | HTMLElement;
prevent: boolean;
};
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
export const scope: {
current: Scope;
/** Stops all automatizations. E. g. signals used as attributes in current scope
* registers removing these listeners (and clean signal if no other listeners are detected)
* on `disconnected` event. */
preventDefault<T extends boolean>(prevent: T): T;
/**
* This represents reference to the current host element — `scope.host()`.
* It can be also used to register Addon(s) (functions to be called when component is initized)
* — `scope.host(on.connected(console.log))`.
* */
host: Host<SupportedElement>;
/**
* Creates/gets an AbortController that triggers when the element disconnects
* */
signal: AbortSignal;
state: Scope[];
/** Adds new child scope. All attributes are inherited by default. */
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
/** Adds root scope as a child of the current scope. */
pushRoot(): ReturnType<Array<Scope>["push"]>;
/** Removes last/current child scope. */
pop(): ReturnType<Array<Scope>["pop"]>;
};
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
/**
* This is used primarly for server side rendering. To be sure that all async operations
* are finished before the page is sent to the client.
* ```
* // on component
* function component(){
* …
* queue(fetch(...).then(...));
* }
*
* // building the page
* async function build(){
* const { component }= await import("./component.js");
* document.body.append(el(component));
* await queue();
* retutn document.body.innerHTML;
* }
* ```
* */
export function queue(promise?: Promise<unknown>): Promise<unknown>;
/**
* Memoization utility for caching DOM elements to improve performance.
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
*
* @param key - Unique identifier for the element (usually an ID or unique value)
* @param generator - Function that creates the element
* @returns The cached element if the key exists, otherwise the result of the generator function
*
* @example
* ```ts
* // Within S.el for list rendering
* S.el(itemsSignal, (items, memo) =>
* el("ul").append(
* ...items.map(item =>
* memo(item.id, () => el(ItemComponent, item))
* )
* )
* )
* ```
*/
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
/**
* Memo namespace containing utility functions for memoization.
*/
export namespace memo {
/**
* Checks if an object is a memo scope.
* @param obj - The object to check
* @returns True if the object is a memo scope
*/
export function isScope(obj: any): boolean;
/**
* Creates a memoized function with optional cleanup support.
*
* @param fun - The function to memoize
* @param options - Configuration options
* @param options.signal - AbortSignal for cleanup
* @param options.onlyLast - When true, only keeps the cache from the most recent call
* @returns A memoized version of the function with a .clear() method
*
* @example
* ```ts
* const renderItems = memo.scope(function(items) {
* return items.map(item =>
* memo(item.id, () => el("div", item.name))
* );
* }, {
* signal: controller.signal,
* onlyLast: true
* });
* ```
*/
export function scope<F extends Function>(fun: F, options?: {
signal?: AbortSignal;
onlyLast?: boolean;
}): F & {
clear: () => void;
};
}
/* TypeScript MEH */
declare global {
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
interface ddeDocumentFragment extends DocumentFragment {
append: ddeAppend<ddeDocumentFragment>;
}
interface ddeHTMLElement extends HTMLElement {
append: ddeAppend<ddeHTMLElement>;
}
interface ddeSVGElement extends SVGElement {
append: ddeAppend<ddeSVGElement>;
}
interface ddeMathMLElement extends MathMLElement {
append: ddeAppend<ddeMathMLElement>;
}
interface ddeHTMLElementTagNameMap {
"a": ddeHTMLAnchorElement;
"area": ddeHTMLAreaElement;
"audio": ddeHTMLAudioElement;
"base": ddeHTMLBaseElement;
"blockquote": ddeHTMLQuoteElement;
"body": ddeHTMLBodyElement;
"br": ddeHTMLBRElement;
"button": ddeHTMLButtonElement;
"canvas": ddeHTMLCanvasElement;
"caption": ddeHTMLTableCaptionElement;
"col": ddeHTMLTableColElement;
"colgroup": ddeHTMLTableColElement;
"data": ddeHTMLDataElement;
"datalist": ddeHTMLDataListElement;
"del": ddeHTMLModElement;
"details": ddeHTMLDetailsElement;
"dialog": ddeHTMLDialogElement;
"div": ddeHTMLDivElement;
"dl": ddeHTMLDListElement;
"embed": ddeHTMLEmbedElement;
"fieldset": ddeHTMLFieldSetElement;
"form": ddeHTMLFormElement;
"h1": ddeHTMLHeadingElement;
"h2": ddeHTMLHeadingElement;
"h3": ddeHTMLHeadingElement;
"h4": ddeHTMLHeadingElement;
"h5": ddeHTMLHeadingElement;
"h6": ddeHTMLHeadingElement;
"head": ddeHTMLHeadElement;
"hr": ddeHTMLHRElement;
"html": ddeHTMLHtmlElement;
"iframe": ddeHTMLIFrameElement;
"img": ddeHTMLImageElement;
"input": ddeHTMLInputElement;
"ins": ddeHTMLModElement;
"label": ddeHTMLLabelElement;
"legend": ddeHTMLLegendElement;
"li": ddeHTMLLIElement;
"link": ddeHTMLLinkElement;
"map": ddeHTMLMapElement;
"menu": ddeHTMLMenuElement;
"meta": ddeHTMLMetaElement;
"meter": ddeHTMLMeterElement;
"object": ddeHTMLObjectElement;
"ol": ddeHTMLOListElement;
"optgroup": ddeHTMLOptGroupElement;
"option": ddeHTMLOptionElement;
"output": ddeHTMLOutputElement;
"p": ddeHTMLParagraphElement;
"picture": ddeHTMLPictureElement;
"pre": ddeHTMLPreElement;
"progress": ddeHTMLProgressElement;
"q": ddeHTMLQuoteElement;
"script": ddeHTMLScriptElement;
"select": ddeHTMLSelectElement;
"slot": ddeHTMLSlotElement;
"source": ddeHTMLSourceElement;
"span": ddeHTMLSpanElement;
"style": ddeHTMLStyleElement;
"table": ddeHTMLTableElement;
"tbody": ddeHTMLTableSectionElement;
"td": ddeHTMLTableCellElement;
"template": ddeHTMLTemplateElement;
"textarea": ddeHTMLTextAreaElement;
"tfoot": ddeHTMLTableSectionElement;
"th": ddeHTMLTableCellElement;
"thead": ddeHTMLTableSectionElement;
"time": ddeHTMLTimeElement;
"title": ddeHTMLTitleElement;
"tr": ddeHTMLTableRowElement;
"track": ddeHTMLTrackElement;
"ul": ddeHTMLUListElement;
"video": ddeHTMLVideoElement;
}
interface ddeSVGElementTagNameMap {
"a": ddeSVGAElement;
"animate": ddeSVGAnimateElement;
"animateMotion": ddeSVGAnimateMotionElement;
"animateTransform": ddeSVGAnimateTransformElement;
"circle": ddeSVGCircleElement;
"clipPath": ddeSVGClipPathElement;
"defs": ddeSVGDefsElement;
"desc": ddeSVGDescElement;
"ellipse": ddeSVGEllipseElement;
"feBlend": ddeSVGFEBlendElement;
"feColorMatrix": ddeSVGFEColorMatrixElement;
"feComponentTransfer": ddeSVGFEComponentTransferElement;
"feComposite": ddeSVGFECompositeElement;
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
"feDistantLight": ddeSVGFEDistantLightElement;
"feDropShadow": ddeSVGFEDropShadowElement;
"feFlood": ddeSVGFEFloodElement;
"feFuncA": ddeSVGFEFuncAElement;
"feFuncB": ddeSVGFEFuncBElement;
"feFuncG": ddeSVGFEFuncGElement;
"feFuncR": ddeSVGFEFuncRElement;
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
"feImage": ddeSVGFEImageElement;
"feMerge": ddeSVGFEMergeElement;
"feMergeNode": ddeSVGFEMergeNodeElement;
"feMorphology": ddeSVGFEMorphologyElement;
"feOffset": ddeSVGFEOffsetElement;
"fePointLight": ddeSVGFEPointLightElement;
"feSpecularLighting": ddeSVGFESpecularLightingElement;
"feSpotLight": ddeSVGFESpotLightElement;
"feTile": ddeSVGFETileElement;
"feTurbulence": ddeSVGFETurbulenceElement;
"filter": ddeSVGFilterElement;
"foreignObject": ddeSVGForeignObjectElement;
"g": ddeSVGGElement;
"image": ddeSVGImageElement;
"line": ddeSVGLineElement;
"linearGradient": ddeSVGLinearGradientElement;
"marker": ddeSVGMarkerElement;
"mask": ddeSVGMaskElement;
"metadata": ddeSVGMetadataElement;
"mpath": ddeSVGMPathElement;
"path": ddeSVGPathElement;
"pattern": ddeSVGPatternElement;
"polygon": ddeSVGPolygonElement;
"polyline": ddeSVGPolylineElement;
"radialGradient": ddeSVGRadialGradientElement;
"rect": ddeSVGRectElement;
"script": ddeSVGScriptElement;
"set": ddeSVGSetElement;
"stop": ddeSVGStopElement;
"style": ddeSVGStyleElement;
"svg": ddeSVGSVGElement;
"switch": ddeSVGSwitchElement;
"symbol": ddeSVGSymbolElement;
"text": ddeSVGTextElement;
"textPath": ddeSVGTextPathElement;
"title": ddeSVGTitleElement;
"tspan": ddeSVGTSpanElement;
"use": ddeSVGUseElement;
"view": ddeSVGViewElement;
}
}
// editorconfig-checker-disable
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
append: ddeAppend<ddeHTMLAnchorElement>;
}
export interface ddeHTMLAreaElement extends HTMLAreaElement {
append: ddeAppend<ddeHTMLAreaElement>;
}
export interface ddeHTMLAudioElement extends HTMLAudioElement {
append: ddeAppend<ddeHTMLAudioElement>;
}
export interface ddeHTMLBaseElement extends HTMLBaseElement {
append: ddeAppend<ddeHTMLBaseElement>;
}
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
append: ddeAppend<ddeHTMLQuoteElement>;
}
export interface ddeHTMLBodyElement extends HTMLBodyElement {
append: ddeAppend<ddeHTMLBodyElement>;
}
export interface ddeHTMLBRElement extends HTMLBRElement {
append: ddeAppend<ddeHTMLBRElement>;
}
export interface ddeHTMLButtonElement extends HTMLButtonElement {
append: ddeAppend<ddeHTMLButtonElement>;
}
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
append: ddeAppend<ddeHTMLCanvasElement>;
}
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
append: ddeAppend<ddeHTMLTableCaptionElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLTableColElement extends HTMLTableColElement {
append: ddeAppend<ddeHTMLTableColElement>;
}
export interface ddeHTMLDataElement extends HTMLDataElement {
append: ddeAppend<ddeHTMLDataElement>;
}
export interface ddeHTMLDataListElement extends HTMLDataListElement {
append: ddeAppend<ddeHTMLDataListElement>;
}
export interface ddeHTMLModElement extends HTMLModElement {
append: ddeAppend<ddeHTMLModElement>;
}
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
append: ddeAppend<ddeHTMLDetailsElement>;
}
export interface ddeHTMLDialogElement extends HTMLDialogElement {
append: ddeAppend<ddeHTMLDialogElement>;
}
export interface ddeHTMLDivElement extends HTMLDivElement {
append: ddeAppend<ddeHTMLDivElement>;
}
export interface ddeHTMLDListElement extends HTMLDListElement {
append: ddeAppend<ddeHTMLDListElement>;
}
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
append: ddeAppend<ddeHTMLEmbedElement>;
}
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
append: ddeAppend<ddeHTMLFieldSetElement>;
}
export interface ddeHTMLFormElement extends HTMLFormElement {
append: ddeAppend<ddeHTMLFormElement>;
}
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
append: ddeAppend<ddeHTMLHeadingElement>;
}
export interface ddeHTMLHeadElement extends HTMLHeadElement {
append: ddeAppend<ddeHTMLHeadElement>;
}
export interface ddeHTMLHRElement extends HTMLHRElement {
append: ddeAppend<ddeHTMLHRElement>;
}
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
append: ddeAppend<ddeHTMLHtmlElement>;
}
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
append: ddeAppend<ddeHTMLIFrameElement>;
}
export interface ddeHTMLImageElement extends HTMLImageElement {
append: ddeAppend<ddeHTMLImageElement>;
}
export interface ddeHTMLInputElement extends HTMLInputElement {
append: ddeAppend<ddeHTMLInputElement>;
}
export interface ddeHTMLLabelElement extends HTMLLabelElement {
append: ddeAppend<ddeHTMLLabelElement>;
}
export interface ddeHTMLLegendElement extends HTMLLegendElement {
append: ddeAppend<ddeHTMLLegendElement>;
}
export interface ddeHTMLLIElement extends HTMLLIElement {
append: ddeAppend<ddeHTMLLIElement>;
}
export interface ddeHTMLLinkElement extends HTMLLinkElement {
append: ddeAppend<ddeHTMLLinkElement>;
}
export interface ddeHTMLMapElement extends HTMLMapElement {
append: ddeAppend<ddeHTMLMapElement>;
}
export interface ddeHTMLMenuElement extends HTMLMenuElement {
append: ddeAppend<ddeHTMLMenuElement>;
}
export interface ddeHTMLMetaElement extends HTMLMetaElement {
append: ddeAppend<ddeHTMLMetaElement>;
}
export interface ddeHTMLMeterElement extends HTMLMeterElement {
append: ddeAppend<ddeHTMLMeterElement>;
}
export interface ddeHTMLObjectElement extends HTMLObjectElement {
append: ddeAppend<ddeHTMLObjectElement>;
}
export interface ddeHTMLOListElement extends HTMLOListElement {
append: ddeAppend<ddeHTMLOListElement>;
}
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
append: ddeAppend<ddeHTMLOptGroupElement>;
}
export interface ddeHTMLOptionElement extends HTMLOptionElement {
append: ddeAppend<ddeHTMLOptionElement>;
}
export interface ddeHTMLOutputElement extends HTMLOutputElement {
append: ddeAppend<ddeHTMLOutputElement>;
}
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
append: ddeAppend<ddeHTMLParagraphElement>;
}
export interface ddeHTMLPictureElement extends HTMLPictureElement {
append: ddeAppend<ddeHTMLPictureElement>;
}
export interface ddeHTMLPreElement extends HTMLPreElement {
append: ddeAppend<ddeHTMLPreElement>;
}
export interface ddeHTMLProgressElement extends HTMLProgressElement {
append: ddeAppend<ddeHTMLProgressElement>;
}
export interface ddeHTMLScriptElement extends HTMLScriptElement {
append: ddeAppend<ddeHTMLScriptElement>;
}
export interface ddeHTMLSelectElement extends HTMLSelectElement {
append: ddeAppend<ddeHTMLSelectElement>;
}
export interface ddeHTMLSlotElement extends HTMLSlotElement {
append: ddeAppend<ddeHTMLSlotElement>;
}
export interface ddeHTMLSourceElement extends HTMLSourceElement {
append: ddeAppend<ddeHTMLSourceElement>;
}
export interface ddeHTMLSpanElement extends HTMLSpanElement {
append: ddeAppend<ddeHTMLSpanElement>;
}
export interface ddeHTMLStyleElement extends HTMLStyleElement {
append: ddeAppend<ddeHTMLStyleElement>;
}
export interface ddeHTMLTableElement extends HTMLTableElement {
append: ddeAppend<ddeHTMLTableElement>;
}
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
append: ddeAppend<ddeHTMLTableSectionElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
append: ddeAppend<ddeHTMLTemplateElement>;
}
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
append: ddeAppend<ddeHTMLTextAreaElement>;
}
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
append: ddeAppend<ddeHTMLTableCellElement>;
}
export interface ddeHTMLTimeElement extends HTMLTimeElement {
append: ddeAppend<ddeHTMLTimeElement>;
}
export interface ddeHTMLTitleElement extends HTMLTitleElement {
append: ddeAppend<ddeHTMLTitleElement>;
}
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
append: ddeAppend<ddeHTMLTableRowElement>;
}
export interface ddeHTMLTrackElement extends HTMLTrackElement {
append: ddeAppend<ddeHTMLTrackElement>;
}
export interface ddeHTMLUListElement extends HTMLUListElement {
append: ddeAppend<ddeHTMLUListElement>;
}
export interface ddeHTMLVideoElement extends HTMLVideoElement {
append: ddeAppend<ddeHTMLVideoElement>;
}
export interface ddeSVGAElement extends SVGAElement {
append: ddeAppend<ddeSVGAElement>;
}
export interface ddeSVGAnimateElement extends SVGAnimateElement {
append: ddeAppend<ddeSVGAnimateElement>;
}
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
append: ddeAppend<ddeSVGAnimateMotionElement>;
}
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
append: ddeAppend<ddeSVGAnimateTransformElement>;
}
export interface ddeSVGCircleElement extends SVGCircleElement {
append: ddeAppend<ddeSVGCircleElement>;
}
export interface ddeSVGClipPathElement extends SVGClipPathElement {
append: ddeAppend<ddeSVGClipPathElement>;
}
export interface ddeSVGDefsElement extends SVGDefsElement {
append: ddeAppend<ddeSVGDefsElement>;
}
export interface ddeSVGDescElement extends SVGDescElement {
append: ddeAppend<ddeSVGDescElement>;
}
export interface ddeSVGEllipseElement extends SVGEllipseElement {
append: ddeAppend<ddeSVGEllipseElement>;
}
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
append: ddeAppend<ddeSVGFEBlendElement>;
}
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
append: ddeAppend<ddeSVGFEColorMatrixElement>;
}
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
append: ddeAppend<ddeSVGFEComponentTransferElement>;
}
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
append: ddeAppend<ddeSVGFECompositeElement>;
}
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
}
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
}
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
}
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
append: ddeAppend<ddeSVGFEDistantLightElement>;
}
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
append: ddeAppend<ddeSVGFEDropShadowElement>;
}
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
append: ddeAppend<ddeSVGFEFloodElement>;
}
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
append: ddeAppend<ddeSVGFEFuncAElement>;
}
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
append: ddeAppend<ddeSVGFEFuncBElement>;
}
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
append: ddeAppend<ddeSVGFEFuncGElement>;
}
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
append: ddeAppend<ddeSVGFEFuncRElement>;
}
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
}
export interface ddeSVGFEImageElement extends SVGFEImageElement {
append: ddeAppend<ddeSVGFEImageElement>;
}
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
append: ddeAppend<ddeSVGFEMergeElement>;
}
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
append: ddeAppend<ddeSVGFEMergeNodeElement>;
}
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
append: ddeAppend<ddeSVGFEMorphologyElement>;
}
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
append: ddeAppend<ddeSVGFEOffsetElement>;
}
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
append: ddeAppend<ddeSVGFEPointLightElement>;
}
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
append: ddeAppend<ddeSVGFESpecularLightingElement>;
}
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
append: ddeAppend<ddeSVGFESpotLightElement>;
}
export interface ddeSVGFETileElement extends SVGFETileElement {
append: ddeAppend<ddeSVGFETileElement>;
}
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
append: ddeAppend<ddeSVGFETurbulenceElement>;
}
export interface ddeSVGFilterElement extends SVGFilterElement {
append: ddeAppend<ddeSVGFilterElement>;
}
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
append: ddeAppend<ddeSVGForeignObjectElement>;
}
export interface ddeSVGGElement extends SVGGElement {
append: ddeAppend<ddeSVGGElement>;
}
export interface ddeSVGImageElement extends SVGImageElement {
append: ddeAppend<ddeSVGImageElement>;
}
export interface ddeSVGLineElement extends SVGLineElement {
append: ddeAppend<ddeSVGLineElement>;
}
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
append: ddeAppend<ddeSVGLinearGradientElement>;
}
export interface ddeSVGMarkerElement extends SVGMarkerElement {
append: ddeAppend<ddeSVGMarkerElement>;
}
export interface ddeSVGMaskElement extends SVGMaskElement {
append: ddeAppend<ddeSVGMaskElement>;
}
export interface ddeSVGMetadataElement extends SVGMetadataElement {
append: ddeAppend<ddeSVGMetadataElement>;
}
export interface ddeSVGMPathElement extends SVGMPathElement {
append: ddeAppend<ddeSVGMPathElement>;
}
export interface ddeSVGPathElement extends SVGPathElement {
append: ddeAppend<ddeSVGPathElement>;
}
export interface ddeSVGPatternElement extends SVGPatternElement {
append: ddeAppend<ddeSVGPatternElement>;
}
export interface ddeSVGPolygonElement extends SVGPolygonElement {
append: ddeAppend<ddeSVGPolygonElement>;
}
export interface ddeSVGPolylineElement extends SVGPolylineElement {
append: ddeAppend<ddeSVGPolylineElement>;
}
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
append: ddeAppend<ddeSVGRadialGradientElement>;
}
export interface ddeSVGRectElement extends SVGRectElement {
append: ddeAppend<ddeSVGRectElement>;
}
export interface ddeSVGScriptElement extends SVGScriptElement {
append: ddeAppend<ddeSVGScriptElement>;
}
export interface ddeSVGSetElement extends SVGSetElement {
append: ddeAppend<ddeSVGSetElement>;
}
export interface ddeSVGStopElement extends SVGStopElement {
append: ddeAppend<ddeSVGStopElement>;
}
export interface ddeSVGStyleElement extends SVGStyleElement {
append: ddeAppend<ddeSVGStyleElement>;
}
export interface ddeSVGSVGElement extends SVGSVGElement {
append: ddeAppend<ddeSVGSVGElement>;
}
export interface ddeSVGSwitchElement extends SVGSwitchElement {
append: ddeAppend<ddeSVGSwitchElement>;
}
export interface ddeSVGSymbolElement extends SVGSymbolElement {
append: ddeAppend<ddeSVGSymbolElement>;
}
export interface ddeSVGTextElement extends SVGTextElement {
append: ddeAppend<ddeSVGTextElement>;
}
export interface ddeSVGTextPathElement extends SVGTextPathElement {
append: ddeAppend<ddeSVGTextPathElement>;
}
export interface ddeSVGTitleElement extends SVGTitleElement {
append: ddeAppend<ddeSVGTitleElement>;
}
export interface ddeSVGTSpanElement extends SVGTSpanElement {
append: ddeAppend<ddeSVGTSpanElement>;
}
export interface ddeSVGUseElement extends SVGUseElement {
append: ddeAppend<ddeSVGUseElement>;
}
export interface ddeSVGViewElement extends SVGViewElement {
append: ddeAppend<ddeSVGViewElement>;
}
export {
dispatchEvent$1 as dispatchEvent,
el as createElement,
elNS as createElementNS,
};
export as namespace iife;
export {};

1
dist/iife.min.js vendored

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

View File

@ -6,29 +6,8 @@
version="1.1"
id="svg5"
xml:space="preserve"
sodipodi:docname="logo.svg"
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="3.296875"
inkscape:cx="128"
inkscape:cy="101.61137"
inkscape:window-width="1920"
inkscape:window-height="1052"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g2" /><defs
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs4"><linearGradient
id="bgGradient"
x1="18"
@ -66,14 +45,14 @@
style="fill:url(#bgGradient);stroke-width:1.13636"
ry="50" /><g
id="g2"
transform="translate(0.4430186,-1.5165883)"><g
transform="translate(-3.5569814,-1.5165883)"><g
id="g1"
transform="matrix(1.5900346,0,0,1.5900346,-121.12651,-66.626074)"><g
opacity="0.25"
fill="#ffffff"
filter="url(#glow)"
id="g5"
transform="matrix(0.55415879,0,0,0.56134669,112.16444,51.505106)"><path
transform="matrix(0.55415879,0,0,0.56134669,110.90661,51.505106)"><path
d="m 80,60 -30,68 30,68"
stroke="#ffffff"
stroke-width="8"
@ -87,6 +66,6 @@
d="m 152.28246,124.58038 q 0,-4.06854 1.24681,-7.15275 1.24681,-3.08421 3.28107,-5.15785 2.03427,-2.06051 4.65913,-3.11046 2.62486,-1.04994 5.38096,-1.04994 6.82464,0 10.3682,4.02916 3.54356,4.04228 3.54356,11.78562 0,0.78746 -0.0263,1.64054 -0.0394,0.85308 -0.10499,1.37805 h -20.01456 q 0,3.01859 2.49362,4.76412 2.49362,1.73241 6.43091,1.73241 2.42799,0 4.63287,-0.52497 2.19176,-0.52497 3.70106,-1.04995 l 1.11556,6.89026 q -2.09989,0.72184 -4.46226,1.20744 -2.36237,0.49872 -5.31534,0.49872 -3.93729,0 -7.04775,-1.02369 -3.12359,-1.01058 -5.31534,-3.00547 -2.20489,-2.00802 -3.38607,-4.96098 -1.18114,-2.95297 -1.18114,-6.89026 m 20.67077,-3.21546 q 0,-1.2468 -0.3281,-2.40174 -0.32811,-1.14182 -1.04995,-2.06052 -0.72183,-0.9187 -1.8374,-1.48304 -1.11557,-0.55123 -2.7561,-0.55123 -1.57492,0 -2.71673,0.52498 -1.15494,0.52497 -1.91615,1.44367 -0.74809,0.9187 -1.16806,2.09989 -0.43311,1.18119 -0.56435,2.42799 z m 40.75096,17.06159 q -0.19687,0.13125 -0.89245,0.45936 -0.68247,0.3281 -1.79803,0.69558 -1.11557,0.35436 -2.71673,0.61685 -1.61429,0.26248 -3.64856,0.26248 -5.57783,0 -8.13707,-3.32045 -2.55923,-3.30732 -2.55923,-9.67261 v -26.18298 h -8.5308 v -6.693389 h 16.60224 v 33.466969 q 0,3.14983 1.24681,4.26539 1.24681,1.11557 3.14983,1.11557 2.428,0 4.06853,-0.65621 1.64054,-0.65622 2.16551,-0.85308 z"
id="path1-3"
style="fill:#ff5252;stroke-width:1.31243" /></g><path
d="m 25.256467,130.12148 q 0,6.64555 2.489444,10.86495 2.468347,4.21939 7.953563,4.21939 1.582274,0 2.953578,-0.10548 1.371303,-0.10549 2.848092,-0.31646 v -27.00413 q -1.476789,-0.84388 -3.375517,-1.4346 -1.898728,-0.56962 -4.008427,-0.56962 -4.641336,0 -6.751034,3.69197 -2.109699,3.69198 -2.109699,10.65398 m 29.219322,23.62862 q -3.586487,1.16034 -8.755248,1.89873 -5.168761,0.7384 -10.126552,0.7384 -11.603341,0 -17.55269,-6.85651 -5.970447,-6.85652 -5.970447,-18.77632 0,-12.13076 5.021083,-19.15606 4.999985,-7.0042 14.810082,-7.0042 2.637123,0 5.168761,0.56962 2.531638,0.59072 4.430366,1.64557 V 84.235539 l 12.974645,-2.215183 z m 23.523136,-23.62862 q 0,6.64555 2.489444,10.86495 2.468344,4.21939 7.953559,4.21939 1.582268,0 2.953578,-0.10548 1.3713,-0.10549 2.84809,-0.31646 v -27.00413 q -1.47679,-0.84388 -3.37552,-1.4346 -1.89873,-0.56962 -4.008423,-0.56962 -4.64133,0 -6.75103,3.69197 -2.109698,3.69198 -2.109698,10.65398 m 29.219315,23.62862 q -3.58648,1.16034 -8.75524,1.89873 -5.168774,0.7384 -10.126562,0.7384 -11.603332,0 -17.552681,-6.85651 -5.970446,-6.85652 -5.970446,-18.77632 0,-12.13076 4.999984,-19.15606 5.021082,-7.0042 14.831178,-7.0042 2.63712,0 5.168753,0.56962 2.53164,0.59072 4.43037,1.64557 V 84.235539 l 12.974644,-2.215183 z"
d="m 27.256467,130.12148 q 0,6.64555 2.489444,10.86495 2.468347,4.21939 7.953563,4.21939 1.582274,0 2.953578,-0.10548 1.371303,-0.10549 2.848092,-0.31646 v -27.00413 q -1.476789,-0.84388 -3.375517,-1.4346 -1.898728,-0.56962 -4.008427,-0.56962 -4.641336,0 -6.751034,3.69197 -2.109699,3.69198 -2.109699,10.65398 m 29.219322,23.62862 q -3.586487,1.16034 -8.755248,1.89873 -5.168761,0.7384 -10.126552,0.7384 -11.603341,0 -17.55269,-6.85651 -5.970447,-6.85652 -5.970447,-18.77632 0,-12.13076 5.021083,-19.15606 4.999985,-7.0042 14.810082,-7.0042 2.637123,0 5.168761,0.56962 2.531638,0.59072 4.430366,1.64557 V 84.235539 l 12.974645,-2.215183 z m 23.523136,-23.62862 q 0,6.64555 2.489444,10.86495 2.468344,4.21939 7.953559,4.21939 1.582268,0 2.953578,-0.10548 1.3713,-0.10549 2.84809,-0.31646 v -27.00413 q -1.47679,-0.84388 -3.37552,-1.4346 -1.89873,-0.56962 -4.008423,-0.56962 -4.64133,0 -6.75103,3.69197 -2.109698,3.69198 -2.109698,10.65398 m 29.219315,23.62862 q -3.58648,1.16034 -8.75524,1.89873 -5.168774,0.7384 -10.126562,0.7384 -11.603332,0 -17.552681,-6.85651 -5.970446,-6.85652 -5.970446,-18.77632 0,-12.13076 4.999984,-19.15606 5.021082,-7.0042 14.831178,-7.0042 2.63712,0 5.168753,0.56962 2.53164,0.59072 4.43037,1.64557 V 84.235539 l 12.974644,-2.215183 z"
id="path1"
style="fill:#ff5252;fill-opacity:0.664797;stroke-width:2.1097" /></g></svg>
style="fill:#ff5252;stroke-width:2.1097;fill-opacity:0.66479665" /></g></svg>

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -1,4 +1,4 @@
import { page_id, registerClientFile, styles } from "../ssr.js";
import { registerClientFile, styles } from "../ssr.js";
const host= "."+code.name;
styles.css`
/* Code block styling */
@ -177,9 +177,6 @@ ${host}:hover .copy-button {
}
`;
import { el } from "deka-dom-el";
/**
* @typedef {"js"|"ts"|"html"|"css"|"shell"|"-"} Language
* */
/**
* Prints code to the page and registers flems to make it interactive.
* @param {object} attrs
@ -187,35 +184,56 @@ import { el } from "deka-dom-el";
* @param {string} [attrs.className]
* @param {URL} [attrs.src] Example code file path
* @param {string} [attrs.content] Example code
* @param {Language} [attrs.language="-s"] Language of the code
* @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
* @param {string} [attrs.page_id] ID of the page, if setted it registers shiki
* */
export function code({ id, src, content, language= "-", className= host.slice(1) }){
if(src){
content= s.cat(src);
if(language=== "-") language= /** @type {Language} */(src.pathname.split(".").pop());
}
content= normalizeIndentation(content);
export function code({ id, src, content, language= "js", className= host.slice(1), page_id }){
if(src) content= s.cat(src);
let dataJS;
if(language!== "-"){
registerClientPart();
if(page_id){
registerClientPart(page_id);
dataJS= "todo";
}
return el("div", { id, className, dataJS, tabIndex: 0 }).append(
el("code", { className: "language-"+language, textContent: content.trim() })
);
}
export function pre({ content }){
content= normalizeIndentation(content);
return el("pre").append(el("code", content.trim()));
}
let is_registered= {};
function registerClientPart(){
/** @param {string} page_id */
function registerClientPart(page_id){
if(is_registered[page_id]) return;
// Add Shiki with a more reliable loading method
document.head.append(
// Use a newer version of Shiki with better performance
el("script", { src: "https://cdn.jsdelivr.net/npm/shiki@0.14.3/dist/index.unpkg.iife.js", defer: true }),
// Make sure we can match Flems styling in dark/light mode
el("style", `
/* Ensure CodeMirror and Shiki use the same font */
.CodeMirror *, .shiki * {
font-family: var(--font-mono) !important;
}
/* Style Shiki's output to match our theme */
.shiki {
background-color: var(--shiki-color-background) !important;
color: var(--shiki-color-text) !important;
padding: 1rem;
border-radius: var(--border-radius);
tab-size: 2;
}
/* Ensure Shiki code tokens use our CSS variables */
.shiki .keyword { color: var(--shiki-token-keyword) !important; }
.shiki .constant { color: var(--shiki-token-constant) !important; }
.shiki .string { color: var(--shiki-token-string) !important; }
.shiki .comment { color: var(--shiki-token-comment) !important; }
.shiki .function { color: var(--shiki-token-function) !important; }
.shiki .operator, .shiki .punctuation { color: var(--shiki-token-punctuation) !important; }
.shiki .parameter { color: var(--shiki-token-parameter) !important; }
.shiki .variable { color: var(--shiki-token-variable) !important; }
.shiki .property { color: var(--shiki-token-property) !important; }
`),
);
registerClientFile(
@ -227,9 +245,3 @@ function registerClientPart(){
is_registered[page_id]= true;
}
/** @param {string} src */
function normalizeIndentation(src){
const lines= src.split("\n");
const min_indent= Math.min(...lines.map(line=> line.search(/\S/)).filter(i=> i >= 0));
return lines.map(line=> line.slice(min_indent)).join("\n");
}

View File

@ -1,175 +0,0 @@
import { page_id, styles } from "../ssr.js";
styles.css`
#html-to-dde-converter {
grid-column: full-main;
display: flex;
flex-direction: column;
gap: 1.5rem;
padding: 1.5rem;
border-radius: var(--border-radius);
background-color: var(--bg-sidebar);
box-shadow: var(--shadow);
border: 1px solid var(--border);
}
#html-to-dde-converter h3 {
margin-top: 0;
color: var(--primary);
}
#html-to-dde-converter .description {
color: var(--text-light);
font-size: 0.95rem;
margin-top: -1rem;
}
#html-to-dde-converter .converter-form {
display: flex;
flex-direction: column;
gap: 1rem;
}
#html-to-dde-converter .input-group,
#html-to-dde-converter .output-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
#html-to-dde-converter [type="number"]{
width: 3em;
font-variant-numeric: tabular-nums;
font-size: 1rem;
}
#html-to-dde-converter label {
font-weight: 500;
display: flex;
justify-content: space-between;
}
#html-to-dde-converter .options {
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 0.5rem;
}
#html-to-dde-converter .option-group {
display: flex;
align-items: center;
gap: 0.5rem;
}
#html-to-dde-converter textarea {
font-family: var(--font-mono);
font-size: 0.9rem;
padding: 1rem;
border-radius: var(--border-radius);
border: 1px solid var(--border);
background-color: var(--bg);
color: var(--text);
min-height: 200px;
height: 25em;
resize: vertical;
}
#html-to-dde-converter textarea:focus {
outline: 2px solid var(--primary-light);
outline-offset: 1px;
}
#html-to-dde-converter .button-group {
display: flex;
gap: 0.5rem;
justify-content: space-between;
align-items: center;
}
#html-to-dde-converter button {
padding: 0.5rem 1rem;
border-radius: var(--border-radius);
border: none;
background-color: var(--primary);
color: var(--button-text);
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s ease;
}
#html-to-dde-converter button:hover {
background-color: var(--primary-dark);
}
#html-to-dde-converter button.secondary {
background-color: transparent;
border: 1px solid var(--border);
color: var(--text);
}
#html-to-dde-converter button.secondary:hover {
background-color: var(--bg);
border-color: var(--primary);
}
#html-to-dde-converter .copy-button {
background-color: var(--secondary);
}
#html-to-dde-converter .copy-button:hover {
background-color: var(--secondary-dark);
}
#html-to-dde-converter .status {
font-size: 0.9rem;
color: var(--text-light);
}
#html-to-dde-converter .error {
color: hsl(0, 100%, 60%);
font-size: 0.9rem;
margin-top: 0.5rem;
}
/* Sample HTML examples list */
#html-to-dde-converter .examples-list {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.5rem;
}
#html-to-dde-converter .example-button {
font-size: 0.85rem;
padding: 0.25rem 0.5rem;
}
`;
import { ireland } from "./ireland.html.js";
import { el } from "deka-dom-el";
const fileURL= url=> new URL(url, import.meta.url);
export function converter(){
registerClientPart(page_id);
return el(ireland, {
src: fileURL("./converter.js.js"),
exportName: "converter",
});
}
let is_registered= {};
/** @param {string} page_id */
function registerClientPart(page_id){
if(is_registered[page_id]) return;
document.head.append(
el("script", {
// src: "https://unpkg.com/@beforesemicolon/html-parser/dist/client.js",
src: "https://cdn.jsdelivr.net/npm/@beforesemicolon/html-parser/dist/client.js",
type: "text/javascript",
charset: "utf-8",
defer: true
}),
);
is_registered[page_id]= true;
}

View File

@ -1,384 +0,0 @@
import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
const { parse }= globalThis.BFS || { parse(){ return { children: [ "not implemented" ] } } };
// Example HTML snippets
const examples = [
{
name: "Simple Component",
html: `<div class="card">
<img src="image.jpg" alt="Card Image" class="card-image">
<h2 class="card-title">Card Title</h2>
<p class="card-text">This is a simple card component</p>
<button aria-pressed="mixed" type="button" class="card-button">Click Me</button>
</div>`
},
{
name: "Navigation",
html: `<nav class="main-nav">
<ul>
<li><a href="/" class="active">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/services">Services</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>`
},
{
name: "Form",
html: `<form class="contact-form" onsubmit="submitForm(event)">
<div class="form-group">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="message">Message:</label>
<textarea id="message" name="message" rows="4" required></textarea>
</div>
<button type="submit" class="submit-btn">Send Message</button>
</form>`
}
];
// Convert HTML to dd<el> code
function convertHTMLtoDDE(html, options = {}) {
try {
const parsed = parse(html);
const content = parsed.children[0] || parsed.childNodes[0];
return !content ? "" : nodeToDDE(content, options);
} catch (error) {
console.error("Parsing error:", error);
return `// Error parsing HTML: ${error.message}`;
}
}
// Node types based on standard DOM nodeType values
const NODE_TYPE = {
ELEMENT: 1, // Standard element node (equivalent to node.type === "element")
TEXT: 3, // Text node (equivalent to node.type === "text")
COMMENT: 8 // Comment node (equivalent to node.type === "comment")
};
// Convert a parsed node to dd<el> code
function nodeToDDE(node, options = {}, level = 0) {
const tab= options.indent === "-1" ? "\t" : " ".repeat(options.indent);
const indent = tab.repeat(level);
const nextIndent = tab.repeat(level + 1);
const { nodeType } = node;
// Handle text nodes
if (nodeType === NODE_TYPE.TEXT) {
const text = el("i", { innerText: node.nodeValue }).textContent;
if (!text.trim()) return null;
// Return as plain text or template string for longer text
return text.includes("\n") || text.includes('"')
? `\`${text}\``
: `"${text}"`;
}
// Handle comment nodes
if (nodeType === NODE_TYPE.COMMENT) {
const text = node.nodeValue;
if (!text.trim()) return null;
return text.includes("\n")
? [ "/*", ...text.trim().split("\n").map(l=> tab+l), "*/" ]
: [ `// ${text}` ];
}
// For element nodes
if (nodeType === NODE_TYPE.ELEMENT) {
// Special case for SVG elements
const isNS = node.tagName === "svg";
const elFunction = isNS ? "elNS" : "el";
// Get tag name
let tagStr = `"${node.tagName}"`;
// Process attributes
const attrs = [];
const sets = {
aria: {},
data: {},
}
for (const { name: key, value } of node.attributes) {
// Handle class attribute
if (key === "class") {
attrs.push(`className: "${value}"`);
continue;
}
// Handle style attribute
if (key === "style") {
if (options.styleAsObject) {
// Convert inline style to object
const styleObj = {};
value.split(";").forEach(part => {
const [propRaw, valueRaw] = part.split(":");
if (propRaw && valueRaw) {
const prop = propRaw.trim();
const propCamel = prop.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
styleObj[propCamel] = valueRaw.trim();
}
});
if (Object.keys(styleObj).length > 0) {
const styleStr = JSON.stringify(styleObj).replace(/"([^"]+)":/g, "$1:");
attrs.push(`style: ${styleStr}`);
}
} else {
// Keep as string
attrs.push(`style: "${value}"`);
}
continue;
}
// Handle boolean attributes
if (value === "" || value === key) {
attrs.push(`${key}: true`);
continue;
}
// Handle data/aria attributes
if (key.startsWith("data-") || key.startsWith("aria-")) {
const keyName = key.startsWith("aria-") ? "aria" : "data";
const keyCamel = key.slice(keyName.length + 1).replace(/-([a-z])/g, (_, c) => c.toUpperCase());
sets[keyName][keyCamel] = value;
continue;
}
// Regular attributes
const keyRegular = key==="for"
? "htmlFor"
: key.startsWith("on")
? `"=${key}"`
: key.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
attrs.push(`${keyRegular}: "${value}"`);
}
// Process sets
for (const [name, set] of Object.entries(sets)) {
if(options.dataAttrsAsCamel)
for (const [key, value] of Object.entries(set))
attrs.push(`${name}${key[0].toUpperCase() + key.substring(1)}: "${value}"`);
else {
const setStr= Object.entries(set).map(([key, value]) => `${key}: "${value}"`).join(",");
if (setStr !== "")
attrs.push(`${name}set: { ${setStr} }`);
}
}
// Process children
const children = [];
for (const child of node.childNodes) {
const childCode = nodeToDDE(child, options, level + 1);
if (!childCode) continue;
children.push(childCode);
}
if(node.childNodes.length===1 && node.childNodes[0].nodeType===NODE_TYPE.TEXT){
const textContent= children.pop().slice(1, -1);
attrs.unshift(`textContent: "${textContent}"`);
}
// Build the element creation code
let result = `${elFunction}("${node.tagName.toLowerCase()}"`;
// Add attributes if any
if (attrs.length > 0) {
const tooLong= attrs.join(``).length+result.length > 55;
if(options.expaned || tooLong || attrs.length > 3)
result += `, {\n${nextIndent}${attrs.join(`,\n${nextIndent}`)},\n${indent}}`;
else
result += `, { ${attrs.join(", ")} }`;
}
// Add children if any
if (children.length > 0) {
const chs= children.map(ch=>
Array.isArray(ch) ? ch.map(l=> nextIndent + l).join("\n") :
nextIndent + ch + ",");
result += `).append(\n${chs.join("\n")}\n${indent})`;
} else {
result += ")";
}
return result;
}
return null;
}
export function converter() {
// State for the converter
const htmlInput = S(examples[0].html);
const error = S("");
const status = S("");
const showStatus= msg => {
status.set(msg);
// Clear status after 3 seconds
setTimeout(() => status.set(""), 3000);
};
// Options state
const options = {
styleAsObject: {
title: "Convert style to object",
value: S(true),
},
dataAttrsAsCamel: {
title: "dataKey/ariaKey (or dataset/ariaset)",
value: S(true),
},
indent: {
title: "Indentation (-1 for tabs)",
value: S("-1"),
type: "number",
},
expaned: {
title: "Force multiline",
value: S(false),
}
};
const getOptions = ()=> Object.fromEntries(Object.entries(options)
.map(([key, option]) => ([
key,
option.value.get()
]))
);
// Update the dd<el> output when input or options change
const ddeOutput = S(() => {
try {
const result = convertHTMLtoDDE(htmlInput.get(), getOptions());
error.set("");
return result;
} catch (err) {
error.set(`Error: ${err.message}`);
return "";
}
});
// Event handlers
const onConvert = on("submit", e => {
e.preventDefault();
htmlInput.set(htmlInput.get(), true);
showStatus("Converted!");
});
const onCopy = on("click", async () => {
if (!ddeOutput.get()) return;
try {
await navigator.clipboard.writeText(ddeOutput.get());
showStatus("Copied to clipboard!");
} catch (err) {
error.set(`Could not copy: ${err.message}`);
}
});
const onClear = on("click", () => {
htmlInput.set("");
showStatus("Input cleared");
});
const onExampleLoad = (example) => on("click", () => {
htmlInput.set(example.html);
showStatus(`Loaded "${example.name}" example`);
});
const optionsElements = () => Object.entries(options)
.map(([key, option]) =>
el("label", { className: "option-group" }).append(
option.type==="number"
? el("input", {
type: option.type || "checkbox",
name: key,
value: option.value.get(),
max: 10,
}, on("change", e => option.value.set(e.target.value)))
: el("input", {
type: option.type || "checkbox",
name: key,
checked: option.value.get(),
}, on("change", e => option.value.set(e.target.checked))),
option.title,
)
);
const exampleButtons = examples.map(example =>
el("button", {
type: "button",
className: "secondary example-button"
}, onExampleLoad(example)).append(example.name)
);
return el("div", { id: "html-to-dde-converter" }).append(
el("h3", "HTML to dd<el> Converter"),
el("p", { className: "description" }).append(
"Convert HTML markup to dd<el> JavaScript code. Paste your HTML below or choose from an example."
),
el("form", { className: "converter-form" }, onConvert).append(
el("div", { className: "options" }).append(...optionsElements()),
el("div", { className: "examples-list" }).append(
el("label", "Examples: "),
...exampleButtons
),
el("div", { className: "editor-container" }).append(
el("div", { className: "input-group" }).append(
el("label", { htmlFor: "html-input" }).append(
"HTML Input",
el("div", { className: "button-group" }).append(
el("button", {
type: "button",
className: "secondary",
title: "Clear input"
}, onClear).append("Clear")
)
),
el("textarea", {
id: "html-input",
spellcheck: false,
value: htmlInput,
placeholder: "Paste your HTML here or choose an example",
oninput: e => htmlInput.set(e.target.value)
})
),
el("div", { className: "output-group" }).append(
el("label", { htmlFor: "dde-output" }).append(
"dd<el> Output",
el("div", { className: "button-group" }).append(
el("button", {
textContent: "Copy",
type: "button",
className: "copy-button",
title: "Copy to clipboard",
disabled: S(() => !ddeOutput.get())
}, onCopy)
)
),
el("textarea", {
id: "dde-output",
readonly: true,
spellcheck: false,
placeholder: "The converted dd<el> code will appear here",
value: S(() => ddeOutput.get() || "// Convert HTML to see results here")
})
)
),
el("div", { className: "button-group" }).append(
S.el(error, error => !error ? el() : el("div", { className: "error" }).append(error)),
el("div", { className: "status", textContent: status }),
el("button", { type: "submit" }).append("Convert")
)
)
);
}

View File

@ -1,8 +1,9 @@
import { page_id, styles } from "../ssr.js";
import { styles } from "../ssr.js";
const host= "."+example.name;
styles.css`
${host} {
grid-column: full-main;
width: calc(100% - .75em);
height: calc(4/6 * var(--body-max-width));
border-radius: var(--border-radius);
box-shadow: var(--shadow);
@ -83,6 +84,7 @@ html[data-theme="light"] .cm-s-material .cm-error { color: #f44336 !important; }
@media (max-width: 767px) {
${host} {
height: 50vh;
max-width: 100%;
}
${host} main {
flex-grow: 1;
@ -94,18 +96,6 @@ html[data-theme="light"] .cm-s-material .cm-error { color: #f44336 !important; }
max-width: 100% !important;
}
}
${host}[data-variant=big]{
height: 150vh;
main {
flex-flow: column nowrap;
flex-grow: 1;
}
main > * {
width: 100%;
max-width: 100% !important;
}
}
`;
const dde_content= s.cat(new URL("../../dist/esm-with-signals.js", import.meta.url)).toString();
@ -118,15 +108,15 @@ import { relative } from "node:path";
* @param {object} attrs
* @param {URL} attrs.src Example code file path
* @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
* @param {"normal"|"big"} [attrs.variant="normal"] Size of the example
* @param {string} attrs.page_id ID of the page
* */
export function example({ src, language= "js", variant= "normal" }){
export function example({ src, language= "js", page_id }){
registerClientPart(page_id);
const content= s.cat(src).toString()
.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ' from "./esm-with-signals.js";');
const id= "code-example-"+generateCodeId(src);
return el().append(
el(code, { id, content, language, className: example.name }, el=> el.dataset.variant= variant),
el(code, { id, content, language, className: example.name }),
elCode({ id, content, extension: "."+language })
);
}

View File

@ -1,375 +0,0 @@
/**
* Case Study: Data Dashboard with Charts
*
* This example demonstrates:
* - Integration with a third-party charting library
* - Data fetching and state management
* - Responsive layout design
* - Multiple interactive components working together
*/
import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
/**
* Data Dashboard Component with Chart Integration
* @returns {HTMLElement} Dashboard element
*/
export function DataDashboard() {
// Mock data for demonstration
const DATA = {
sales: [42, 58, 65, 49, 72, 85, 63, 70, 78, 89, 95, 86],
visitors: [1420, 1620, 1750, 1850, 2100, 2400, 2250, 2500, 2750, 2900, 3100, 3200],
conversion: [2.9, 3.5, 3.7, 2.6, 3.4, 3.5, 2.8, 2.8, 2.8, 3.1, 3.0, 2.7],
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
};
const years = [2022, 2023, 2024];
const dataTypes = [
{ id: 'sales', label: 'Sales', unit: 'K' },
{ id: 'visitors', label: 'Visitors', unit: '' },
{ id: 'conversion', label: 'Conversion Rate', unit: '%' }
];
// Filter options
const selectedYear = S(2024);
const onYearChange = on("change", e => {
selectedYear.set(parseInt(/** @type {HTMLSelectElement} */(e.target).value));
loadData();
});
const selectedDataType = S(/** @type {'sales' | 'visitors' | 'conversion'} */ ('sales'));
const onDataTypeChange = on("click", e => {
const type = /** @type {'sales' | 'visitors' | 'conversion'} */(
/** @type {HTMLButtonElement} */(e.currentTarget).dataset.type);
selectedDataType.set(type);
});
const currentDataType = S(() => dataTypes.find(type => type.id === selectedDataType.get()));
const selectedData = S(() => DATA[selectedDataType.get()]);
// Values based on filters
const totalValue = S(() => selectedData.get().reduce((sum, value) => sum + value, 0));
const averageValue = S(() => {
const data = selectedData.get();
return data.reduce((sum, value) => sum + value, 0) / data.length;
});
const highestValue = S(() => Math.max(...selectedData.get()));
// Simulate data loading
const isLoading = S(false);
const error = S(null);
function loadData() {
isLoading.set(true);
error.set(null);
// Simulate API call
setTimeout(() => {
if (Math.random() > 0.9) {
// Simulate occasional error
error.set('Failed to load data. Please try again.');
}
isLoading.set(false);
}, 800);
}
// Reactive chart rendering
const chart = S(()=> {
const chart= el("canvas", { id: "chart-canvas", width: 800, height: 400 });
const ctx = chart.getContext('2d');
const data = selectedData.get();
const months = DATA.months;
const width = chart.width;
const height = chart.height;
const maxValue = Math.max(...data) * 1.1;
const barWidth = width / data.length - 10;
// Clear canvas
ctx.clearRect(0, 0, width, height);
// Draw background grid
ctx.beginPath();
ctx.strokeStyle = '#f0f0f0';
ctx.lineWidth = 1;
for(let i = 0; i < 5; i++) {
const y = height - (height * (i / 5)) - 30;
ctx.moveTo(50, y);
ctx.lineTo(width - 20, y);
// Draw grid labels
ctx.fillStyle = '#999';
ctx.font = '12px Arial';
ctx.fillText(Math.round(maxValue * (i / 5)).toString(), 20, y + 5);
}
ctx.stroke();
// Draw bars
data.forEach((value, index) => {
const x = index * (barWidth + 10) + 60;
const barHeight = (value / maxValue) * (height - 60);
// Bar
ctx.fillStyle = '#4a90e2';
ctx.fillRect(x, height - barHeight - 30, barWidth, barHeight);
// Month label
ctx.fillStyle = '#666';
ctx.font = '12px Arial';
ctx.fillText(months[index], x + barWidth/2 - 10, height - 10);
});
// Chart title
ctx.fillStyle = '#333';
ctx.font = 'bold 14px Arial';
ctx.fillText(`${currentDataType.get().label} (${selectedYear.get()})`, width/2 - 80, 20);
return chart;
});
return el("div", { className: "dashboard" }).append(
el("header", { className: "dashboard-header" }).append(
el("h1", "Sales Performance Dashboard"),
el("div", { className: "year-filter" }).append(
el("label", { htmlFor: "yearSelect", textContent: "Select Year:" }),
el("select", { id: "yearSelect" },
on.defer(el=> el.value = selectedYear.get().toString()),
onYearChange
).append(
...years.map(year => el("option", { value: year, textContent: year }))
)
)
),
S.el(error, errorMsg => !errorMsg
? el()
: el("div", { className: "error-message" }).append(
el("p", errorMsg),
el("button", { textContent: "Retry", type: "button" }, on("click", loadData)),
),
),
S.el(isLoading, loading => !loading
? el()
: el("div", { className: "loading-spinner" })
),
// Main dashboard content
el("div", { className: "dashboard-content" }).append(
// Metrics cards
el("div", { className: "metrics-container" }).append(
el("div", { className: "metric-card" }).append(
el("h3", "Total"),
el("#text", S(() => `${totalValue.get().toLocaleString()}${currentDataType.get().unit}`)),
),
el("div", { className: "metric-card" }).append(
el("h3", "Average"),
el("#text", S(() => `${averageValue.get().toFixed(1)}${currentDataType.get().unit}`)),
),
el("div", { className: "metric-card" }).append(
el("h3", "Highest"),
el("#text", S(() => `${highestValue.get()}${currentDataType.get().unit}`)),
),
),
// Data type selection tabs
el("div", { className: "data-type-tabs" }).append(
...dataTypes.map(type =>
el("button", {
type: "button",
className: S(() => selectedDataType.get() === type.id ? 'active' : ''),
dataType: type.id,
textContent: type.label
}, onDataTypeChange)
)
),
// Chart container
el("div", { className: "chart-container" }).append(
S.el(chart, chart => chart)
)
),
);
}
// Render the component
document.body.append(
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
el(DataDashboard)
),
el("style", `
.dashboard {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 1000px;
margin: 0 auto;
padding: 1rem;
background: #fff;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
border-radius: 8px;
}
.dashboard-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 1px solid #eee;
}
.dashboard-header h1 {
font-size: 1.5rem;
margin: 0;
color: #333;
}
.year-filter {
display: flex;
align-items: center;
gap: 0.5rem;
}
.year-filter select {
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.metrics-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin-bottom: 1.5rem;
}
.metric-card {
background: #f9f9f9;
border-radius: 8px;
padding: 1rem;
text-align: center;
transition: transform 0.2s ease;
}
.metric-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
}
.metric-card h3 {
margin-top: 0;
color: #666;
font-size: 0.9rem;
margin-bottom: 0.5rem;
}
.metric-card p {
font-size: 1.5rem;
font-weight: bold;
color: #333;
margin: 0;
}
.data-type-tabs {
display: flex;
border-bottom: 1px solid #eee;
margin-bottom: 1.5rem;
}
.data-type-tabs button {
background: none;
border: none;
padding: 0.75rem 1.5rem;
font-size: 1rem;
cursor: pointer;
color: #666;
position: relative;
}
.data-type-tabs button.active {
color: #4a90e2;
font-weight: 500;
}
.data-type-tabs button.active::after {
content: '';
position: absolute;
bottom: -1px;
left: 0;
width: 100%;
height: 3px;
background: #4a90e2;
border-radius: 3px 3px 0 0;
}
.chart-container {
background: #fff;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1.5rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
overflow: auto;
}
.loading-spinner {
display: flex;
justify-content: center;
align-items: center;
height: 100px;
}
.loading-spinner::before {
content: '';
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #4a90e2;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error-message {
background: #ffecec;
color: #e74c3c;
padding: 1rem;
border-radius: 4px;
margin-bottom: 1.5rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.error-message p {
margin: 0;
}
.error-message button {
background: #e74c3c;
color: white;
border: none;
border-radius: 4px;
padding: 0.5rem 1rem;
cursor: pointer;
}
@media (max-width: 768px) {
.metrics-container {
grid-template-columns: 1fr;
}
.dashboard-header {
flex-direction: column;
align-items: flex-start;
gap: 1rem;
}
.year-filter {
width: 100%;
}
.year-filter select {
flex-grow: 1;
}
}
`)
);

View File

@ -1,412 +0,0 @@
/**
* Case Study: Interactive Image Gallery
*
* This example demonstrates:
* - Dynamic loading of content
* - Lightbox functionality
* - Animation handling
* - Keyboard and gesture navigation
*/
import { el, memo, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
// Sample image data
const imagesSample = (url=> [
{ id: 1, src: url+'nature', alt: 'Nature', title: 'Beautiful Landscape' },
{ id: 2, src: url+'places', alt: 'City', title: 'Urban Architecture' },
{ id: 3, src: url+'people', alt: 'People', title: 'Street Photography' },
{ id: 4, src: url+'food', alt: 'Food', title: 'Culinary Delights' },
{ id: 5, src: url+'animals', alt: 'Animals', title: 'Wildlife' },
{ id: 6, src: url+'travel', alt: 'Travel', title: 'Adventure Awaits' },
{ id: 7, src: url+'computer', alt: 'Technology', title: 'Modern Tech' },
{ id: 8, src: url+'music', alt: 'Art', title: 'Creative Expression' },
])('https://api.algobook.info/v1/randomimage?category=');
/**
* Interactive Image Gallery Component
* @returns {HTMLElement} Gallery element
*/
export function ImageGallery(images= imagesSample) {
const filterTag = S('all');
const imagesToDisplay = S(() => {
const tag = filterTag.get();
if (tag === 'all') return images;
else return images.filter(img => img.alt.toLowerCase() === tag);
})
const onFilterChange = tag => on("click", () => {
filterTag.set(tag);
});
// Lightbox
const selectedImageId = S(null);
const selectedImage = S(() => {
const id = selectedImageId.get();
return id ? images.find(img => img.id === id) : null;
});
const isLightboxOpen = S(() => selectedImage.get() !== null);
const onImageClick = id => on("click", () => {
selectedImageId.set(id);
document.body.style.overflow = 'hidden'; // Prevent scrolling when lightbox is open
// Add keyboard event listeners when lightbox opens
document.addEventListener('keydown', handleKeyDown);
});
const closeLightbox = () => {
selectedImageId.set(null);
document.body.style.overflow = ''; // Restore scrolling
// Remove keyboard event listeners when lightbox closes
document.removeEventListener('keydown', handleKeyDown);
};
const onPrevImage = e => {
e.stopPropagation(); // Prevent closing the lightbox
const images = imagesToDisplay.get();
const currentId = selectedImageId.get();
const currentIndex = images.findIndex(img => img.id === currentId);
const prevIndex = (currentIndex - 1 + images.length) % images.length;
selectedImageId.set(images[prevIndex].id);
};
const onNextImage = e => {
e.stopPropagation(); // Prevent closing the lightbox
const images = imagesToDisplay.get();
const currentId = selectedImageId.get();
const currentIndex = images.findIndex(img => img.id === currentId);
const nextIndex = (currentIndex + 1) % images.length;
selectedImageId.set(images[nextIndex].id);
};
// Keyboard navigation handler
function handleKeyDown(e) {
switch(e.key) {
case 'Escape':
closeLightbox();
break;
case 'ArrowLeft':
document.querySelector('.lightbox-prev-btn').click();
break;
case 'ArrowRight':
document.querySelector('.lightbox-next-btn').click();
break;
}
}
// Build the gallery UI
return el("div", { className: "gallery-container" }).append(
// Gallery header
el("header", { className: "gallery-header" }).append(
el("h1", "Interactive Image Gallery"),
el("p", "Click on any image to view it in the lightbox. Use arrow keys for navigation.")
),
// Filter options
el("div", { className: "gallery-filters" }).append(
el("button", {
classList: { active: S(() => filterTag.get() === 'all') },
textContent: "All"
}, onFilterChange('all')),
el("button", {
classList: { active: S(() => filterTag.get() === 'nature') },
textContent: "Nature"
}, onFilterChange('nature')),
el("button", {
classList: { active: S(() => filterTag.get() === 'urban') },
textContent: "Urban"
}, onFilterChange('urban')),
el("button", {
classList: { active: S(() => filterTag.get() === 'people') },
textContent: "People"
}, onFilterChange('people'))
),
// Image grid
el("div", { className: "gallery-grid" }).append(
S.el(imagesToDisplay, images =>
images.map(image =>
memo(image.id, ()=>
el("div", {
className: "gallery-item",
dataTag: image.alt.toLowerCase()
}).append(
el("img", {
src: image.src,
alt: image.alt,
loading: "lazy"
}, onImageClick(image.id)),
el("div", { className: "gallery-item-caption" }).append(
el("h3", image.title),
el("p", image.alt)
)
)
)
)
)
),
// Lightbox (only shown when an image is selected)
S.el(isLightboxOpen, open => !open
? el()
: el("div", { className: "lightbox-overlay" }, on("click", closeLightbox)).append(
el("div", {
className: "lightbox-content",
onClick: e => e.stopPropagation() // Prevent closing when clicking inside
}).append(
el("button", {
className: "lightbox-close-btn",
"aria-label": "Close lightbox"
}, on("click", closeLightbox)).append("×"),
el("button", {
className: "lightbox-prev-btn",
"aria-label": "Previous image"
}, on("click", onPrevImage)).append(""),
el("button", {
className: "lightbox-next-btn",
"aria-label": "Next image"
}, on("click", onNextImage)).append(""),
S.el(selectedImage, img => !img
? el()
: el("div", { className: "lightbox-image-container" }).append(
el("img", {
src: img.src,
alt: img.alt,
className: "lightbox-image"
}),
el("div", { className: "lightbox-caption" }).append(
el("h2", img.title),
el("p", img.alt)
)
)
)
)
)
),
);
}
// Render the component
document.body.append(
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
el(ImageGallery)
),
el("style", `
.gallery-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.gallery-header {
text-align: center;
margin-bottom: 2rem;
}
.gallery-header h1 {
margin-bottom: 0.5rem;
color: #333;
}
.gallery-header p {
color: #666;
}
.gallery-filters {
display: flex;
justify-content: center;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.gallery-filters button {
background: none;
border: none;
padding: 0.5rem 1.5rem;
margin: 0 0.5rem;
font-size: 1rem;
cursor: pointer;
border-radius: 30px;
transition: all 0.3s ease;
color: #555;
}
.gallery-filters button:hover {
background: #f0f0f0;
}
.gallery-filters button.active {
background: #4a90e2;
color: white;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
.gallery-item {
position: relative;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
transition: transform 0.5s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
color: white;
padding: 1rem;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.gallery-item:hover .gallery-item-caption {
transform: translateY(0);
}
.gallery-item-caption h3 {
margin: 0 0 0.5rem;
font-size: 1.2rem;
}
.gallery-item-caption p {
margin: 0;
font-size: 0.9rem;
opacity: 0.8;
}
/* Lightbox styles */
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
padding: 2rem;
}
.lightbox-content {
position: relative;
max-width: 90%;
max-height: 90%;
}
.lightbox-image-container {
overflow: hidden;
border-radius: 4px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
background: #000;
}
.lightbox-image {
max-width: 100%;
max-height: 80vh;
display: block;
margin: 0 auto;
}
.lightbox-caption {
background: #222;
color: white;
padding: 1rem;
text-align: center;
}
.lightbox-caption h2 {
margin: 0 0 0.5rem;
}
.lightbox-caption p {
margin: 0;
opacity: 0.8;
}
.lightbox-close-btn,
.lightbox-prev-btn,
.lightbox-next-btn {
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
font-size: 1.5rem;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background 0.3s ease;
position: absolute;
}
.lightbox-close-btn:hover,
.lightbox-prev-btn:hover,
.lightbox-next-btn:hover {
background: rgba(0, 0, 0, 0.8);
}
.lightbox-close-btn {
top: -25px;
right: -25px;
}
.lightbox-prev-btn {
left: -25px;
top: 50%;
transform: translateY(-50%);
}
.lightbox-next-btn {
right: -25px;
top: 50%;
transform: translateY(-50%);
}
@media (max-width: 768px) {
.gallery-container {
padding: 1rem;
}
.gallery-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
}
.lightbox-prev-btn,
.lightbox-next-btn {
width: 40px;
height: 40px;
font-size: 1.2rem;
}
}
`)
);

View File

@ -1,339 +0,0 @@
/**
* Case Study: Interactive Form with Validation
*
* This example demonstrates:
* - Form handling with real-time validation
* - Reactive UI updates based on input state
* - Complex form state management
* - Clean separation of concerns (data, validation, UI)
*/
import { dispatchEvent, el, on, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
/**
* @typedef {Object} FormState
* @property {string} name
* @property {string} email
* @property {string} password
* @property {string} confirmPassword
* @property {boolean} agreedToTerms
* */
/**
* Interactive Form with Validation Component
* @returns {HTMLElement} Form element
*/
export function InteractiveForm() {
const submitted = S(false);
/** @type {FormState|null} */
let formState = null;
/** @param {CustomEvent<FormState>} event */
const onSubmit = ({ detail }) => {
submitted.set(true);
formState = detail;
};
const onAnotherAccount = () => {
submitted.set(false)
formState = null;
};
return el("div", { className: "form-container" }).append(
S.el(submitted, s => s
? el("div", { className: "success-message" }).append(
el("h3", "Thank you for registering!"),
el("p", `Welcome, ${formState.name}! Your account has been created successfully.`),
el("button", { textContent: "Register another account", type: "button" },
on("click", onAnotherAccount)
),
)
: el(Form, { initial: formState }, on("form:submit", onSubmit))
)
);
}
/**
* Form Component
* @type {(props: { initial: FormState | null }) => HTMLElement}
* */
export function Form({ initial }) {
const { host }= scope;
// Form state management
const formState = S(initial || {
name: '',
email: '',
password: '',
confirmPassword: '',
agreedToTerms: false
}, {
/**
* @template {keyof FormState} K
* @param {K} key
* @param {FormState[K]} value
* */
update(key, value) {
this.value[key] = value;
}
});
/**
* Event handler for input events
* @param {"value"|"checked"} prop
* @returns {(ev: Event) => void}
* */
const onChange= prop => ev => {
const input = /** @type {HTMLInputElement} */(ev.target);
S.action(formState, "update", /** @type {keyof FormState} */(input.id), input[prop]);
};
// Form validate state
const nameValid = S(() => formState.get().name.length >= 3);
const emailValid = S(() => {
const email = formState.get().email;
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
});
const passwordValid = S(() => {
const password = formState.get().password;
return password.length >= 8 && /[A-Z]/.test(password) && /[0-9]/.test(password);
});
const passwordsMatch = S(() => {
const { password, confirmPassword } = formState.get();
return password === confirmPassword && confirmPassword !== '';
});
const termsAgreed = S(() => formState.get().agreedToTerms);
const formValid = S(() =>
nameValid.get() &&
emailValid.get() &&
passwordValid.get() &&
passwordsMatch.get() &&
termsAgreed.get()
);
const dispatcSubmit = dispatchEvent("form:submit", host);
const onSubmit = on("submit", e => {
e.preventDefault();
if (!formValid.get()) {
return;
}
dispatcSubmit(formState.get());
});
// Component UI
return el("form", { className: "registration-form" }, onSubmit).append(
el("h2", "Create an Account"),
// Name field
el("div", { classList: {
"form-group": true,
valid: nameValid,
invalid: S(()=> !nameValid.get() && formState.get().name)
}}).append(
el("label", { htmlFor: "name", textContent: "Full Name" }),
el("input", {
id: "name",
type: "text",
value: formState.get().name,
placeholder: "Enter your full name"
}, on("input", onChange("value"))),
el("div", { className: "validation-message", textContent: "Name must be at least 3 characters long" }),
),
// Email field
el("div", { classList: {
"form-group": true,
valid: emailValid,
invalid: S(()=> !emailValid.get() && formState.get().email)
}}).append(
el("label", { htmlFor: "email", textContent: "Email Address" }),
el("input", {
id: "email",
type: "email",
value: formState.get().email,
placeholder: "Enter your email address"
}, on("input", onChange("value"))),
el("div", { className: "validation-message", textContent: "Please enter a valid email address" })
),
// Password field
el("div", { classList: {
"form-group": true,
valid: passwordValid,
invalid: S(()=> !passwordValid.get() && formState.get().password)
}}).append(
el("label", { htmlFor: "password", textContent: "Password" }),
el("input", {
id: "password",
type: "password",
value: formState.get().password,
placeholder: "Create a password"
}, on("input", onChange("value"))),
el("div", {
className: "validation-message",
textContent: "Password must be at least 8 characters with at least one uppercase letter and one number",
}),
),
// Confirm password field
el("div", { classList: {
"form-group": true,
valid: passwordsMatch,
invalid: S(()=> !passwordsMatch.get() && formState.get().confirmPassword)
}}).append(
el("label", { htmlFor: "confirmPassword", textContent: "Confirm Password" }),
el("input", {
id: "confirmPassword",
type: "password",
value: formState.get().confirmPassword,
placeholder: "Confirm your password"
}, on("input", onChange("value"))),
el("div", { className: "validation-message", textContent: "Passwords must match" }),
),
// Terms agreement
el("div", { className: "form-group checkbox-group" }).append(
el("input", {
id: "agreedToTerms",
type: "checkbox",
checked: formState.get().agreedToTerms
}, on("change", onChange("checked"))),
el("label", { htmlFor: "agreedToTerms", textContent: "I agree to the Terms and Conditions" }),
),
// Submit button
el("button", {
textContent: "Create Account",
type: "submit",
className: "submit-button",
disabled: S(() => !formValid.get())
}),
);
}
// Render the component
document.body.append(
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
el(InteractiveForm)
),
el("style", `
.form-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 500px;
margin: 0 auto;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
background: #fff;
}
h2 {
margin-top: 0;
color: #333;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
position: relative;
transition: all 0.3s ease;
}
label {
display: block;
margin-bottom: 0.5rem;
color: #555;
font-weight: 500;
}
input[type="text"],
input[type="email"],
input[type="password"] {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
transition: border-color 0.3s ease;
}
input:focus {
outline: none;
border-color: #4a90e2;
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
.checkbox-group {
display: flex;
align-items: center;
}
.checkbox-group label {
margin: 0 0 0 0.5rem;
}
.validation-message {
font-size: 0.85rem;
color: #e74c3c;
margin-top: 0.5rem;
height: 0;
overflow: hidden;
opacity: 0;
transition: all 0.3s ease;
}
.form-group.invalid .validation-message {
height: auto;
opacity: 1;
}
.form-group.valid input {
border-color: #2ecc71;
}
.form-group.invalid input {
border-color: #e74c3c;
}
.submit-button {
background-color: #4a90e2;
color: white;
border: none;
border-radius: 4px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
width: 100%;
}
.submit-button:hover:not(:disabled) {
background-color: #3a7bc8;
}
.submit-button:disabled {
background-color: #b5b5b5;
cursor: not-allowed;
}
.success-message {
text-align: center;
color: #2ecc71;
}
.success-message h3 {
margin-top: 0;
}
.success-message button {
background-color: #2ecc71;
color: white;
border: none;
border-radius: 4px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
cursor: pointer;
margin-top: 1rem;
}
.success-message button:hover {
background-color: #27ae60;
}
`),
);

View File

@ -1,501 +0,0 @@
import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
export function ProductCatalog() {
const itemsPerPage = 5;
const products = asyncSignal(S, fetchProducts, { initial: [], keepLast: true });
const searchTerm = S("");
const handleSearch = (e) => searchTerm.set(e.target.value);
const sortOrder = S("default");
const handleSort = (e) => sortOrder.set(e.target.value);
const page = S(1);
const handlePageChange = (newPage) => page.set(newPage);
const resetFilters = () => {
searchTerm.set("");
sortOrder.set("default");
page.set(1);
};
const filteredProducts = S(() => {
if (products.status.get() !== "resolved") return [];
const results = products.result.get().filter(product =>
product.title.toLowerCase().includes(searchTerm.get().toLowerCase()) ||
product.description.toLowerCase().includes(searchTerm.get().toLowerCase())
);
return [...results].sort((a, b) => {
const order = sortOrder.get();
if (order === "price-asc") return a.price - b.price;
if (order === "price-desc") return b.price - a.price;
if (order === "rating") return b.rating - a.rating;
return 0; // default: no sorting
});
});
const totalPages = S(() => Math.ceil(filteredProducts.get().length / itemsPerPage));
const paginatedProducts = S(() => {
const currentPage = page.get();
const filtered = filteredProducts.get();
const start = (currentPage - 1) * itemsPerPage;
return filtered.slice(start, start + itemsPerPage);
});
// Component structure
return el("div", { className: "product-catalog" }).append(
el("header", { className: "catalog-header" }).append(
el("h2", "Product Catalog"),
el("div", { className: "toolbar" }).append(
el("button", {
className: "refresh-btn",
textContent: "Refresh Products",
type: "button",
onclick: () => products.invoke(),
}),
el("button", {
className: "reset-btn",
textContent: "Reset Filters",
type: "button",
onclick: resetFilters,
})
)
),
// Search and filter controls
el("div", { className: "controls" }).append(
el("div", { className: "search-box" }).append(
el("input", {
type: "search",
placeholder: "Search products...",
value: searchTerm,
oninput: handleSearch,
})
),
el("div", { className: "sort-options" }).append(
el("label", "Sort by: "),
el("select", { onchange: handleSort }, on.defer(el => el.value = sortOrder.get())).append(
el("option", { value: "default", textContent: "Default" }),
el("option", { value: "price-asc", textContent: "Price: Low to High" }),
el("option", { value: "price-desc", textContent: "Price: High to Low" }),
el("option", { value: "rating", textContent: "Top Rated" })
)
)
),
// Status indicators
el("div", { className: "status-container" }).append(
S.el(products.status, status =>
status === "pending" ?
el("div", { className: "loader" }).append(
el("div", { className: "spinner" }),
el("p", "Loading products...")
)
: status === "rejected" ?
el("div", { className: "error-message" }).append(
el("p", products.error.get().message),
el("button", {
textContent: "Try Again",
onclick: () => products.invoke()
})
)
: el()
)
),
// Results count
S.el(S(()=> [filteredProducts.get(), searchTerm.get()]), ([filtered, term]) =>
products.status.get() === "resolved"
? el("div", {
className: "results-info",
textContent: term ?
`Found ${filtered.length} products matching "${term}"`
: `Showing all ${filtered.length} products`
})
: el()
),
// Products grid
el("div", { className: "products-grid" }).append(
S.el(paginatedProducts, paginatedItems =>
products.status.get() === "resolved" && paginatedItems.length > 0 ?
paginatedItems.map(product => el(ProductCard, { product }))
: products.status.get() === "resolved" && paginatedItems.length === 0 ?
el("p", { className: "no-results", textContent: "No products found matching your criteria." })
: el()
)
),
// Pagination
S.el(S(()=> [totalPages.get(), page.get()]), ([total, current]) =>
products.status.get() === "resolved" && total > 1 ?
el("div", { className: "pagination" }).append(
el("button", {
textContent: "Previous",
disabled: current === 1,
onclick: () => handlePageChange(current - 1)
}),
...Array.from({ length: total }, (_, i) => i + 1).map(num =>
el("button", {
className: num === current ? "current-page" : "",
textContent: num,
onclick: () => handlePageChange(num)
})
),
el("button", {
textContent: "Next",
disabled: current === total,
onclick: () => handlePageChange(current + 1)
})
)
: el()
)
);
}
// Product card component
function ProductCard({ product }) {
const showDetails = S(false);
return el("div", { className: "product-card" }).append(
el("div", { className: "product-image" }).append(
el("img", { src: product.thumbnail, alt: product.title })
),
el("div", { className: "product-info" }).append(
el("h3", { className: "product-title", textContent: product.title }),
el("div", { className: "product-price-rating" }).append(
el("span", { className: "product-price", textContent: `$${product.price.toFixed(2)}` }),
el("span", { className: "product-rating" }).append(
el("span", { className: "stars", textContent: "★".repeat(Math.round(product.rating)) }),
el("span", { className: "rating-value", textContent: `(${product.rating})` }),
)
),
el("p", { className: "product-category", textContent: `Category: ${product.category}` }),
S.el(showDetails, details =>
details ?
el("div", { className: "product-details" }).append(
el("p", { className: "product-description", textContent: product.description }),
el("div", { className: "product-meta" }).append(
el("p", `Brand: ${product.brand}`),
el("p", `Stock: ${product.stock} units`),
el("p", `Discount: ${product.discountPercentage}%`)
)
)
: el()
),
el("div", { className: "product-actions" }).append(
el("button", {
className: "details-btn",
textContent: S(() => showDetails.get() ? "Hide Details" : "Show Details"),
onclick: () => showDetails.set(!showDetails.get())
}),
el("button", {
className: "add-to-cart-btn",
textContent: "Add to Cart"
})
)
)
);
}
// Data fetching function
async function fetchProducts({ signal }) {
await simulateNetworkDelay();
// Simulate random errors for demonstration
if (Math.random() > 0.9) throw new Error("Failed to load products. Network error.");
const response = await fetch("https://dummyjson.com/products", { signal });
if (!response.ok) throw new Error(`API error: ${response.status}`);
const data = await response.json();
return data.products.slice(0, 20); // Limit to 20 products for the demo
}
// Utility for simulating network latency
function simulateNetworkDelay(min = 300, max = 1200) {
const delay = Math.floor(Math.random() * (max - min + 1)) + min;
return new Promise(resolve => setTimeout(resolve, delay));
}
/**
* Custom hook for async data fetching with signals
* @template T
* @param {typeof S} S - Signal constructor
* @param {(params: { signal: AbortSignal }) => Promise<T>} invoker - Async function to execute
* @param {{ initial?: T, keepLast?: boolean }} options - Configuration options
* @returns {Object} Status signals and control methods
*/
export function asyncSignal(S, invoker, { initial, keepLast } = {}) {
// Status tracking signals
const status = S("pending");
const result = S(initial);
const error = S(null);
let controller = null;
// Function to trigger data fetching
async function invoke() {
// Cancel any in-flight request
if (controller) controller.abort();
controller = new AbortController();
status.set("pending");
error.set(null);
if (!keepLast) result.set(initial);
try {
const data = await invoker({
signal: controller.signal,
});
if (!controller.signal.aborted) {
status.set("resolved");
result.set(data);
}
} catch (e) {
if (e.name !== "AbortError") {
error.set(e);
status.set("rejected");
}
}
}
// Initial data fetch
invoke();
return { status, result, error, invoke };
}
// Initialize the component
document.body.append(
el(ProductCatalog),
el("style", `
.product-catalog {
font-family: system-ui, -apple-system, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.catalog-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.toolbar button {
margin-left: 10px;
padding: 8px 12px;
border-radius: 4px;
border: none;
background: #4a6cf7;
color: white;
cursor: pointer;
}
.controls {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
gap: 15px;
flex-wrap: wrap;
}
.search-box input {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
width: 300px;
max-width: 100%;
}
.sort-options select {
padding: 8px 12px;
border: 1px solid #ddd;
border-radius: 4px;
}
.loader {
text-align: center;
padding: 40px 0;
}
.spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #4a6cf7;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-message {
background: #ffebee;
color: #c62828;
padding: 15px;
border-radius: 4px;
margin: 20px 0;
text-align: center;
}
.results-info {
margin-bottom: 15px;
color: #666;
}
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.product-card {
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}
.product-image img {
width: 100%;
height: 180px;
object-fit: cover;
display: block;
}
.product-info {
padding: 15px;
}
.product-title {
margin: 0 0 10px;
font-size: 1.1rem;
height: 2.4rem;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.product-price-rating {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
}
.product-price {
font-weight: bold;
color: #4a6cf7;
font-size: 1.2rem;
}
.stars {
color: gold;
margin-right: 5px;
}
.product-category {
color: #666;
font-size: 0.9rem;
margin-bottom: 15px;
}
.product-details {
margin: 15px 0;
font-size: 0.9rem;
}
.product-description {
line-height: 1.5;
margin-bottom: 10px;
color: #444;
}
.product-meta {
display: flex;
flex-wrap: wrap;
gap: 10px;
color: #666;
font-size: 0.85rem;
}
.product-actions {
display: flex;
gap: 10px;
margin-top: 15px;
}
.product-actions button {
flex: 1;
padding: 8px 0;
border: none;
border-radius: 4px;
cursor: pointer;
}
.details-btn {
background: #eee;
color: #333;
}
.add-to-cart-btn {
background: #4a6cf7;
color: white;
}
.pagination {
display: flex;
justify-content: center;
gap: 5px;
margin-top: 30px;
}
.pagination button {
padding: 8px 12px;
border: 1px solid #ddd;
background: white;
border-radius: 4px;
cursor: pointer;
}
.pagination button.current-page {
background: #4a6cf7;
color: white;
border-color: #4a6cf7;
}
.pagination button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.no-results {
grid-column: 1 / -1;
text-align: center;
padding: 40px;
color: #666;
}
@media (max-width: 768px) {
.controls {
flex-direction: column;
}
.search-box input {
width: 100%;
}
.products-grid {
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
}
}
`),
);

View File

@ -1,715 +0,0 @@
/**
* Case Study: Task Manager Application
*
* This example demonstrates:
* - Complex state management with signals
* - Drag and drop functionality
* - Local storage persistence
* - Responsive design for different devices
*/
import { el, on, dispatchEvent, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
/** @typedef {{ id: number, title: string, description: string, priority: string, status: string }} Task */
/**
* Task Manager Component
* @returns {HTMLElement} Task manager UI
*/
export function TaskManager() {
// <Tasks store>
const STORAGE_KEY = 'dde-task-manager';
const STATUSES = {
TODO: 'todo',
IN_PROGRESS: 'in-progress',
DONE: 'done'
};
/** @type {Task[]} */
let initialTasks = [];
try {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
initialTasks = JSON.parse(saved);
}
} catch (e) {
console.error('Failed to load tasks from localStorage', e);
}
if (!initialTasks.length) {
initialTasks = [
{ id: 1, title: 'Create project structure', description: 'Set up folders and initial files',
status: STATUSES.DONE, priority: 'high' },
{ id: 2, title: 'Design UI components', description: 'Create mockups for main views',
status: STATUSES.IN_PROGRESS, priority: 'medium' },
{ id: 3, title: 'Implement authentication', description: 'Set up user login and registration',
status: STATUSES.TODO, priority: 'high' },
{ id: 4, title: 'Write documentation', description: 'Document API endpoints and usage examples',
status: STATUSES.TODO, priority: 'low' },
];
}
const tasks = S(initialTasks, {
add(task) { this.value.push(task); },
remove(id) { this.value = this.value.filter(task => task.id !== id); },
update(id, task) {
const current= this.value.find(t => t.id === id);
if (current) Object.assign(current, task);
}
});
S.on(tasks, value => {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
} catch (e) {
console.error('Failed to save tasks to localStorage', e);
}
});
// </Tasks store>
const filterPriority = S('all');
const searchQuery = S('');
// Filtered tasks based on priority and search query
const filteredTasks = S(() => {
let filtered = tasks.get();
// Filter by priority
if (filterPriority.get() !== 'all') {
filtered = filtered.filter(task => task.priority === filterPriority.get());
}
// Filter by search query
const query = searchQuery.get().toLowerCase();
if (query) {
filtered = filtered.filter(task =>
task.title.toLowerCase().includes(query) ||
task.description.toLowerCase().includes(query)
);
}
return filtered;
});
/** Tasks grouped by status for display in columns */
const tasksByStatus = S(() => {
const filtered = filteredTasks.get();
return {
[STATUSES.TODO]: filtered.filter(t => t.status === STATUSES.TODO),
[STATUSES.IN_PROGRESS]: filtered.filter(t => t.status === STATUSES.IN_PROGRESS),
[STATUSES.DONE]: filtered.filter(t => t.status === STATUSES.DONE)
};
});
// <Add> signals and handlers for adding new tasks
const newTask = { title: '', description: '', priority: 'medium' };
const onAddTask = e => {
e.preventDefault();
if (!newTask.title) return;
S.action(tasks, "add", {
id: Date.now(),
status: STATUSES.TODO,
...newTask
});
e.target.reset();
};
// </Add>
const onCardEdit= on("card:edit", /** @param {CardEditEvent} ev */({ detail: [ id, task ] })=>
S.action(tasks, "update", id, task));
const onCardDelete= on("card:delete", /** @param {CardDeleteEvent} ev */({ detail: id })=>
S.action(tasks, "remove", id));
const { onDragable, onDragArea }= moveElementAddon(
(id, status) => S.action(tasks, "update", id, { status })
);
// Build the task manager UI
return el("div", { className: "task-manager" }).append(
el("header", { className: "app-header" }).append(
el("h1", "DDE Task Manager"),
el("div", { className: "app-controls" }).append(
el("input", {
type: "text",
placeholder: "Search tasks...",
value: searchQuery.get()
}, on("input", e => searchQuery.set(e.target.value))),
el("select", null,
on.defer(el=> el.value= filterPriority.get()),
on("change", e => filterPriority.set(e.target.value))
).append(
el("option", { value: "all", textContent: "All Priorities" }),
el("option", { value: "low", textContent: "Low Priority" }),
el("option", { value: "medium", textContent: "Medium Priority" }),
el("option", { value: "high", textContent: "High Priority" })
)
)
),
// Add new task form
el("form", { className: "new-task-form" }, on("submit", onAddTask)).append(
el("div", { className: "form-row" }).append(
el("input", {
type: "text",
placeholder: "New task title",
value: newTask.title,
required: true
}, on("input", e => newTask.title= e.target.value.trim())),
el("select", null,
on.defer(el=> el.value= newTask.priority),
on("change", e => newTask.priority= e.target.value)
).append(
el("option", { value: "low", textContent: "Low" }),
el("option", { value: "medium", textContent: "Medium" }),
el("option", { value: "high", textContent: "High" })
),
el("button", { type: "submit", className: "add-btn" }).append("Add Task")
),
el("textarea", {
placeholder: "Task description (optional)",
value: newTask.description
}, on("input", e => newTask.description= e.target.value.trim()))
),
// Task board with columns
el("div", { className: "task-board" }).append(
// Todo column
el("div", {
id: `column-${STATUSES.TODO}`,
className: "task-column"
}, onDragArea(STATUSES.TODO)).append(
el("h2", { className: "column-header" }).append(
"To Do ",
el("span", {
textContent: S(() => tasksByStatus.get()[STATUSES.TODO].length),
className: "task-count"
}),
),
S.el(S(() => tasksByStatus.get()[STATUSES.TODO]), tasks =>
el("div", { className: "column-tasks" }).append(
...tasks.map(task=> el(TaskCard, { task, onDragable }, onCardEdit, onCardDelete))
)
)
),
// In Progress column
el("div", {
id: `column-${STATUSES.IN_PROGRESS}`,
className: "task-column"
}, onDragArea(STATUSES.IN_PROGRESS)).append(
el("h2", { className: "column-header" }).append(
"In Progress ",
el("span", {
textContent: S(() => tasksByStatus.get()[STATUSES.IN_PROGRESS].length),
className: "task-count",
}),
),
S.el(S(() => tasksByStatus.get()[STATUSES.IN_PROGRESS]), tasks =>
el("div", { className: "column-tasks" }).append(
...tasks.map(task=> el(TaskCard, { task, onDragable }, onCardEdit, onCardDelete))
)
)
),
// Done column
el("div", {
id: `column-${STATUSES.DONE}`,
className: "task-column"
}, onDragArea(STATUSES.DONE)).append(
el("h2", { className: "column-header" }).append(
"Done ",
el("span", {
textContent: S(() => tasksByStatus.get()[STATUSES.DONE].length),
className: "task-count",
}),
),
S.el(S(() => tasksByStatus.get()[STATUSES.DONE]), tasks =>
el("div", { className: "column-tasks" }).append(
...tasks.map(task=> el(TaskCard, { task, onDragable }, onCardEdit, onCardDelete))
)
)
)
),
);
}
/** @typedef {CustomEvent<[ string, Task ]>} CardEditEvent */
/** @typedef {CustomEvent<string>} CardDeleteEvent */
/**
* Task Card Component
* @type {(props: { task: Task, onDragable: (id: number) => ddeElementAddon<HTMLDivElement> }) => HTMLElement}
* @fires {CardEditEvent} card:edit
* @fires {CardDeleteEvent} card:delete
* */
function TaskCard({ task, onDragable }){
const { host }= scope;
const isEditing = S(false);
const onEditStart = () => isEditing.set(true);
const dispatchEdit= dispatchEvent("card:edit", host);
const dispatchDelete= dispatchEvent("card:delete", host).bind(null, task.id);
return el("div", {
id: `task-${task.id}`,
className: `task-card priority-${task.priority}`,
draggable: true
}, onDragable(task.id)).append(
S.el(isEditing, editing => editing
? el(EditMode)
: el().append(
el("div", { className: "task-header" }).append(
el("h3", { className: "task-title", textContent: task.title }),
el("div", { className: "task-actions" }).append(
el("button", {
textContent: "✎",
className: "edit-btn",
ariaLabel: "Edit task"
}, on("click", onEditStart)),
el("button", {
textContent: "✕",
className: "delete-btn",
ariaLabel: "Delete task"
}, on("click", dispatchDelete))
)
),
!task.description
? el()
: el("p", { className: "task-description", textContent: task.description }),
el("div", { className: "task-meta" }).append(
el("span", {
className: `priority-badge priority-${task.priority}`,
textContent: task.priority.charAt(0).toUpperCase() + task.priority.slice(1)
})
)
)
)
);
function EditMode(){
const onSubmit = on("submit", e => {
e.preventDefault();
const formData = new FormData(/** @type {HTMLFormElement} */(e.target));
const title = formData.get("title");
const description = formData.get("description");
const priority = formData.get("priority");
isEditing.set(false);
dispatchEdit([ task.id, { title, description, priority } ]);
})
const onEditCancel = () => isEditing.set(false);
return el("form", { className: "task-edit-form" }, onSubmit).append(
el("input", {
name: "title",
className: "task-title-input",
defaultValue: task.title,
placeholder: "Task title",
required: true,
autoFocus: true
}),
el("textarea", {
name: "description",
className: "task-desc-input",
defaultValue: task.description,
placeholder: "Description (optional)"
}),
el("select", {
name: "priority",
}, on.defer(el=> el.value = task.priority)).append(
el("option", { value: "low", textContent: "Low Priority" }),
el("option", { value: "medium", textContent: "Medium Priority" }),
el("option", { value: "high", textContent: "High Priority" })
),
el("div", { className: "task-edit-actions" }).append(
el("button", {
textContent: "Cancel",
type: "button",
className: "cancel-btn"
}, on("click", onEditCancel)),
el("button", {
textContent: "Save",
type: "submit",
className: "save-btn"
})
)
);
}
}
/**
* Helper function to handle move an element
* @param {(id: string, status: string) => void} onMoved
* */
function moveElementAddon(onMoved){
let draggedTaskId = null;
function onDragable(id) {
return element => {
on("dragstart", e => {
draggedTaskId= id;
e.dataTransfer.effectAllowed = 'move';
// Add some styling to the element being dragged
setTimeout(() => {
const el = document.getElementById(`task-${id}`);
if (el) el.classList.add('dragging');
}, 0);
})(element);
on("dragend", () => {
draggedTaskId= null;
// Remove the styling
const el = document.getElementById(`task-${id}`);
if (el) el.classList.remove('dragging');
})(element);
};
}
function onDragArea(status) {
return element => {
on("dragover", e => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
// Add a visual indicator for the drop target
const column = document.getElementById(`column-${status}`);
if (column) column.classList.add('drag-over');
})(element);
on("dragleave", () => {
// Remove the visual indicator
const column = document.getElementById(`column-${status}`);
if (column) column.classList.remove('drag-over');
})(element);
on("drop", e => {
e.preventDefault();
const id = draggedTaskId;
if (id) onMoved(id, status);
// Remove the visual indicator
const column = document.getElementById(`column-${status}`);
if (column) column.classList.remove('drag-over');
})(element);
};
}
return { onDragable, onDragArea };
}
// Render the component
document.body.append(
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
el(TaskManager)
),
el("style", `
.task-manager {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 1rem;
color: #333;
}
.app-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
flex-wrap: wrap;
gap: 1rem;
}
.app-header h1 {
margin: 0;
color: #2d3748;
}
.app-controls {
display: flex;
gap: 1rem;
}
.app-controls input,
.app-controls select {
padding: 0.5rem;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 0.9rem;
}
.new-task-form {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
margin-bottom: 2rem;
}
.form-row {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
.form-row input {
flex-grow: 1;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 1rem;
}
.form-row select {
width: 100px;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 1rem;
}
.add-btn {
background: #4a90e2;
color: white;
border: none;
border-radius: 4px;
padding: 0.75rem 1.5rem;
font-size: 1rem;
cursor: pointer;
transition: background 0.2s ease;
}
.add-btn:hover {
background: #3a7bc8;
}
.new-task-form textarea {
width: 100%;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 1rem;
resize: vertical;
min-height: 80px;
}
.task-board {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1.5rem;
}
.task-column {
background: #f7fafc;
border-radius: 8px;
padding: 1rem;
min-height: 400px;
transition: background 0.2s ease;
}
.column-header {
margin-top: 0;
padding-bottom: 0.75rem;
border-bottom: 2px solid #e2e8f0;
font-size: 1.25rem;
color: #2d3748;
display: flex;
align-items: center;
}
.task-count {
display: inline-flex;
justify-content: center;
align-items: center;
background: #e2e8f0;
color: #4a5568;
border-radius: 50%;
width: 25px;
height: 25px;
font-size: 0.875rem;
margin-left: 0.5rem;
}
.column-tasks {
margin-top: 1rem;
display: flex;
flex-direction: column;
gap: 1rem;
min-height: 200px;
}
.task-card {
background: white;
border-radius: 6px;
padding: 1rem;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
cursor: grab;
transition: transform 0.2s ease, box-shadow 0.2s ease;
position: relative;
border-left: 4px solid #ccc;
}
.task-card:hover {
transform: translateY(-3px);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
}
.task-card.dragging {
opacity: 0.5;
cursor: grabbing;
}
.task-card.priority-low {
border-left-color: #38b2ac;
}
.task-card.priority-medium {
border-left-color: #ecc94b;
}
.task-card.priority-high {
border-left-color: #e53e3e;
}
.task-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 0.5rem;
}
.task-title {
margin: 0;
font-size: 1.1rem;
color: #2d3748;
word-break: break-word;
}
.task-description {
margin: 0.5rem 0;
font-size: 0.9rem;
color: #4a5568;
word-break: break-word;
}
.task-actions {
display: flex;
gap: 0.5rem;
}
.edit-btn,
.delete-btn {
background: none;
border: none;
font-size: 1rem;
cursor: pointer;
width: 24px;
height: 24px;
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 50%;
color: #718096;
transition: background 0.2s ease, color 0.2s ease;
}
.edit-btn:hover {
background: #edf2f7;
color: #4a5568;
}
.delete-btn:hover {
background: #fed7d7;
color: #e53e3e;
}
.task-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 0.75rem;
}
.priority-badge {
font-size: 0.75rem;
padding: 0.2rem 0.5rem;
border-radius: 4px;
font-weight: 500;
}
.priority-badge.priority-low {
background: #e6fffa;
color: #2c7a7b;
}
.priority-badge.priority-medium {
background: #fefcbf;
color: #975a16;
}
.priority-badge.priority-high {
background: #fed7d7;
color: #c53030;
}
.drag-over {
background: #f0f9ff;
border: 2px dashed #4a90e2;
}
.task-edit-form {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.task-title-input,
.task-desc-input {
width: 100%;
padding: 0.5rem;
border: 1px solid #e2e8f0;
border-radius: 4px;
font-size: 0.9rem;
}
.task-desc-input {
min-height: 60px;
resize: vertical;
}
.task-edit-actions {
display: flex;
justify-content: flex-end;
gap: 0.5rem;
margin-top: 0.5rem;
}
.cancel-btn,
.save-btn {
padding: 0.4rem 0.75rem;
border-radius: 4px;
font-size: 0.9rem;
cursor: pointer;
}
.cancel-btn {
background: #edf2f7;
color: #4a5568;
border: 1px solid #e2e8f0;
}
.save-btn {
background: #4a90e2;
color: white;
border: none;
}
@media (max-width: 768px) {
.app-header {
flex-direction: column;
align-items: flex-start;
}
.app-controls {
width: 100%;
flex-direction: column;
}
.form-row {
flex-direction: column;
}
.task-board {
grid-template-columns: 1fr;
}
}
`)
);

View File

@ -8,8 +8,7 @@ export class HTMLCustomElement extends HTMLElement{
connectedCallback(){
customElementRender(
this.attachShadow({ mode: "open" }),
ddeComponent,
this
ddeComponent
);
}
set attr(value){ this.setAttribute("attr", value); }

View File

@ -2,6 +2,7 @@
import {
customElementRender,
customElementWithDDE,
observedAttributes,
} from "deka-dom-el";
/** @type {ddePublicElementTagNameMap} */
import { S } from "deka-dom-el/signals";

View File

@ -9,7 +9,7 @@ export class HTMLCustomElement extends HTMLElement{
// nice place to render custom element
}
attributeChangedCallback(name, oldValue, newValue){
// listen to attribute changes (see `S.observedAttributes`)
// listen to attribute changes (see `observedAttributes`)
}
disconnectedCallback(){
// nice place to clean up

View File

@ -1,6 +1,7 @@
import {
customElementRender,
customElementWithDDE,
observedAttributes,
el, on, scope,
} from "deka-dom-el";
import { S } from "deka-dom-el/signals";
@ -8,6 +9,7 @@ export class HTMLCustomElement extends HTMLElement{
static tagName= "custom-element";
static observedAttributes= [ "attr" ];
connectedCallback(){
console.log(observedAttributes(this));
customElementRender(
this.attachShadow({ mode: "open" }),
ddeComponent,

View File

@ -1,6 +1,5 @@
import { S } from "deka-dom-el/signals";
// ===== Approach 1: Traditional debouncing with utility function =====
// Debouncing signal updates
function debounce(func, wait) {
let timeout;
return (...args)=> {
@ -9,59 +8,8 @@ function debounce(func, wait) {
};
}
const inputSignal = S("");
const debouncedSet = debounce(value => inputSignal.set(value), 300);
const inputSignal= S("");
const debouncedSet= debounce(value => inputSignal.set(value), 300);
// In your input handler
inputElement.addEventListener("input", e => debouncedSet(e.target.value));
// ===== Approach 2: Signal debouncing utility =====
/**
* Creates a debounced signal that only updates after delay
* @param {any} initialValue Initial signal value
* @param {number} delay Debounce delay in ms
*/
function createDebouncedSignal(initialValue, delay = 300) {
// Create two signals: one for immediate updates, one for debounced values
const immediateSignal = S(initialValue);
const debouncedSignal = S(initialValue);
// Keep track of the timeout
let timeout = null;
// Set up a listener on the immediate signal
S.on(immediateSignal, value => {
// Clear any existing timeout
if (timeout) clearTimeout(timeout);
// Set a new timeout to update the debounced signal
timeout = setTimeout(() => {
debouncedSignal.set(value);
}, delay);
});
// Return an object with both signals and a setter function
return {
// The raw signal that updates immediately
raw: immediateSignal,
// The debounced signal that only updates after delay
debounced: debouncedSignal,
// Setter function to update the immediate signal
set: value => immediateSignal.set(value)
};
}
// Usage example
const searchInput = createDebouncedSignal("", 300);
// Log immediate changes for demonstration
S.on(searchInput.raw, value => console.log("Input changed to:", value));
// Only perform expensive operations on the debounced value
S.on(searchInput.debounced, value => {
console.log("Performing search with:", value);
// Expensive operation would go here
});
// In your input handler
searchElement.addEventListener("input", e => searchInput.set(e.target.value));
inputElement.addEventListener("input", e=> debouncedSet(e.target.value));

View File

@ -1,4 +1,4 @@
// Example of reactive element marker
<!--<dde:mark type="reactive" component="<component-name>">-->
<!--<dde:mark type=\"reactive\" source=\"...\">-->
<!-- content that updates when signal changes -->
<!--</dde:mark>-->

View File

@ -4,11 +4,11 @@ import { el } from "deka-dom-el";
const button = el("button", {
textContent: "Click me",
className: "primary",
disabled: true,
disabled: true
});
// Shorter and more expressive
// than the native approach
// Add to DOM
document.body.append(button);
document.body.append(button);

View File

@ -6,6 +6,6 @@ import { el } from "deka-dom-el";
document.body.append(
el("div").append(
el("h1", "Title"),
el("p", "Paragraph"),
),
el("p", "Paragraph")
)
);

View File

@ -24,11 +24,7 @@ document.body.append(
);
import { chainableAppend } from "deka-dom-el";
/**
* @template {keyof HTMLElementTagNameMap} TAG
* @param {TAG} tag
* @returns {ddeHTMLElementTagNameMap[TAG] extends HTMLElement ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement}
* */
/** @param {keyof HTMLElementTagNameMap} tag */
const createElement= tag=> chainableAppend(document.createElement(tag));
document.body.append(
createElement("p").append(

View File

@ -8,12 +8,12 @@ button.disabled = true;
const button2 = Object.assign(
document.createElement('button'),
{
textContent: "Click me",
className: "primary",
disabled: true
textContent: "Click me",
className: "primary",
disabled: true
}
);
// Add to DOM
document.body.append(button);
document.body.append(button2);
document.body.appendChild(button);
document.body.appendChild(button2);

View File

@ -2,14 +2,14 @@
const div = document.createElement('div');
const h1 = document.createElement('h1');
h1.textContent = 'Title';
div.append(h1);
div.appendChild(h1);
const p = document.createElement('p');
p.textContent = 'Paragraph';
div.append(p);
div.appendChild(p);
// append doesn't return parent
// appendChild doesn't return parent
// so chaining is not possible
// Add to DOM
document.body.append(div);
document.body.appendChild(div);

View File

@ -1,19 +0,0 @@
import { el, on, dispatchEvent, scope } from "deka-dom-el";
document.body.append(
el(component),
);
function component(){
const { host }= scope;
const dispatchExample= dispatchEvent(
"example",
{ bubbles: true },
host
);
return el("div").append(
el("p", "Dispatch events from outside of the component."),
el("button", { textContent: "Dispatch", type: "button" },
on("click", dispatchExample))
);
}

View File

@ -1,28 +1,15 @@
import { el, on } from "deka-dom-el";
function allLifecycleEvents(){
return el("form", null,
el=> log({ type: "dde:created", detail: el }),
on.connected(log),
on.disconnected(log),
).append(
el("select", { id: "country" }, on.defer(select => {
// This runs when the select is ready with all its options
select.value = "cz"; // Pre-select Czechia
log({ type: "dde:on.defer", detail: select });
})).append(
el("option", { value: "au", textContent: "Australia" }),
el("option", { value: "ca", textContent: "Canada" }),
el("option", { value: "cz", textContent: "Czechia" }),
),
el("p", "See lifecycle events in console."),
);
}
const paragraph= el("p", "See live-cycle events in console.",
el=> log({ type: "dde:created", detail: el }),
on.connected(log),
on.disconnected(log),
on.attributeChanged(log));
document.body.append(
el(allLifecycleEvents),
el("button", "Remove Element", on("click", function(){
this.previousSibling.remove();
}))
paragraph,
el("button", "Update attribute", on("click", ()=> paragraph.setAttribute("test", Math.random().toString()))),
" ",
el("button", "Remove", on("click", ()=> paragraph.remove()))
);
/** @param {Partial<CustomEvent>} event */

View File

@ -6,8 +6,9 @@ let count = 0;
button.addEventListener('click', () => {
count++;
document.querySelector('p').textContent =
'Clicked ' + count + ' times';
'Clicked ' + count + ' times';
if (count > 10)
button.disabled = true;
if (count > 10) {
button.disabled = true;
}
});

View File

@ -1,4 +1,4 @@
import { el } from "deka-dom-el";
import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
// A HelloWorld component using the 3PS pattern
@ -7,24 +7,24 @@ function HelloWorld({ emoji = "🚀" }) {
const clicks = S(0);
return el().append(
// PART 2: Bind state to UI elements
el("p", {
className: "greeting",
// This paragraph automatically updates when clicks changes
textContent: S(() => `Hello World ${emoji.repeat(clicks.get())}`)
}),
// PART 2: Bind state to UI elements
el("p", {
className: "greeting",
// This paragraph automatically updates when clicks changes
textContent: S(() => `Hello World ${emoji.repeat(clicks.get())}`)
}),
// PART 3: Update state in response to events
el("button", {
type: "button",
textContent: "Add emoji",
// When clicked, update the state
onclick: () => clicks.set(clicks.get() + 1)
})
// PART 3: Update state in response to events
el("button", {
type: "button",
textContent: "Add emoji",
// When clicked, update the state
onclick: () => clicks.set(clicks.get() + 1)
})
);
}
// Use the component in your app
document.body.append(
el(HelloWorld, { emoji: "🎉" })
);
);

View File

@ -15,15 +15,15 @@ function HelloWorldComponent({ initial }){
return el().append(
el("p", {
textContent: S(() => `Hello World ${emoji.get().repeat(clicks.get())}`),
textContent: S(() => `Hello World ${emoji().repeat(clicks())}`),
className: "example",
ariaLive: "polite", //OR ariaset: { live: "polite" },
dataset: { example: "Example" }, //OR dataExample: "Example",
}),
el("button",
{ textContent: "Fire", type: "button" },
on("click", ()=> clicks.set(clicks.get() + 1)),
on("keyup", ()=> clicks.set(clicks.get() - 2)),
on("click", ()=> clicks(clicks() + 1)),
on("keyup", ()=> clicks(clicks() - 2)),
),
el("select", null, onChange).append(
el(OptionComponent, "🎉", isSelected),//OR { textContent: "🎉" }

View File

@ -9,7 +9,7 @@ document.body.append(
padding: 1em;
margin: 1em;
}
`.trim())
`.trim())
);
export function CounterStandard() {

View File

@ -1,2 +0,0 @@
// use NPM or for example https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.js
import { memo } from "deka-dom-el";

View File

@ -1,115 +0,0 @@
// Example of how memoization improves performance with list rendering
import { el, on, memo } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
// A utility to log element creation
function logCreation(name) {
console.log(`Creating ${name} element`);
return name;
}
// Create a signal with our items
const itemsSignal = S([
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
{ id: 3, name: "Item 3" }
], {
add() {
const { length }= this.value;
this.value.push({
id: length + 1,
name: `Item ${length + 1}`
});
},
force(){},
});
// Without memoization - creates new elements on every render
function withoutMemo() {
return el("div").append(
el("h3", "Without Memoization (check console for element creation)"),
el("p", "Elements are recreated on every render"),
S.el(itemsSignal, items =>
el("ul").append(
...items.map(item =>
el("li").append(
el("span", logCreation(item.name))
)
)
)
),
);
}
// With memoization - reuses elements when possible
function withMemo() {
return el("div").append(
el("h3", "With Memoization (check console for element creation)"),
el("p", "Elements are reused when the key (item.id) stays the same"),
S.el(itemsSignal, items =>
el("ul").append(
...items.map(item =>
// Use item.id as a stable key for memoization
memo(item.id, () =>
el("li").append(
el("span", logCreation(item.name))
)
)
)
)
),
);
}
// Using memo.scope for a custom memoized function
const renderMemoList = memo.scope(function(items) {
return el("ul").append(
...items.map(item =>
memo(item.id, () =>
el("li").append(
el("span", logCreation(`Custom memo: ${item.name}`))
)
)
)
);
});
function withCustomMemo() {
return el("div").append(
el("h3", "With Custom Memo Function"),
el("p", "Using memo.scope to create a memoized rendering function"),
S.el(itemsSignal, items =>
renderMemoList(items)
),
el("button", "Clear Cache",
on("click", () => {
renderMemoList.clear();
S.action(itemsSignal, "force");
}
)
)
);
}
// Demo component showing the difference
export function MemoDemo() {
return el("div", { style: "padding: 1em; border: 1px solid #ccc;" }).append(
el("h2", "Memoization Demo"),
el("p", "See in the console when elements are created."),
el("p").append(`
Notice that without memoization, elements are recreated on every render. With memoization,
only new elements are created.
`),
el("button", "Add Item",
on("click", () => S.action(itemsSignal, "add"))
),
el("div", { style: "display: flex; gap: 2em; margin-top: 1em;" }).append(
withoutMemo(),
withMemo(),
withCustomMemo()
)
);
}
document.body.append(el(MemoDemo));

View File

@ -1,381 +0,0 @@
import { dispatchEvent, el, memo, on, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
/**
* Main TodoMVC application component
*
* Creates and manages the TodoMVC application with the following features:
* - Todo items management (add, edit, delete)
* - Filtering by status (all, active, completed)
* - Client-side routing via URL hash
* - Persistent storage with localStorage
*
* @returns {HTMLElement} The root TodoMVC application element
*/
function Todos(){
const { signal } = scope;
const pageS = routerSignal(S, signal);
const todosS = todosSignal();
/** Derived signal that filters todos based on current route */
const todosFilteredS = S(()=> {
const todos = todosS.get();
const filter = pageS.get();
if (filter === "all") return todos;
return todos.filter(todo => {
if (filter === "active") return !todo.completed;
if (filter === "completed") return todo.completed;
});
});
const todosRemainingS = S(()=> todosS.get().filter(todo => !todo.completed).length);
/** @type {ddeElementAddon<HTMLInputElement>} */
const onToggleAll = on("change", event => {
const checked = /** @type {HTMLInputElement} */ (event.target).checked;
S.action(todosS, "completeAll", checked);
});
const formNewTodo = "newTodo";
/** @type {ddeElementAddon<HTMLFormElement>} */
const onSubmitNewTodo = on("submit", event => {
event.preventDefault();
const input = /** @type {HTMLInputElement} */(
/** @type {HTMLFormElement} */(event.target).elements.namedItem(formNewTodo)
);
const title = input.value.trim();
if (!title) return;
S.action(todosS, "add", title);
input.value = "";
});
const onClearCompleted = on("click", () => S.action(todosS, "clearCompleted"));
const onDelete = on("todo:delete", ev =>
S.action(todosS, "delete", /** @type {{ detail: Todo["id"] }} */(ev).detail));
const onEdit = on("todo:edit", ev =>
S.action(todosS, "edit", /** @type {{ detail: Partial<Todo> & { id: Todo["id"] } }} */(ev).detail));
return el("section", { className: "todoapp" }).append(
el("header", { className: "header" }).append(
el("h1", "todos"),
el("form", null, onSubmitNewTodo).append(
el("input", {
className: "new-todo",
name: formNewTodo,
placeholder: "What needs to be done?",
autocomplete: "off",
autofocus: true
})
)
),
S.el(todosS, todos => !todos.length
? el()
: el("main", { className: "main" }).append(
el("input", {
id: "toggle-all",
className: "toggle-all",
type: "checkbox"
}, onToggleAll),
el("label", { htmlFor: "toggle-all", title: "Mark all as complete" }),
el("ul", { className: "todo-list" }).append(
S.el(todosFilteredS, filteredTodos => filteredTodos.map(todo =>
memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
)
)
)
),
S.el(todosS, ({ length }) => !length
? el()
: el("footer", { className: "footer" }).append(
el("span", { className: "todo-count" }).append(
el("strong", length + " " + (length === 1 ? "item" : "items")),
),
memo("filters", ()=>
el("ul", { className: "filters" }).append(
...[ "All", "Active", "Completed" ].map(textContent =>
el("li").append(
el("a", {
textContent,
classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
href: `#${textContent.toLowerCase()}`
})
)
)
),
),
length - todosRemainingS.get() === 0
? el()
: memo("delete", () =>
el("button",
{ textContent: "Clear completed", className: "clear-completed" },
onClearCompleted)
)
)
)
);
}
/**
* Todo item data structure
* @typedef {{ title: string, id: string, completed: boolean }} Todo
*/
/**
* Component for rendering an individual todo item
*
* Features:
* - Display todo with completed state
* - Toggle completion status
* - Delete todo
* - Edit todo with double-click
* - Cancel edit with Escape key
*
* @param {Todo} todo - The todo item data
* @fires {void} todo:delete - todo deletion event
* @fires {Partial<Todo>} todo:edit - todo edits event
*/
function TodoItem({ id, title, completed }) {
const { host }= scope;
const isEditing = S(false);
const isCompleted = S(completed);
/** @type {(id: string) => void} Dispatch function for deleting todo */
const dispatchDelete= dispatchEvent("todo:delete", host);
/** @type {(data: {id: string, [key: string]: any}) => void} Dispatch function for editing todo */
const dispatchEdit = dispatchEvent("todo:edit", host);
/** @type {ddeElementAddon<HTMLInputElement>} */
const onToggleCompleted = on("change", (ev) => {
const completed= /** @type {HTMLInputElement} */(ev.target).checked;
isCompleted.set(completed);
dispatchEdit({ id, completed });
});
/** @type {ddeElementAddon<HTMLButtonElement>} */
const onDelete = on("click", () => dispatchDelete(id));
/** @type {ddeElementAddon<HTMLLabelElement>} */
const onStartEdit = on("dblclick", () => isEditing.set(true));
/** @type {ddeElementAddon<HTMLInputElement>} */
const onBlurEdit = on("blur", event => {
const value = /** @type {HTMLInputElement} */(event.target).value.trim();
if (value) {
dispatchEdit({ id, title: value });
} else {
dispatchDelete(id);
}
isEditing.set(false);
});
const formEdit = "edit";
/** @type {ddeElementAddon<HTMLFormElement>} */
const onSubmitEdit = on("submit", event => {
event.preventDefault();
const input = /** @type {HTMLFormElement} */(event.target).elements.namedItem(formEdit);
const value = /** @type {HTMLInputElement} */(input).value.trim();
if (value) {
dispatchEdit({ id, title: value });
} else {
dispatchDelete(id);
}
isEditing.set(false);
});
/**
* Event handler for keyboard events in edit mode
* @type {ddeElementAddon<HTMLInputElement>}
*/
const onKeyDown = on("keydown", event => {
if (event.key !== "Escape") return;
isEditing.set(false);
});
return el("li", { classList: { completed: isCompleted, editing: isEditing } }).append(
el("div", { className: "view" }).append(
el("input", {
className: "toggle",
type: "checkbox",
checked: completed
}, onToggleCompleted),
el("label", { textContent: title }, onStartEdit),
el("button", { ariaLabel: "Delete todo", className: "destroy" }, onDelete)
),
S.el(isEditing, editing => !editing
? el()
: el("form", null, onSubmitEdit).append(
el("input", {
className: "edit",
name: formEdit,
value: title,
}, onBlurEdit, onKeyDown, addFocus)
)
)
);
}
// Set up the document head
document.head.append(
el("title", "TodoMVC: dd<el>"),
el("meta", { name: "description", content: "A TodoMVC implementation using dd<el>." }),
el("link", {
rel: "stylesheet",
href: "https://cdn.jsdelivr.net/npm/todomvc-common@1.0.5/base.css"
}),
el("link", {
rel: "stylesheet",
href: "https://cdn.jsdelivr.net/npm/todomvc-app-css@2.4.2/index.css"
})
);
// Set up the document body
document.body.append(
el(Todos),
el("footer", { className: "info" }).append(
el("p", "Double-click to edit a todo"),
el("p").append(
"Created with ",
el("a", { textContent: "deka-dom-el", href: "https://github.com/jaandrle/deka-dom-el" })
),
el("p").append(
"Part of ",
el("a", { textContent: "TodoMVC", href: "http://todomvc.com" })
)
)
);
/**
* Utility function to set focus on an input element
* Uses requestAnimationFrame to ensure the element is rendered
* before trying to focus it
*
* @param {HTMLInputElement} editInput - The input element to focus
* @returns {number} The requestAnimationFrame ID
*/
function addFocus(editInput){
return requestAnimationFrame(()=> {
editInput.focus();
editInput.selectionStart = editInput.selectionEnd = editInput.value.length;
});
}
/**
* Creates a signal for managing todos with persistence
*
* Features:
* - Loads todos from localStorage on initialization
* - Automatically saves todos to localStorage on changes
* - Provides actions for adding, editing, deleting todos
*/
function todosSignal(){
const store_key = "dde-todos";
// Try to load todos from localStorage
let savedTodos = [];
try {
const stored = localStorage.getItem(store_key);
if (stored) {
savedTodos = JSON.parse(stored);
}
} catch (_) {}
const out= S(/** @type {Todo[]} */(savedTodos || []), {
/**
* Add a new todo
* @param {string} value - The title of the new todo
*/
add(value){
this.value.push({
completed: false,
title: value,
id: uuid(),
});
},
/**
* Edit an existing todo
* @param {{ id: string, [key: string]: any }} data - Object containing id and fields to update
*/
edit({ id, ...update }){
const index = this.value.findIndex(t => t.id === id);
if (index === -1) return this.stopPropagation();
Object.assign(this.value[index], update);
},
/**
* Delete a todo by id
* @param {string} id - The id of the todo to delete
*/
delete(id){
const index = this.value.findIndex(t => t.id === id);
if (index === -1) return this.stopPropagation();
this.value.splice(index, 1);
},
/**
* Remove all completed todos
*/
clearCompleted() {
this.value = this.value.filter(todo => !todo.completed);
},
completeAll(state= true) {
this.value.forEach(todo => todo.completed = state);
},
/**
* Handle cleanup when signal is cleared
*/
[S.symbols.onclear](){
}
});
/**
* Save todos to localStorage whenever the signal changes
* @param {Todo[]} value - Current todos array
*/
S.on(out, /** @param {Todo[]} value */ function saveTodos(value) {
try {
localStorage.setItem(store_key, JSON.stringify(value));
} catch (e) {
console.error("Failed to save todos to localStorage", e);
// Optionally, provide user feedback
}
});
return out;
}
/**
* Creates a signal for managing route state
*
* @param {typeof S} signal - The signal constructor from a library
* @param {AbortSignal} abortSignal
*/
function routerSignal(signal, abortSignal){
const initial = location.hash.replace("#", "") || "all";
const out = signal(initial, {
/**
* Set the current route
* @param {"all"|"active"|"completed"} hash - The route to set
*/
set(hash){
location.hash = hash;
//this.value = hash;
},
});
// Setup hash change listener
window.addEventListener("hashchange", () => {
const hash = location.hash.replace("#", "") || "all";
//S.action(out, "set", /** @type {"all"|"active"|"completed"} */(hash));
out.set(hash);
}, { signal: abortSignal });
return out;
}
/**
* Generates a RFC4122 version 4 compliant UUID
* Used to create unique identifiers for todo items
*
* @returns {string} A randomly generated UUID
*/
function uuid() {
let uuid = "";
for (let i = 0; i < 32; i++) {
let random = (Math.random() * 16) | 0;
if (i === 8 || i === 12 || i === 16 || i === 20)
uuid += "-";
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16);
}
return uuid;
}

View File

@ -14,7 +14,7 @@ function component(){
const textContent= S("Click to change text.");
const onclickChange= on("click", function redispatch(){
textContent.set("Text changed! "+(new Date()).toString())
textContent("Text changed! "+(new Date()).toString())
});
return el("p", textContent, onclickChange);
}

View File

@ -0,0 +1,14 @@
import { scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function customSignalLogic() {
// Create an isolated scope for a specific operation
scope.push(); // Start new scope
// These signals are in the new scope
const isolatedCount = S(0);
const isolatedDerived = S(() => isolatedCount.get() * 2);
// Clean up by returning to previous scope
scope.pop();
}

View File

@ -16,9 +16,7 @@ function Counter() {
// THE HOST IS PROBABLY DIFFERENT THAN
// YOU EXPECT AND SIGNAL MAY BE
// UNEXPECTEDLY REMOVED!!!
S.on(count, (count)=>
host().querySelector("button").disabled = count >= 10
);
host().querySelector("button").disabled = count.get() >= 10;
};
setTimeout(()=> {
// ok, BUT consider extract to separate function

View File

@ -7,7 +7,7 @@ const todos= S([], {
const removed= this.value.pop();
if(removed) S.clear(removed);
},
[S.symbols.onclear](){ // this covers `S.clear(todos)`
[S.symbols.onclear](){ // this covers `O.clear(todos)`
S.clear(...this.value);
}
});

View File

@ -1,5 +1,6 @@
// Handling async data in SSR
import { JSDOM } from "jsdom";
import { S } from "deka-dom-el/signals";
import { register, queue } from "deka-dom-el/jsdom";
async function renderWithAsyncData() {
@ -7,7 +8,23 @@ async function renderWithAsyncData() {
const { el } = await register(dom);
// Create a component that fetches data
const { AsyncComponent } = await import("./components/AsyncComponent.js");
function AsyncComponent() {
const title= S("-");
const description= S("-");
// Use the queue to track the async operation
queue(fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => {
title.set(data.title);
description.set(data.description);
}));
return el("div", { className: "async-content" }).append(
el("h2", title),
el("p", description)
);
}
// Render the page
dom.window.document.body.append(
@ -24,24 +41,3 @@ async function renderWithAsyncData() {
}
renderWithAsyncData();
// file: components/AsyncComponent.js
import { el } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function AsyncComponent() {
const title= S("-");
const description= S("-");
// Use the queue to track the async operation
queue(fetch("https://api.example.com/data")
.then(response => response.json())
.then(data => {
title.set(data.title);
description.set(data.description);
}));
return el("div", { className: "async-content" }).append(
el("h2", title),
el("p", description)
);
}

View File

@ -11,7 +11,6 @@ async function renderPage() {
const { el } = await register(dom);
// Create a simple header component
// can be separated into a separate file and use `import { el } from "deka-dom-el"`
function Header({ title }) {
return el("header").append(
el("h1", title),

View File

@ -17,7 +17,6 @@ async function renderPage() {
const { el } = await register(dom);
// 4. Dynamically import page components
// use `import { el } from "deka-dom-el"`
const { Header } = await import("./components/Header.js");
const { Content } = await import("./components/Content.js");

View File

@ -1,6 +1,6 @@
// Basic jsdom integration example
import { JSDOM } from "jsdom";
import { register, unregister, queue } from "deka-dom-el/jsdom";
import { register, unregister, queue } from "deka-dom-el/jsdom.js";
// Create a jsdom instance
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");

View File

@ -12,7 +12,7 @@ async function buildSite() {
];
// Create output directory
mkdirSync("./dist/docs", { recursive: true });
mkdirSync("./dist", { recursive: true });
// Build each page
for (const page of pages) {
@ -23,7 +23,6 @@ async function buildSite() {
const { el } = await register(dom);
// Import the page component
// use `import { el } from "deka-dom-el"`
const { default: PageComponent } = await import(page.component);
// Render the page with its metadata
@ -36,7 +35,7 @@ async function buildSite() {
// Write the HTML to a file
const html = dom.serialize();
writeFileSync(`./dist/docs/${page.id}.html`, html);
writeFileSync(`./dist/${page.id}.html`, html);
console.log(`Built page: ${page.id}.html`);
}

View File

@ -1,83 +0,0 @@
import { styles, page_id } from "../ssr.js";
styles.css`
#library-url-form {
display: flex;
flex-flow: column nowrap;
gap: 1rem;
padding: 1.5rem;
border-radius: var(--border-radius);
background-color: var(--bg-sidebar);
box-shadow: var(--shadow);
border: 1px solid var(--border);
margin: 1.5rem 0;
}
#library-url-form .selectors {
display: flex;
flex-flow: row wrap;
gap: 0.75rem;
}
#library-url-form output {
display: flex;
flex-flow: column nowrap;
gap: 0.75rem;
margin-top: 0.5rem;
}
#library-url-form output p {
font-weight: 500;
margin: 0.25rem 0;
color: var(--text-light);
}
#library-url-form .url-title {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: -0.25rem;
}
#library-url-form .url-title strong {
font-family: var(--font-mono);
font-size: 0.95rem;
}
#library-url-form .url-title span {
color: var(--text-light);
font-size: 0.9rem;
}
#library-url-form .code {
margin-bottom: 1rem;
}
#library-url-form .info-text {
font-size: 0.9rem;
font-style: italic;
margin-top: 1rem;
color: var(--text-light);
}
@media (max-width: 768px) {
#library-url-form .selectors {
flex-direction: column;
}
#library-url-form select {
width: 100%;
}
}
`;
import { el } from "deka-dom-el";
import { ireland } from "./ireland.html.js";
export function getLibraryUrl(){
return el(ireland, {
src: new URL("./getLibraryUrl.js.js", import.meta.url),
exportName: "getLibraryUrl",
page_id,
});
}

View File

@ -1,92 +0,0 @@
import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
const url_base= {
jsdeka: "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/",
};
export function getLibraryUrl(){
const lib= S([ "esm", "-with-signals", ".min" ]);
const url= S(()=> url_base.jsdeka+lib.get().join(""));
const urlLabel= S(() => {
const [format, signalsPart, minified] = lib.get();
const formatText = format === "esm" ? "ES Module" : "IIFE";
const signalsText = signalsPart ? " with signals" : "";
const minText = minified ? " (minified)" : "";
return `${formatText}${signalsText}${minText}`;
})
const onSubmit= on("submit", ev => {
ev.preventDefault();
const form= new FormData(/** @type {HTMLFormElement} */ (ev.target));
lib.set([
"module",
"what",
"minified",
].map(name => /** @type {string} */(form.get(name))));
});
const onChangeSubmit= on("change",
ev=> /** @type {HTMLSelectElement} */(ev.target).form.requestSubmit()
);
return el("form", { id: "library-url-form" }, onSubmit).append(
el("h4", "Select your preferred library format:"),
el("div", { className: "selectors" }).append(
el("select", { name: "module" }, onChangeSubmit,
on.defer(select => select.value = lib.get()[0]),
).append(
el("option", { value: "esm", textContent: "ESM — modern JavaScript module" }),
el("option", { value: "iife", textContent: "IIFE — legacy JavaScript with DDE global variable" }),
),
el("select", { name: "what" }, onChangeSubmit,
on.defer(select => select.value = lib.get()[1]),
).append(
el("option", { value: "", textContent: "DOM part only" }),
el("option", { value: "-with-signals", textContent: "DOM + signals" }),
),
el("select", { name: "minified" }, onChangeSubmit,
on.defer(select => select.value = lib.get()[2]),
).append(
el("option", { value: "", textContent: "Unminified" }),
el("option", { value: ".min", textContent: "Minified" }),
),
),
el("output").append(
el("div", { className: "url-title" }).append(
el("strong", "JavaScript:"),
el("span", urlLabel),
),
el(code, { value: S(()=> url.get()+".js") }),
el("div", { className: "url-title" }).append(
el("strong", "TypeScript definition:")
),
el(code, { value: S(()=> url.get()+".d.ts") }),
el("p", { className: "info-text",
textContent: "Use the CDN URL in your HTML or import it in your JavaScript files."
})
)
)
}
/** @param {{ value: ddeSignal<string> }} props */
function code({ value }){
/** @type {ddeSignal<"Copy"|"Copied!">} */
const textContent= S("Copy");
const onCopy= on("click", () => {
navigator.clipboard.writeText(value.get());
textContent.set("Copied!");
setTimeout(() => {
textContent.set("Copy");
}, 1500);
});
return el("div", { className: "code", dataJs: "done", tabIndex: 0 }).append(
el("code").append(
el("pre", value),
),
el("button", {
className: "copy-button",
textContent,
ariaLabel: "Copy code to clipboard",
}, onCopy)
)
;
}

View File

@ -1,33 +1,3 @@
import { styles } from "../ssr.js";
styles.css`
[data-dde-mark] {
opacity: .5;
filter: grayscale();
@media (prefers-reduced-motion: no-preference) {
animation: fadein 2s infinite ease forwards;;
}
position: relative;
&::after {
content: "Loading Ireland…";
background-color: rgba(0, 0, 0, .5);
color: white;
font-weight: bold;
border-radius: 5px;
padding: 5px 10px;
position: absolute;
top: 3%;
left: 50%;
transform: translateX(-50%);
}
}
@keyframes fadein {
from { opacity: .5; }
to { opacity: .85; }
}
`;
import { el, queue } from "deka-dom-el";
import { addEventListener, registerClientFile } from "../ssr.js";
import { relative } from "node:path";
@ -43,6 +13,7 @@ const componentsRegistry = new Map();
* @param {object} attrs
* @param {URL} attrs.src - Path to the file containing the component
* @param {string} [attrs.exportName="default"] - Name of the export to use
* @param {string} attrs.page_id - ID of the current page
* @param {object} [attrs.props={}] - Props to pass to the component
*/
export function ireland({ src, exportName = "default", props = {} }) {
@ -50,17 +21,10 @@ export function ireland({ src, exportName = "default", props = {} }) {
const path= "./"+relative(dir, src.pathname);
const id = "ireland-" + generateComponentId(src);
const element = el.mark({ type: "later", name: ireland.name });
queue(
import(path)
.then(module => {
const component = module[exportName];
const content= el(component, props, mark(id));
element.replaceWith(content);
content.querySelectorAll("input, textarea, button")
.forEach(el=> el.disabled= true);
})
.catch(console.error)
);
queue(import(path).then(module => {
const component = module[exportName];
element.replaceWith(el(component, props, mark(id)));
}));
if(!componentsRegistry.size)
addEventListener("oneachrender", registerClientPart);

View File

@ -11,6 +11,10 @@ export function mnemonic(){
el("code", "customElementWithDDE(<custom-element>)"),
" — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator",
),
el("li").append(
el("code", "observedAttributes(<custom-element>)"),
" — returns record of observed attributes (keys uses camelCase)",
),
el("li").append(
el("code", "S.observedAttributes(<custom-element>)"),
" — returns record of observed attributes (keys uses camelCase and values are signals)",
@ -28,4 +32,4 @@ export function mnemonic(){
" — simulate slots for “dde”/functional components",
),
);
}
}

View File

@ -9,7 +9,7 @@ export function mnemonic(){
),
el("li").append(
el("code", "el(<tag-name>, <primitive>)[.append(...)]: <element-from-tag-name>"),
" — simple element containing only text (accepts string, number or signal)",
" — simple element containing only text",
),
el("li").append(
el("code", "el(<tag-name>, <object>)[.append(...)]: <element-from-tag-name>"),
@ -26,6 +26,6 @@ export function mnemonic(){
el("li").append(
el("code", "elNS(<namespace>)(<as-el-see-above>)[.append(...)]: <element-based-on-arguments>"),
" — typically SVG elements",
),
)
);
}

View File

@ -12,24 +12,19 @@ export function mnemonic(){
" — corresponds to custom elemnets callbacks ", el("code", "<live-cycle>Callback(...){...}"),
". To connect to custom element see following page, else it is simulated by MutationObserver."
),
el("li").append(
el("code", "on.defer(<identity>=> <identity>)(<identity>)"),
" — calls callback later",
),
el("li").append(
el("code", "dispatchEvent(<event>[, <options>])(element)"),
" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
),
el("li").append(
el("code", "dispatchEvent(<event>, <element>)([<detail>])"),
" — just ", el("code", "<element>.dispatchEvent(new Event(<event>))"), " or ",
el("code", "<element>.dispatchEvent(new CustomEvent(<event>, { detail: <detail> }))")
),
el("li").append(
el("code", "dispatchEvent(<event>[, <options>])(<element>[, <detail>])"),
" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>] ))"), " or ",
el("code", "<element>.dispatchEvent(new CustomEvent(<event>, { detail: <detail> }))")
),
el("li").append(
el("code", "dispatchEvent(<event>[, <options>], <host>)([<detail>])"),
" — just ", el("code", "<host>().dispatchEvent(new Event(<event>[, <options>]))"), " or ",
el("code", "<host>().dispatchEvent(new CustomEvent(<event>, { detail: <detail> }[, <options>] ))"),
" (see scopes section of docs)"
),
);
}

View File

@ -1,15 +0,0 @@
import { el } from "deka-dom-el";
import { mnemonicUl } from "../mnemonicUl.html.js";
export function mnemonic(){
return mnemonicUl().append(
el("li").append(
el("code", "memo.scope(<function>, <argument(s)>)"),
" — Scope for memo",
),
el("li").append(
el("code", "memo(<key>, <generator>)"),
" — returns value from memo and/or generates it (and caches it)",
),
);
}

View File

@ -14,10 +14,6 @@ export function mnemonic(){
el("li").append(
el("code", "scope.host(...<addons>)"),
" — use addons to current component",
),
el("li").append(
el("code", "scope.signal"),
" — get AbortSignal that triggers when the element disconnects",
)
);
}

View File

@ -14,6 +14,10 @@ export function mnemonic(){
el("code", "S.on(<signal>, <listener>[, <options>])"),
" — listen to the signal value changes",
),
el("li").append(
el("code", "S.clear(...<signals>)"),
" — off and clear signals",
),
el("li").append(
el("code", "S(<value>, <actions>)"),
" — signal: pattern to create complex reactive objects/arrays",
@ -25,11 +29,6 @@ export function mnemonic(){
el("li").append(
el("code", "S.el(<signal>, <function-returning-dom>)"),
" — render partial dom structure (template) based on the current signal value",
),
el("li").append(
el("code", "S.clear(...<signals>)"),
" — off and clear signals (most of the time it is not needed as reactive ",
"attributes and elements are handled automatically)",
),
)
);
}

View File

@ -1,50 +0,0 @@
import { styles } from "../ssr.js";
styles.css`
/* Scroll to top button */
.scroll-top-button {
position: fixed;
bottom: 2rem;
left: 2rem;
display: flex;
align-items: center;
justify-content: center;
width: 3rem;
height: 3rem;
border-radius: 50%;
background-color: var(--primary);
color: var(--button-text);
font-size: 1.5rem;
text-decoration: none;
box-shadow: var(--shadow);
transition: background-color 0.2s ease, transform 0.2s ease;
z-index: 1000;
}
.scroll-top-button:hover {
background-color: var(--primary-dark);
transform: translateY(-4px);
text-decoration: none;
}
@media (max-width: 768px) {
.scroll-top-button {
bottom: 0.5rem;
left: unset;
right: .5rem;
width: 2.5rem;
height: 2.5rem;
}
}
`;
import { el } from "deka-dom-el";
import { ireland } from "./ireland.html.js";
export function scrollTop(){
return el(ireland, {
src: new URL("./scrollTop.js.js", import.meta.url),
exportName: "scrollTop",
page_id: "*",
});
}

View File

@ -1,14 +0,0 @@
import { el } from "deka-dom-el";
export function scrollTop() {
return el("a", {
href: "#",
className: "scroll-top-button",
ariaLabel: "Scroll to top",
textContent: "↑",
onclick: (e) => {
e.preventDefault();
window.scrollTo({ top: 0, behavior: "smooth" });
}
})
}

View File

@ -16,6 +16,7 @@ styles.css`
SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
--body-max-width: 40rem;
--sidebar-width: 20rem;
--header-height: 4rem;
--border-radius: 0.375rem;
@ -72,7 +73,6 @@ styles.css`
html {
scroll-behavior: smooth;
tab-size: var(--tab-size, 2rem);
}
/* Accessibility improvements */
@ -86,7 +86,7 @@ html {
}
:focus-visible {
outline: 3px solid var(--primary-light);
outline: 3px solid hsl(231, 48%, 70%);
outline-offset: 2px;
}
@ -193,34 +193,6 @@ pre code {
background-color: transparent;
padding: 0;
}
figure {
width: 100%;
text-align: center;
color: var(--text-light);
border: 1px dashed var(--border);
border-radius: var(--border-radius);
img {
object-fit: contain;
border-radius: var(--border-radius);
box-shadow: var(--shadow);
max-width: 100%;
}
}
select {
padding: 0.5rem 0.75rem;
border-radius: var(--border-radius);
border: 1px solid var(--border);
background-color: var(--bg);
color: var(--text);
cursor: pointer;
font-size: 0.95rem;
font-family: var(--font-main);
}
select:hover {
border-color: var(--primary);
}
/* Layout */
body {
@ -243,8 +215,8 @@ body {
}
@media (min-width: 768px) {
body {
grid-template-rows: auto 1fr;
grid-template-columns: auto 1fr;
grid-template-rows: var(--header-height) 1fr;
grid-template-columns: var(--sidebar-width) 1fr;
grid-template-areas:
"header header"
"sidebar content";
@ -262,7 +234,7 @@ body > main {
}
body > main > *, body > main slot > * {
width: 100%;
max-width: calc(var(--body-max-width) * 5/3);
max-width: 100%;
margin-inline: auto;
grid-column: main;
}
@ -278,7 +250,7 @@ h2 {
/* Section headings with better visual hierarchy */
body > main h3, body > main h4 {
scroll-margin-top: 1rem;
scroll-margin-top: calc(var(--header-height) + 1rem);
}
/* Make clickable heading links for better navigation */
@ -295,8 +267,9 @@ body > main h3, body > main h4 {
/* Boxes */
.illustration{
grid-column: full-main;
width: calc(100% - .75em);
}
.illustration:not(:has( .comparison)):not(:has( .tabs)) {
.illustration:not(:has( .comparison)){
grid-column: main;
pre {

View File

@ -1,10 +1,9 @@
import "./components/getLibraryUrl.html.js";
import { t, T } from "./utils/index.js";
export const info= {
href: "./",
title: t`Introduction`,
fullTitle: t`Vanilla for flavouring — a full-fledged feast for large projects`,
description: t`Reactive DOM library for creating dynamic UIs with a declarative syntax`,
fullTitle: t`Vanilla for flavouring — a full-fledged feast for large projects`,
description: t`A lightweight, reactive DOM library for creating dynamic UIs with a declarative syntax`,
};
import { el } from "deka-dom-el";
@ -12,15 +11,10 @@ import { simplePage } from "./layout/simplePage.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { example } from "./components/example.html.js";
import { code } from "./components/code.html.js";
import { getLibraryUrl } from "./components/getLibraryUrl.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
const references= {
npm: {
title: t`NPM package page for dd<el>`,
href: "https://www.npmjs.com/package/deka-dom-el",
},
w_mvv: {
w_mvv:{
title: t`Wikipedia: Modelviewviewmodel`,
href: "https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel",
},
@ -31,27 +25,28 @@ const references= {
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
Welcome to Deka DOM Elements (dd<el> or DDE) — a library for building dynamic UIs with
a declarative syntax that stays close to the native DOM API. dd<el> gives you powerful reactive tools
without the complexity and overhead of larger frameworks.
el("p").append(...T`
Welcome to Deka DOM Elements (DDE) — a lightweight library for building dynamic UIs with a declarative
syntax that stays close to the native DOM API. DDE gives you powerful reactive tools without the complexity
and overhead of larger frameworks.
`),
el("div", { className: "callout" }).append(
el("h4", t`Key Benefits of dd<el>`),
el("h4", t`What Makes DDE Special`),
el("ul").append(
el("li", t`No build step required — use directly in the browser`),
el("li", t`Lightweight core (~1015kB minified) without unnecessary dependencies (0 at now 😇)`),
el("li", t`Lightweight core (~1015kB minified) with zero dependencies`),
el("li", t`Natural DOM API — work with real DOM nodes, not abstractions`),
el("li", t`Built-in (but optional) reactivity with simplified but powerful signals system`),
el("li", t`Built-in reactivity with powerful signals system`),
el("li", t`Clean code organization with the 3PS pattern`)
)
),
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js") }),
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
el(h3, { textContent: t`The 3PS Pattern: Simplified architecture pattern`, id: "h-3ps" }),
el("p").append(T`
At the heart of dd<el> is the 3PS (3-Part Separation) pattern. This simple yet powerful approach helps you
el(h3, { textContent: t`The 3PS Pattern: A Better Way to Build UIs`, id: "h-3ps" }),
el("p").append(...T`
At the heart of DDE is the 3PS (3-Part Separation) pattern. This simple yet powerful approach helps you
organize your UI code into three distinct areas, making your applications more maintainable and easier
to reason about.
`),
@ -59,116 +54,63 @@ export function page({ pkg, info }){
el("div", { className: "tabs" }).append(
el("div", { className: "tab" }).append(
el("h5", t`Traditional DOM Manipulation`),
el(code, { src: fileURL("./components/examples/introducing/3ps-before.js") }),
el(code, { src: fileURL("./components/examples/introducing/3ps-before.js"), page_id }),
),
el("div", { className: "tab" }).append(
el("h5", t`dd<el>'s 3PS Pattern`),
el(code, { src: fileURL("./components/examples/introducing/3ps.js") }),
el("h5", t`DDE's 3PS Pattern`),
el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }),
)
)
),
el("p").append(T`
el("p").append(...T`
The 3PS pattern separates your code into three clear parts:
`),
el("ol").append(
el("li").append(T`
${el("strong", "Create State")}: Define your applications reactive data using signals
el("li").append(...T`
${el("strong", "Create State")}: Define your application's reactive data using signals
`),
el("li").append(T`
${el("strong", "React to Changes")}: Define how UI elements and other parts of your app react to state
changes
el("li").append(...T`
${el("strong", "Bind to Elements")}: Define how UI elements react to state changes
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Update State")}: Modify state in response to user events or other triggers
`)
),
el("p").append(T`
el("p").append(...T`
By separating these concerns, your code becomes more modular, testable, and easier to maintain. This
approach ${el("strong", "is not something new and/or special to dd<el>")}. Its based on ${el("a", {
textContent: "MVC", ...references.w_mvc })} (${el("a", { textContent: "MVVM", ...references.w_mvv })}),
but is there presented in simpler form.
approach shares principles with more formal patterns like ${el("a", { textContent: "MVVM",
...references.w_mvv })} and ${el("a", { textContent: "MVC", ...references.w_mvc })}, but with less
overhead and complexity.
`),
el("div", { className: "note" }).append(
el("p").append(T`
el("p").append(...T`
The 3PS pattern becomes especially powerful when combined with components, allowing you to create
reusable pieces of UI with encapsulated state and behavior. Youll learn more about this in the
reusable pieces of UI with encapsulated state and behavior. You'll learn more about this in the
following sections.
`),
el("p").append(T`
The 3PS pattern isnt required to use with dd<el> but it is good practice to follow it or some similar
software architecture.
`)
),
el(h3, t`Getting Started`),
el("p").append(T`
There are multiple ways to include dd<el> in your project. You can use npm for a full development setup,
or directly include it from a CDN for quick prototyping.
`),
el("h4", "npm installation"),
el(code, { content: "npm install deka-dom-el --save", language: "shell" }),
el("p").append(T`
…see ${el("a", { textContent: "package page", ...references.npm, target: "_blank" })}.
`),
el("h4", "CDN / Direct Script Usage"),
el("p").append(T`
Use the interactive selector below to choose your preferred format:
`),
el(getLibraryUrl),
el("div", { className: "note" }).append(
el("p").append(T`
Based on your selection, you can use dd<el> in your project like this:
`),
el(code, { content: `
// ESM format (modern JavaScript with import/export)
import { el, on } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
// Or with IIFE format (creates a global DDE object)
// <script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/iife-with-signals.min.js"></script>
const { el, on } = DDE;
`, language: "js" }),
),
el(h3, t`How to Use This Documentation`),
el("p").append(T`
This guide will take you through dd<el>s features step by step:
el("p").append(...T`
This guide will take you through DDE's features step by step:
`),
el("ol", { start: 2 }).append(
el("li").append(T`${el("a", { href: "p02-elements.html" }).append(el("strong", "Elements"))} — Creating
and manipulating DOM elements`),
el("li").append(T`${el("a", { href: "p03-events.html" }).append(el("strong", "Events and Addons"))} —
Handling user interactions and lifecycle events`),
el("li").append(T`${el("a", { href: "p04-signals.html" }).append(el("strong", "Signals"))} — Adding
reactivity to your UI`),
el("li").append(T`${el("a", { href: "p05-scopes.html" }).append(el("strong", "Scopes"))} — Managing
component lifecycles`),
el("li").append(T`${el("a", { href: "p06-customElement.html" }).append(el("strong", "Web Components"))} —
Building native custom elements`),
el("li").append(T`${el("a", { href: "p07-debugging.html" }).append(el("strong", "Debugging"))} — Tools to
help you build and fix your apps`),
el("li").append(T`${el("a", { href: "p08-extensions.html" }).append(el("strong", "Extensions"))} —
Integrating third-party functionalities`),
el("li").append(T`${el("a", { href: "p09-optimization.html" })
.append(el("strong", "Performance Optimization"))} — Techniques for optimizing your applications`),
el("li").append(T`${el("a", { href: "p10-todomvc.html" }).append(el("strong", "TodoMVC"))} — A real-world
application implementation`),
el("li").append(T`${el("a", { href: "p11-ssr.html" }).append(el("strong", "SSR"))} — Server-side
rendering with dd<el>`),
el("li").append(T`${el("a", { href: "p12-ireland.html" }).append(el("strong", "Ireland Components"))} —
Interactive demos with server-side pre-rendering`),
el("li").append(T`${el("a", { href: "p13-appendix.html" }).append(el("strong", "Appendix & Summary"))} —
Comprehensive reference and best practices`),
el("li").append(T`${el("a", { href: "p14-converter.html" }).append(el("strong", "HTML Converter"))} —
Convert HTML to dd<el> JavaScript code`),
el("li").append(T`${el("a", { href: "p15-examples.html" }).append(el("strong", "Examples Gallery"))} —
Real-world application examples and case studies`),
el("ol").append(
el("li").append(...T`${el("strong", "Elements")} — Creating and manipulating DOM elements`),
el("li").append(...T`${el("strong", "Events")} — Handling user interactions and lifecycle events`),
el("li").append(...T`${el("strong", "Signals")} — Adding reactivity to your UI`),
el("li").append(...T`${el("strong", "Scopes")} — Managing component lifecycles`),
el("li").append(...T`${el("strong", "Custom Elements")} — Building web components`),
el("li").append(...T`${el("strong", "Debugging")} — Tools to help you build and fix your apps`),
el("li").append(...T`${el("strong", "Extensions")} — Integrating third-party functionalities`),
el("li").append(...T`${el("strong", "Ireland Components")}
Creating interactive demos with server-side pre-rendering`),
el("li").append(...T`${el("strong", "SSR")} — Server-side rendering with DDE`)
),
el("p").append(T`
el("p").append(...T`
Each section builds on the previous ones, so we recommend following them in order.
Lets get started with the basics of creating elements!
Let's get started with the basics of creating elements!
`),
);
}

View File

@ -105,8 +105,13 @@ ${host_nav} a .nav-number {
}
@media (max-width: 767px) {
${host_nav} {
padding: 0.75rem;
display: flex;
flex-flow: row wrap;
flex-direction: row;
flex-wrap: wrap;
gap: 0.5rem;
border-bottom: 1px solid var(--border);
border-right: none;
justify-content: center;
}
@ -116,7 +121,14 @@ ${host_nav} a .nav-number {
white-space: nowrap;
}
${host_nav} a .nav-number {
width: auto;
margin-right: 0.25rem;
}
${host_nav} a:first-child {
margin-bottom: 0;
margin-right: 0.5rem;
min-width: 100%;
justify-content: center;
}

View File

@ -3,7 +3,6 @@ import { el, simulateSlots } from "deka-dom-el";
import { header } from "./head.html.js";
import { prevNext } from "../components/pageUtils.html.js";
import { scrollTop } from "../components/scrollTop.html.js";
/** @param {Pick<import("../types.d.ts").PageAttrs, "pkg" | "info">} attrs */
export function simplePage({ pkg, info }){
@ -27,9 +26,6 @@ export function simplePage({ pkg, info }){
// Navigation between pages
el(prevNext, info)
),
// Scroll to top button
el(scrollTop),
)
));
}

View File

@ -47,84 +47,81 @@ const references= {
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
el("p").append(...T`
Building user interfaces in JavaScript often involves creating and manipulating DOM elements.
dd<el> provides a simple yet powerful approach to element creation that is declarative, chainable,
and maintains a clean syntax close to HTML structure.
DDE provides a simple yet powerful approach to element creation that is declarative, chainable,
and maintains a clean syntax close to HTML structure.
`),
el("div", { className: "callout" }).append(
el("h4", t`dd<el> Elements: Key Benefits`),
el("div", { class: "callout" }).append(
el("h4", t`DDE Elements: Key Benefits`),
el("ul").append(
el("li", t`Declarative element creation with intuitive property assignment`),
el("li", t`Chainable methods for natural DOM tree construction`),
el("li", t`Simplified component patterns for code reuse`),
el("li", t`Normalized declarative property/attribute handling across browsers`),
el("li", t`Normalized property/attribute handling across browsers`),
el("li", t`Smart element return values for cleaner code flow`)
)
),
el(code, { src: fileURL("./components/examples/elements/intro.js") }),
el(code, { src: fileURL("./components/examples/elements/intro.js"), page_id }),
el(h3, t`Creating Elements: Native vs dd<el>`),
el("p").append(T`
el(h3, t`Creating Elements: Native vs DDE`),
el("p").append(...T`
In standard JavaScript, you create DOM elements using the
${el("a", references.mdn_create).append(el("code", "document.createElement()"))} method
and then set properties individually or with ${el("code", "Object.assign()")}:
and then set properties individually or with Object.assign():
`),
el("div", { className: "illustration" }).append(
el("div", { className: "comparison" }).append(
el("div", { class: "illustration" }).append(
el("div", { class: "comparison" }).append(
el("div").append(
el("h5", t`Native DOM API`),
el(code, { src: fileURL("./components/examples/elements/native-dom-create.js") })
el(code, { src: fileURL("./components/examples/elements/native-dom-create.js"), page_id })
),
el("div").append(
el("h5", t`dd<el> Approach`),
el(code, { src: fileURL("./components/examples/elements/dde-dom-create.js") })
el("h5", t`DDE Approach`),
el(code, { src: fileURL("./components/examples/elements/dde-dom-create.js"), page_id })
)
)
),
el("p").append(T`
The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
el("p").append(...T`
The ${el("code", "el")} function provides a simple wrapper around ${el("code", "document.createElement")}
with enhanced property assignment.
`),
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js") }),
el(example, { src: fileURL("./components/examples/elements/dekaCreateElement.js"), page_id }),
el(h3, t`Advanced Property Assignment`),
el("p").append(T`
The ${el("code", "assign")} function is the heart of dd<el>s element property handling. It is internally
used to assign properties using the ${el("code", "el")} function. ${el("code", "assign")} provides
intelligent assignment of both ${el("a", { textContent: "properties (IDL)", ...references.mdn_idl })}
and attributes:
el("p").append(...T`
The ${el("code", "assign")} function is the heart of DDE's element property handling. It is internally used
to assign properties using the ${el("code", "el")} function. ${el("code", "assign")} provides intelligent
assignment of both properties (IDL) and attributes:
`),
el("div", { className: "function-table" }).append(
el("div", { class: "function-table" }).append(
el("dl").append(
el("dt", t`Property vs Attribute Priority`),
el("dd", t`Prefers IDL properties, falls back to setAttribute() when no writable property exists`),
el("dt", t`Data and ARIA Attributes`),
el("dd").append(T`Both ${el("code", "dataset.keyName")} and ${el("code", "dataKeyName")} syntaxes are
supported (same for ${el("code", "aria")}/${el("code", "ariaset")})`),
el("dd", t`Both dataset.* and data-* syntaxes supported (same for ARIA)`),
el("dt", t`Style Handling`),
el("dd").append(T`Accepts string or object notation for ${el("code", "style")} property`),
el("dd", t`Accepts string or object notation for style property`),
el("dt", t`Class Management`),
el("dd").append(T`Works with ${el("code", "className")} (${el("code", "class")}) and ${el("code",
"classList")} object for toggling classes`),
el("dd", t`Works with className, class, or classList object for toggling classes`),
el("dt", t`Force Modes`),
el("dd").append(T`Use ${el("code", "=")} prefix to force attribute mode, ${el("code", ".")} prefix to
force property mode`),
el("dd", t`Use = prefix to force attribute mode, . prefix to force property mode`),
el("dt", t`Attribute Removal`),
el("dd").append(T`Pass ${el("code", "undefined")} to remove a property or attribute`)
el("dd", t`Pass undefined to remove a property or attribute`)
)
),
el(example, { src: fileURL("./components/examples/elements/dekaAssign.js") }),
el(example, { src: fileURL("./components/examples/elements/dekaAssign.js"), page_id }),
el("div", { className: "note" }).append(
el("p").append(T`
el("div", { class: "note" }).append(
el("p").append(...T`
You can explore standard HTML element properties in the MDN documentation for
${el("a", { textContent: "HTMLElement", ...references.mdn_el })} (base class)
and specific element interfaces like ${el("a", { textContent: "HTMLParagraphElement", ...references.mdn_p })}.
@ -132,81 +129,75 @@ export function page({ pkg, info }){
),
el(h3, t`Building DOM Trees with Chainable Methods`),
el("p").append(T`
One of the most powerful features of dd<el> is its approach to building element trees.
Unlike the native DOM API which doesnt return the parent after ${el("code", "append()")}, dd<el>s
${el("code", "append()")} always returns the parent element:
el("p").append(...T`
One of the most powerful features of DDE is its approach to building element trees.
Unlike the native DOM API which doesn't return the parent after appendChild(), DDE's
append() always returns the parent element:
`),
el("div", { className: "illustration" }).append(
el("div", { className: "comparison" }).append(
el("div", { className: "bad-practice" }).append(
el("div", { class: "illustration" }).append(
el("div", { class: "comparison" }).append(
el("div", { class: "bad-practice" }).append(
el("h5", t`❌ Native DOM API`),
el(code, { src: fileURL("./components/examples/elements/native-dom-tree.js") })
el(code, { src: fileURL("./components/examples/elements/native-dom-tree.js"), page_id })
),
el("div", { className: "good-practice" }).append(
el("h5", t`dd<el> Approach`),
el(code, { src: fileURL("./components/examples/elements/dde-dom-tree.js") })
el("div", { class: "good-practice" }).append(
el("h5", t`DDE Approach`),
el(code, { src: fileURL("./components/examples/elements/dde-dom-tree.js"), page_id })
)
)
),
el("p").append(T`
el("p").append(...T`
This chainable pattern is much cleaner and easier to follow, especially for deeply nested elements.
It also makes it simple to add multiple children to a parent element in a single fluent expression.
It also makes it simple to add multiple children to a parent element in a single fluent expression.
`),
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js") }),
el(example, { src: fileURL("./components/examples/elements/dekaAppend.js"), page_id }),
el(h3, t`Using Components to Build UI Fragments`),
el("p").append(T`
el("p").append(...T`
The ${el("code", "el")} function is overloaded to support both tag names and function components.
This lets you refactor complex UI trees into reusable pieces:
`),
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js") }),
el("p").append(T`
el(example, { src: fileURL("./components/examples/elements/dekaBasicComponent.js"), page_id }),
el("p").append(...T`
Component functions receive the properties object as their first argument, just like regular elements.
This makes it easy to pass data down to components and create reusable UI fragments.
`),
el("div", { className: "tip" }).append(
el("p").append(T`
Its helpful to use naming conventions similar to native DOM elements for your components.
This allows you to keeps your code consistent with the DOM API.
`),
el("p").append(T`
Use ${el("a", { textContent: "destructuring assignment", ...references.mdn_destruct })}
to extract the properties from the ${el("code", "props")} and pass them to the component element:
${el("code", "function component({ className }){ return el(\"p\", { className }); }")} for make
templates cleaner.
`),
el("div", { class: "tip" }).append(
el("p").append(...T`
It's helpful to use naming conventions similar to native DOM elements for your components.
This allows you to use ${el("a", { textContent: "destructuring assignment", ...references.mdn_destruct })}
and keeps your code consistent with the DOM API.
`)
),
el(h3, t`Working with SVG and Other Namespaces`),
el("p").append(T`
For non-HTML elements like SVG, MathML, or custom namespaces, dd<el> provides the ${el("code", "elNS")}
function which corresponds to the native ${el("a", references.mdn_ns).append(el("code",
"document.createElementNS"))}:
el("p").append(...T`
For non-HTML elements like SVG, MathML, or custom namespaces, DDE provides the ${el("code", "elNS")} function
which corresponds to the native ${el("a", references.mdn_ns).append(el("code", "document.createElementNS"))}:
`),
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js") }),
el("p").append(T`
This function returns a namespace-specific element creator, allowing you to work with any element type
el(example, { src: fileURL("./components/examples/elements/dekaElNS.js"), page_id }),
el("p").append(...T`
This function returns a namespace-specific element creator, allowing you to work with any element type
using the same consistent interface.
`),
el(h3, t`Best Practices for Declarative DOM Creation`),
el("ol").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Use component functions for reusable UI fragments:")} Extract common UI patterns
into reusable functions that return elements.
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Leverage destructuring for cleaner component code:")} Use
${el("a", { textContent: "destructuring", ...references.mdn_destruct })} to extract properties
from the props object for cleaner component code.
`),
el("li").append(T`
${el("strong", "Leverage chainable methods for better performance:")} Use chainable methods
el("li").append(...T`
${el("strong", "Leverage chainable methods for better performance:")} Use chainable methods like
${el("code", ".append()")} to build complex DOM trees for better performance and cleaner code.
`),
),
el(mnemonic),
el(mnemonic)
);
}

View File

@ -36,17 +36,23 @@ const references= {
mdn_mutation: {
href: "https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver",
},
/** Readding the element to the DOM fix by Vue */
vue_fix: {
title: t`Vue and Web Components, lifecycle implementation readding the element to the DOM`,
href: "https://vuejs.org/guide/extras/web-components.html#lifecycle",
}
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
Events are at the core of interactive web applications. dd<el> provides a clean, declarative approach to
handling DOM events and extends this pattern with a powerful Addon system to incorporate additional
el("p").append(...T`
Events are at the core of interactive web applications. DDE provides a clean, declarative approach to
handling DOM events and extends this pattern with a powerful Addon system to incorporate additional
functionalities into your UI templates.
`),
el("div", { className: "callout" }).append(
el("h4", t`Why dd<el>s Event System and Addons Matters`),
el("h4", t`Why DDE's Event System and Addons Matters`),
el("ul").append(
el("li", t`Integrate event handling directly in element declarations`),
el("li", t`Leverage lifecycle events for better component design`),
@ -56,76 +62,71 @@ export function page({ pkg, info }){
)
),
el(code, { src: fileURL("./components/examples/events/intro.js") }),
el(code, { src: fileURL("./components/examples/events/intro.js"), page_id }),
el(h3, t`Events and Listeners: Two Approaches`),
el("p").append(T`
el("p").append(...T`
In JavaScript you can listen to native DOM events using
${el("a", references.mdn_listen).append(el("code", "element.addEventListener(type, listener, options)"))}.
dd<el> provides an alternative approach with arguments ordered differently to better fit its declarative
style:
DDE provides an alternative approach with arguments ordered differently to better fit its declarative style:
`),
el("div", { className: "illustration" }).append(
el("div", { className: "tabs" }).append(
el("div", { className: "tab" }).append(
el("h5", t`Native DOM API`),
el(code, { content: `element.addEventListener("click", callback, options);`, language: "js" })
el(code, { content: `element.addEventListener('click', callback, options);`, page_id })
),
el("div", { className: "tab" }).append(
el("h5", t`dd<el> Approach`),
el(code, { content: `on("click", callback, options)(element);`, language: "js" })
el("h5", t`DDE Approach`),
el(code, { content: `on('click', callback, options)(element);`, page_id })
)
)
),
el("p").append(T`
The main benefit of dd<el>s approach is that it works as an Addon (see below), making it easy to integrate
el("p").append(...T`
The main benefit of DDE's approach is that it works as an Addon, making it easy to integrate
directly into element declarations.
`),
el(example, { src: fileURL("./components/examples/events/compare.js") }),
el(example, { src: fileURL("./components/examples/events/compare.js"), page_id }),
el(h3, t`Removing Event Listeners`),
el("div", { className: "note" }).append(
el("p").append(T`
Unlike the native addEventListener/removeEventListener pattern, dd<el> uses ${el("strong", "only")}
${el("a", { textContent: "AbortSignal", ...references.mdn_abortListener })} for declarative removal:
el("p").append(...T`
Unlike the native addEventListener/removeEventListener pattern, DDE uses the ${el("a", {
textContent: "AbortSignal", ...references.mdn_abortListener })} for declarative approach for removal:
`)
),
el(example, { src: fileURL("./components/examples/events/abortSignal.js") }),
el("p").append(T`
This is the same for signals (see next section) and works well with scopes and library extendability (
see scopes and extensions section — mainly ${el("code", "scope.signal")}).
`),
el(example, { src: fileURL("./components/examples/events/abortSignal.js"), page_id }),
el(h3, t`Three Ways to Handle Events`),
el("div", { className: "tabs" }).append(
el("div", { className: "tab", dataTab: "html-attr" }).append(
el("div", { className: "tab", "data-tab": "html-attr" }).append(
el("h4", t`HTML Attribute Style`),
el(code, { src: fileURL("./components/examples/events/attribute-event.js") }),
el("p").append(T`
Forces usage as an HTML attribute. Corresponds to
el(code, { src: fileURL("./components/examples/events/attribute-event.js"), page_id }),
el("p").append(...T`
Forces usage as an HTML attribute. Corresponds to
${el("code", `<button onclick="console.log(event)">click me</button>`)}. This can be particularly
useful for SSR scenarios.
`)
),
el("div", { className: "tab", dataTab: "property" }).append(
el("div", { className: "tab", "data-tab": "property" }).append(
el("h4", t`Property Assignment`),
el(code, { src: fileURL("./components/examples/events/property-event.js") }),
el("p", t`Assigns the event handler directly to the elements property.`)
el(code, { src: fileURL("./components/examples/events/property-event.js"), page_id }),
el("p", t`Assigns the event handler directly to the element's property.`)
),
el("div", { className: "tab", dataTab: "addon" }).append(
el("div", { className: "tab", "data-tab": "addon" }).append(
el("h4", t`Addon Approach`),
el(code, { src: fileURL("./components/examples/events/chain-event.js") }),
el("p", t`Uses the addon pattern (so adds the event listener to the element), see above.`)
el(code, { src: fileURL("./components/examples/events/chain-event.js"), page_id }),
el("p", t`Uses the addon pattern, see above.`)
)
),
el("p").append(T`
For a deeper comparison of these approaches, see
${el("a", { textContent: "WebReflections detailed analysis", ...references.web_events })}.
el("p").append(...T`
For a deeper comparison of these approaches, see
${el("a", { textContent: "WebReflection's detailed analysis", ...references.web_events })}.
`),
el(h3, t`Understanding Addons`),
el("p").append(T`
Addons are a powerful pattern in dd<el> that extends beyond just event handling.
el("p").append(...T`
Addons are a powerful pattern in DDE that extends beyond just event handling.
An Addon is any function that accepts an HTML element as its first parameter.
`),
el("div", { className: "callout" }).append(
@ -135,27 +136,26 @@ export function page({ pkg, info }){
el("li", t`Set up lifecycle behaviors`),
el("li", t`Integrate third-party libraries`),
el("li", t`Create reusable element behaviors`),
el("li", t`Capture element references`), // TODO: add example?
el("li", t`Capture element references`)
)
),
el("p").append(T`
el("p").append(...T`
You can use Addons as ≥3rd argument of the ${el("code", "el")} function, making it possible to
extend your templates with additional functionality:
`),
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js") }),
el("p").append(T`
el(example, { src: fileURL("./components/examples/events/templateWithListeners.js"), page_id }),
el("p").append(...T`
As the example shows, you can provide types in JSDoc+TypeScript using the global type
${el("code", "ddeElementAddon")}. Notice how Addons can also be used to get element references.
`),
el(h3, t`Lifecycle Events`),
el("p").append(T`
Addons are called immediately when an element is created, even before its connected to the live DOM.
You can think of an Addon as an “oncreate event handler.
el("p").append(...T`
Addons are called immediately when an element is created, even before it's connected to the live DOM.
You can think of an Addon as an "oncreate" event handler.
`),
el("p").append(T`
dd<el> provides two additional lifecycle events that correspond to ${el("a", { textContent:
"custom element", ...references.mdn_customElements })} lifecycle callbacks and component patterns:
el("p").append(...T`
DDE provides three additional lifecycle events that correspond to custom element lifecycle callbacks:
`),
el("div", { className: "function-table" }).append(
el("dl").append(
@ -164,74 +164,61 @@ export function page({ pkg, info }){
el("dt", t`on.disconnected(callback)`),
el("dd", t`Fires when the element is removed from the DOM`),
el("dt", t`on.attributeChanged(callback, attributeName)`),
el("dd", t`Fires when the specified attribute changes`)
)
),
el(example, { src: fileURL("./components/examples/events/live-cycle.js") }),
el(example, { src: fileURL("./components/examples/events/live-cycle.js"), page_id }),
el("div", { className: "note" }).append(
el("p").append(T`
For regular elements (non-custom elements), dd<el> uses ${el("a",
references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN")} internally to track
lifecycle events.
el("p").append(...T`
For regular elements (non-custom elements), DDE uses
${el("a", references.mdn_mutation).append(el("code", "MutationObserver"), " | MDN")}
internally to track lifecycle events.
`)
),
el("div", { className: "warning" }).append(
el("ul").append(
el("li").append(T`
Always use ${el("code", "on.*")} functions as library must ensure proper (MutationObserver)
registration, not ${el("code", "on('dde:*', ...)")}, even the native event system is used with event
names prefixed with ${el("code", "dde:")}.
el("li").append(...T`
Always use ${el("code", "on.*")} functions, not ${el("code", "on('dde:*', ...)")}, for proper registration
`),
el("li").append(T`
el("li").append(...T`
Use lifecycle events sparingly, as they require internal tracking
`),
el("li").append(T`
Leverage parent-child relationships: when a parent is removed, all children are also removed
el("li").append(...T`
Leverage parent-child relationships: when a parent is removed, all children are also removed
`),
el("li").append(T`
el("li").append(...T`
…see section later in documentation regarding hosts elements
`),
el("li").append(T`
dd<el> ensures that connected/disconnected events fire only once for better predictability
el("li").append(...T`
DDE ensures that connected/disconnected events fire only once for better predictability
`)
)
),
el(h3, t`Utility Helpers`),
el("p").append(T`
You can use the ${el("code", "on.defer")} helper to defer execution to the next event loop.
This is useful for example when you wan to set some element properties based on the current element
body (typically the ${el("code", "<select value=\"...\">")}).
`),
el("div", { className: "function-table" }).append(
el("dl").append(
el("dt", t`on.defer(callback)`),
el("dd", t`Helper that defers function execution to the next event loop (using setTimeout)`),
)
),
el(h3, t`Dispatching Custom Events`),
el("p").append(T`
This makes it easy to implement component communication through events, following standard web platform
patterns. The curried approach allows for easy reuse of event dispatchers throughout your application.
el("p").append(...T`
This makes it easy to implement component communication through events,
following standard web platform patterns. The curried approach allows for easy reuse
of event dispatchers throughout your application.
`),
el(example, { src: fileURL("./components/examples/events/compareDispatch.js") }),
el(code, { src: fileURL("./components/examples/events/dispatch.js") }),
el(example, { src: fileURL("./components/examples/events/compareDispatch.js"), page_id }),
el(h3, t`Best Practices`),
el("ol").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Clean up listeners")}: Use AbortSignal to prevent memory leaks
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Leverage lifecycle events")}: For component setup and teardown
`),
el("li").append(T`
${el("strong", "Delegate when possible")}: Add listeners to container elements when handling many
similar elements
el("li").append(...T`
${el("strong", "Delegate when possible")}: Add listeners to container elements when handling many similar elements
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Maintain consistency")}: Choose one event binding approach and stick with it
`)
),
@ -250,6 +237,6 @@ export function page({ pkg, info }){
)
),
el(mnemonic),
el(mnemonic)
);
}

View File

@ -42,128 +42,127 @@ const references= {
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
Signals provide a simple yet powerful way to create reactive applications with dd<el>. They handle the
el("p").append(...T`
Signals provide a simple yet powerful way to create reactive applications with DDE. They handle the
fundamental challenge of keeping your UI in sync with changing data in a declarative, efficient way.
`),
el("div", { className: "callout" }).append(
el("div", { class: "callout" }).append(
el("h4", t`What Makes Signals Special?`),
el("ul").append(
el("li", t`Fine-grained reactivity without complex state management`),
el("li", t`Automatic UI updates when data changes`),
el("li", t`Clean separation between data, logic, and UI`),
el("li", t`Small runtime with minimal overhead`),
el("li").append(T`${el("strong", "In future")} no dependencies or framework lock-in`)
el("li").append(...T`${el("strong", "In future")} no dependencies or framework lock-in`)
)
),
el(code, { src: fileURL("./components/examples/signals/intro.js") }),
el(code, { src: fileURL("./components/examples/signals/intro.js"), page_id }),
el(h3, t`The 3-Part Structure of Signals`),
el("p").append(T`
el("p").append(...T`
Signals organize your code into three distinct parts, following the
${el("a", { textContent: t`3PS principle`, href: "./#h-3ps" })}:
`),
el("div", { className: "signal-diagram" }).append(
el("div", { className: "signal-part" }).append(
el("div", { class: "signal-diagram" }).append(
el("div", { class: "signal-part" }).append(
el("h4", t`PART 1: Create Signal`),
el(code, { content: "const count = S(0);", language: "js" }),
el("p", t`Define a reactive value that can be observed and changed`)
el(code, { content: "const count = S(0);", page_id }),
el("p", t`Define a reactive value that can be observed and changed`)
),
el("div", { className: "signal-part" }).append(
el("div", { class: "signal-part" }).append(
el("h4", t`PART 2: React to Changes`),
el(code, { content: "S.on(count, value => updateUI(value));", language: "js" }),
el(code, { content: "S.on(count, value => updateUI(value));", page_id }),
el("p", t`Subscribe to signal changes with callbacks or effects`)
),
el("div", { className: "signal-part" }).append(
el("div", { class: "signal-part" }).append(
el("h4", t`PART 3: Update Signal`),
el(code, { content: "count.set(count.get() + 1);", language: "js" }),
el(code, { content: "count.set(count.get() + 1);", page_id }),
el("p", t`Modify the signal value, which automatically triggers updates`)
)
),
el(example, { src: fileURL("./components/examples/signals/signals.js") }),
el(example, { src: fileURL("./components/examples/signals/signals.js"), page_id }),
el("div", { className: "note" }).append(
el("p").append(T`
Signals implement the ${el("a", { textContent: t`Publishsubscribe pattern`, ...references.wiki_pubsub
})}, a form of ${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven
})}. This architecture allows different parts of your application to stay synchronized through
a shared signal, without direct dependencies on each other. Compare for example with ${el("a", {
textContent: t`fpubsub library`, ...references.fpubsub })}.
el("div", { class: "note" }).append(
el("p").append(...T`
Signals implement the ${el("a", { textContent: t`Publishsubscribe pattern`, ...references.wiki_pubsub })},
a form of ${el("a", { textContent: t`Event-driven programming`, ...references.wiki_event_driven })}.
This architecture allows different parts of your application to stay synchronized through a shared signal,
without direct dependencies on each other. Compare for example with ${el("a", { textContent:
t`fpubsub library`, ...references.fpubsub })}.
`)
),
el(h3, t`Signal Essentials: Core API`),
el("div", { className: "function-table" }).append(
el("div", { class: "function-table" }).append(
el("dl").append(
el("dt", t`Creating a Signal`),
el("dt", t`Creating a Signal`),
el("dd", t`S(initialValue) → creates a signal with the given value`),
el("dt", t`Reading a Signal`),
el("dt", t`Reading a Signal`),
el("dd", t`signal.get() → returns the current value`),
el("dt", t`Writing to a Signal`),
el("dt", t`Writing to a Signal`),
el("dd", t`signal.set(newValue) → updates the value and notifies subscribers`),
el("dt", t`Subscribing to Changes`),
el("dd", t`S.on(signal, callback) → runs callback whenever signal changes`),
el("dt", t`Unsubscribing`),
el("dd").append(T`S.on(signal, callback, { signal: abortController.signal }) → Similarly to the
el("dd").append(...T`S.on(signal, callback, { signal: abortController.signal }) → Similarly to the
${el("code", "on")} function to register DOM events listener.`)
)
),
el("p").append(T`
Signals can be created with any type of value, but they work best with ${el("a", { textContent:
t`primitive types`, ...references.mdn_primitive })} like strings, numbers, and booleans. For complex
data types like objects and arrays, youll want to use Actions (covered below).
el("p").append(...T`
Signals can be created with any type of value, but they work best with
${el("a", { textContent: t`primitive types`, ...references.mdn_primitive })} like strings, numbers, and booleans.
For complex data types like objects and arrays, you'll want to use Actions (covered below).
`),
el(h3, t`Derived Signals: Computed Values`),
el("p").append(T`
el("p").append(...T`
Computed values (also called derived signals) automatically update when their dependencies change.
Create them by passing ${el("strong", "a function")} to ${el("code", "S()")}:
Create them by passing a function to S():
`),
el(example, { src: fileURL("./components/examples/signals/derived.js") }),
el("p").append(T`
Derived signals are read-only - you cant call ${el("code", ".set()")} on them. Their value is always
computed from their dependencies. Theyre perfect for transforming or combining data from other signals.
el(example, { src: fileURL("./components/examples/signals/derived.js"), page_id }),
el("p").append(...T`
Derived signals are read-only - you can't call .set() on them. Their value is always computed
from their dependencies. They're perfect for transforming or combining data from other signals.
`),
el(example, { src: fileURL("./components/examples/signals/computations-abort.js") }),
el(example, { src: fileURL("./components/examples/signals/computations-abort.js"), page_id }),
el(h3, t`Signal Actions: For Complex State`),
el("p").append(T`
When working with objects, arrays, or other complex data structures. Signal Actions provide
a structured way to modify state while maintaining reactivity.
el("p").append(...T`
When working with objects, arrays, or other complex data structures, Signal Actions provide
a structured way to modify state while maintaining reactivity.
`),
el("div", { className: "illustration" }).append(
el("div", { class: "illustration" }).append(
el("h4", t`Actions vs. Direct Mutation`),
el("div", { className: "comparison" }).append(
el("div", { className: "good-practice" }).append(
el("div", { class: "comparison" }).append(
el("div", { class: "good-practice" }).append(
el("h5", t`✅ With Actions`),
el(code, { content: `
const todos = S([], {
add(text) {
this.value.push(text);
// Subscribers notified automatically
}
});
// Use the action
S.action(todos, "add", "New todo");
`, language: "js" })
el(code, { content: `const todos = S([], {
add(text) {
this.value.push(text);
// Subscribers notified automatically
}
});
// Use the action
S.action(todos, "add", "New todo");`, page_id })
),
el("div", { className: "bad-practice" }).append(
el("div", { class: "bad-practice" }).append(
el("h5", t`❌ Without Actions`),
el(code, { content: `
const todos = S([]);
// Directly mutating the array
const items = todos.get();
items.push("New todo");
// This WONT trigger updates!
`, language: "js" }))
const todos = S([]);
// Directly mutating the array
const items = todos.get();
items.push("New todo");
// This WON'T trigger updates!`, page_id }))
),
),
el("p").append(T`
el("p").append(...T`
In some way, you can compare it with ${el("a", { textContent: "useReducer", ...references.mdn_use_reducer })}
hook from React. So, the ${el("code", "S(<data>, <actions>)")} pattern creates a store “machine”. We can
then invoke (dispatch) registered action by calling ${el("code", "S.action(<signal>, <name>, ...<args>)")}
@ -171,9 +170,9 @@ export function page({ pkg, info }){
${el("code", "this.stopPropagation()")} in the method representing the given action. As it can be seen in
examples, the “store” value is available also in the function for given action (${el("code", "this.value")}).
`),
el(example, { src: fileURL("./components/examples/signals/actions-demo.js") }),
el(example, { src: fileURL("./components/examples/signals/actions-demo.js"), page_id }),
el("p").append(T`
el("p").append(...T`
Actions provide these benefits:
`),
el("ul").append(
@ -182,186 +181,112 @@ export function page({ pkg, info }){
el("li", t`Prevent accidental direct mutations`),
el("li", t`Act similar to reducers in other state management libraries`)
),
el("p").append(T`
Heres a more complete example of a todo list using signal actions:
el("p").append(...T`
Here's a more complete example of a todo list using signal actions:
`),
el(example, { src: fileURL("./components/examples/signals/actions-todos.js") }),
el(example, { src: fileURL("./components/examples/signals/actions-todos.js"), page_id }),
el("div", { className: "tip" }).append(
el("p").append(T`
el("div", { class: "tip" }).append(
el("p").append(...T`
${el("strong", "Special Action Methods")}: Signal actions can implement special lifecycle hooks:
`),
el("ul").append(
el("li").append(T`
${el("code", "[S.symbols.onclear]()")} - Called when the signal is cleared. Use it to clean up
resources.
`),
el("li", t`[S.symbols.onclear]() - Called when the signal is cleared. Use it to clean up resources.`),
)
),
el(h3, t`Connecting Signals to the DOM`),
el("p").append(T`
Signals really shine when connected to your UI. dd<el> provides several ways to bind signals to DOM elements:
el("p").append(...T`
Signals really shine when connected to your UI. DDE provides several ways to bind signals to DOM elements:
`),
el("div", { className: "tabs" }).append(
el("div", { className: "tab", dataTab: "attributes" }).append(
el("div", { class: "tabs" }).append(
el("div", { class: "tab", "data-tab": "attributes" }).append(
el("h4", t`Reactive Attributes`),
el("p", t`Bind signal values directly to element attributes, properties, or styles:`),
el(code, { content: `
// Create a signal
const color = S("blue");
el(code, { content: `// Create a signal
const color = S("blue");
// Bind it to an elements style
el("div", {
style: {
color, // Updates when signal changes
fontWeight: S(() => color.get() === "red" ? "bold" : "normal")
}
}, "This text changes color");
// Bind it to an element's style
el("div", {
style: {
color, // Updates when signal changes
fontWeight: S(() => color.get() === "red" ? "bold" : "normal")
}
}, "This text changes color");
// Later:
color.set("red"); // UI updates automatically
`, language: "js" }),
// Later:
color.set("red"); // UI updates automatically`, page_id })
),
el("div", { className: "tab", dataTab: "elements" }).append(
el("div", { class: "tab", "data-tab": "elements" }).append(
el("h4", t`Reactive Elements`),
el("p", t`Dynamically create or update elements based on signal values:`),
el(code, { content: `
// Create an array signal
const items = S(["Apple", "Banana", "Cherry"]);
el(code, { content: `// Create an array signal
const items = S(["Apple", "Banana", "Cherry"]);
// Create a dynamic list that updates when items change
el("ul").append(
S.el(items, items =>
items.map(item => el("li", item))
)
);
// Create a dynamic list that updates when items change
el("ul").append(
S.el(items, items =>
items.map(item => el("li", item))
)
);
// Later:
S.action(items, "push", "Dragonfruit"); // List updates automatically
`, language: "js" }),
// Later:
S.action(items, "push", "Dragonfruit"); // List updates automatically`, page_id })
)
),
el("p").append(T`
el("p").append(...T`
The ${el("code", "assign")} and ${el("code", "el")} functions detect signals automatically and handle binding.
You can use special properties like ${el("code", "dataset")}, ${el("code", "ariaset")}, and
${el("code", "classList")} for fine-grained control over specific attribute types.
`),
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js") }),
el(example, { src: fileURL("./components/examples/signals/dom-attrs.js"), page_id }),
el("p").append(T`
el("p").append(...T`
${el("code", "S.el()")} is especially powerful for conditional rendering and lists:
`),
el(example, { src: fileURL("./components/examples/signals/dom-el.js") }),
el(example, { src: fileURL("./components/examples/signals/dom-el.js"), page_id }),
el(h3, t`Best Practices for Signals`),
el("p").append(T`
el("p").append(...T`
Follow these guidelines to get the most out of signals:
`),
el("ol").append(
el("li").append(T`
${el("strong", "Keep signals small and focused")}: Use many small signals rather than a few large ones
el("li").append(...T`
${el("strong", "Keep signals small and focused")}: Use many small signals rather than a few large ones
`),
el("li").append(T`
${el("strong", "Use derived signals for computations")}: Dont recompute values in multiple places
el("li").append(...T`
${el("strong", "Use derived signals for computations")}: Don't recompute values in multiple places
`),
el("li").append(T`
${el("strong", "Clean up signal subscriptions")}: Use AbortController (scope.host()) to prevent memory
leaks
el("li").append(...T`
${el("strong", "Clean up signal subscriptions")}: Use AbortController or scope.host() to prevent memory leaks
`),
el("li").append(T`
${el("strong", "Use actions for complex state")}: Dont directly mutate objects or arrays in signals
el("li").append(...T`
${el("strong", "Use actions for complex state")}: Don't directly mutate objects or arrays in signals
`),
el("li").append(T`
${el("strong", "Avoid infinite loops")}: Be careful when one signal updates another in a subscription
`),
),
el("p").append(T`
While signals provide powerful reactivity for complex UI interactions, theyre not always necessary.
A good approach is to started with variables/constants and when necessary, convert them to signals.
`),
el("div", { className: "tabs" }).append(
el("div", { className: "tab", dataTab: "events" }).append(
el("h4", t`We can process form events without signals`),
el("p", t`This can be used when the form data doesnt need to be reactive and we just waiting for
results.`),
el(code, { content: `
const onFormSubmit = on("submit", e => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
// this can be sent to a server
// or processed locally
// e.g.: console.log(Object.fromEntries(formData))
});
// …
return el("form", null, onFormSubmit).append(
// …
);
`, language: "js" })
),
el("div", { className: "tab", dataTab: "variables" }).append(
el("h4", t`We can use variables without signals`),
el("p", t`We use this when we dontt need to reflect changes in the elsewhere (UI).`),
el(code, { content: `
let canSubmit = false;
const onFormSubmit = on("submit", e => {
e.preventDefault();
if(!canSubmit) return; // some message
// …
});
const onAllowSubmit = on("click", e => {
canSubmit = true;
});
`, language: "js" }),
),
el("div", { className: "tab", dataTab: "state" }).append(
el("h4", t`Using signals`),
el("p", t`We use this when we need to reflect changes for example in the UI (e.g. enable/disable
buttons).`),
el(code, { content: `
const canSubmit = S(false);
const onFormSubmit = on("submit", e => {
e.preventDefault();
// …
});
const onAllowSubmit = on("click", e => {
canSubmit.set(true);
});
return el("form", null, onFormSubmit).append(
// ...
el("button", { textContent: "Allow Submit", type: "button" }, onAllowSubmit),
el("button", { disabled: S(()=> !canSubmit), textContent: "Submit" })
);
`, language: "js" }),
),
el("li").append(...T`
${el("strong", "Avoid infinite loops")}: Be careful when one signal updates another in a subscription
`)
),
el("div", { className: "troubleshooting" }).append(
el("div", { class: "troubleshooting" }).append(
el("h4", t`Common Signal Pitfalls`),
el("dl").append(
el("dt", t`UI not updating when array/object changes`),
el("dd", t`Use signal actions instead of direct mutation`),
el("dt", t`UI not updating`),
el("dd").append(T`Ensure you passing the (correct) signal not its value (${el("code", "signal")} vs
${el("code", "signal.get()")})`),
el("dt", t`Infinite update loops`),
el("dd", t`Check for circular dependencies between signals`),
el("dt", t`Memory leaks`),
el("dd", t`Use AbortController or scope.host() to clean up subscriptions`),
el("dt", t`Multiple elements updating unnecessarily`),
el("dd", t`Split large signals into smaller, more focused ones`)
)
),
el(mnemonic),
el(mnemonic)
);
}

View File

@ -10,7 +10,7 @@ import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/scopes-init.js";
import { code, pre } from "./components/code.html.js";
import { code } from "./components/code.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
const references= {
@ -27,159 +27,157 @@ const references= {
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
el("p").append(...T`
For state-less components we can use functions as UI components (see “Elements” page). But in real life,
we may need to handle the components life-cycle and provide JavaScript the way to properly use
we may need to handle the component's life-cycle and provide JavaScript the way to properly use
the ${el("a", { textContent: t`Garbage collection`, ...references.garbage_collection })}.
`),
el(code, { src: fileURL("./components/examples/scopes/intro.js") }),
el("p").append(T`The library therefore uses ${el("em", t`scopes`)} to provide these functionalities.`),
el(code, { src: fileURL("./components/examples/scopes/intro.js"), page_id }),
el("p").append(...T`The library therefore uses ${el("em", t`scopes`)} to provide these functionalities.`),
el(h3, t`Understanding Host Elements and Scopes`),
el("p").append(T`
el("p").append(...T`
The ${el("strong", "host")} is the name for the element representing the component. This is typically the
element returned by a function. To get a reference, you can use ${el("code", "scope.host()")}. To apply addons,
element returned by a function. To get a reference, you can use ${el("code", "scope.host()")}. To apply addons,
just use ${el("code", "scope.host(...<addons>)")}.
`),
el("p").append(T`
el("p").append(...T`
Scopes are primarily needed when signals are used in DOM templates (with ${el("code", "el")}, ${el("code",
"assign")}, or ${el("code", "S.el")}). They provide a way for automatically removing signal listeners
"assign")}, or ${el("code", "S.el")}). They provide a way for automatically removing signal listeners
and cleaning up unused signals when components are removed from the DOM.
`),
el("div", { className: "illustration" }).append(
el("h4", t`Component Anatomy`),
el(pre, { content: `
// 1. Component scope created
el(MyComponent);
el("pre").append(el("code", `
// 1. Component scope created
el(MyComponent);
function MyComponent() {
// 2. access the host element (or other scope related values)
const { host } = scope;
function MyComponent() {
  // 2. access the host element
  const { host } = scope;
// 3. Add behavior to host
host(
on.click(handleClick)
);
  // 3. Add behavior to host
  host(
  on.click(handleClick)
  );
// 4. Return the host element
return el("div", {
className: "my-component"
}).append(
el("h2", "Title"),
el("p", "Content"),
);
}
` })
  // 4. Return the host element
  return el("div", {
  className: "my-component"
  }).append(
  el("h2", "Title"),
  el("p", "Content")
  );
}
`.trim()))
),
el("div", { className: "function-table" }).append(
el("h4", t`scope.host()`),
el("dl").append(
el("dt", t`When called with no arguments`),
el("dd", t`Returns a reference to the host element (the root element of your component)`),
el("dd", t`Returns a reference to the host element (the root element of your component)`),
el("dt", t`When called with addons/callbacks`),
el("dd", t`Applies the addons to the host element (and returns the host element)`)
el("dd", t`Applies the addons to the host element and returns the host element`)
)
),
el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js") }),
el(example, { src: fileURL("./components/examples/scopes/scopes-and-hosts.js"), page_id }),
el("div", { className: "tip" }).append(
el("p").append(T`
${el("strong", "Best Practice:")} Always capture the host reference (or other scope related values) at
the beginning of your component function using ${el("code", "const { host } = scope")} to avoid
scope-related issues, especially with ${el("em", "asynchronous code")}.
el("p").append(...T`
${el("strong", "Best Practice:")} Always capture the host reference at the beginning of your component
function using ${el("code", "const { host } = scope")} to avoid scope-related issues, especially with
asynchronous code.
`),
el("p").append(T`
el("p").append(...T`
If you are interested in the implementation details, see Class-Based Components section.
`)
),
el(code, { src: fileURL("./components/examples/scopes/good-practise.js") }),
el(code, { src: fileURL("./components/examples/scopes/good-practise.js"), page_id }),
el(h3, t`Class-Based Components`),
el("p").append(T`
While functional components are the primary pattern in dd<el>, you can also create class-based components.
el("p").append(...T`
While functional components are the primary pattern in DDE, you can also create class-based components.
For this, we implement function ${el("code", "elClass")} and use it to demonstrate implementation details
for better understanding of the scope logic.
`),
el(example, { src: fileURL("./components/examples/scopes/class-component.js") }),
el(example, { src: fileURL("./components/examples/scopes/class-component.js"), page_id }),
el(h3, t`Automatic Cleanup with Scopes`),
el("p").append(T`
el("p").append(...T`
One of the most powerful features of scopes is automatic cleanup when components are removed from the DOM.
This prevents memory leaks and ensures resources are properly released.
`),
el("div", { className: "illustration" }).append(
el("h4", t`Lifecycle Flow`),
el(pre, { content: `
1. Component created → scope established
2. Component added to DOM → connected event
3. Component interactions happen
4. Component removed from DOM → disconnected event
5. Automatic cleanup of:
- Event listeners (browser)
- Signal subscriptions (dd<el> and browser)
- Custom cleanup code (dd<el> and user)
` })
el("pre").append(el("code", `
1. Component created → scope established
2. Component added to DOM → connected event
3. Component interactions happen
4. Component removed from DOM → disconnected event
5. Automatic cleanup of:
  - Event listeners
  - Signal subscriptions
  - Custom cleanup code
`))
),
el(example, { src: fileURL("./components/examples/scopes/cleaning.js") }),
el(example, { src: fileURL("./components/examples/scopes/cleaning.js"), page_id }),
el("div", { className: "note" }).append(
el("p").append(T`
el("p").append(...T`
In this example, when you click "Remove", the component is removed from the DOM, and all its associated
resources are automatically cleaned up, including ${el("em",
"the signal subscription that updates the text content")}. This happens because the library
internally registers a disconnected event handler on the host element.
resources are automatically cleaned up, including the signal subscription that updates the text content.
This happens because the library internally registers a disconnected event handler on the host element.
`)
),
el(h3, t`Declarative vs Imperative Components`),
el("p").append(T`
The library DOM API and signals work best when used declaratively. It means you split your apps logic
into three parts as introduced in ${el("a", { textContent: "Signals (3PS)", ...references.signals })}.
el("p").append(...T`
The library DOM API and signals work best when used declaratively. It means you split your app's logic
into three parts as introduced in ${el("a", { textContent: "Signals", ...references.signals })}.
`),
el("div", { className: "note" }).append(
el("p").append(T`
el("p").append(...T`
Strictly speaking, the imperative way of using the library is not prohibited. Just be careful to avoid
mixing the declarative approach (using signals) with imperative manipulation of elements.
`)
),
el("div", { className: "tabs" }).append(
el("div", { className: "tab", dataTab: "declarative" }).append(
el("div", { className: "tab", "data-tab": "declarative" }).append(
el("h4", t`✅ Declarative Approach`),
el("p", t`Define what your UI should look like based on state:`),
el(code, { src: fileURL("./components/examples/scopes/declarative.js") })
el(code, { src: fileURL("./components/examples/scopes/declarative.js"), page_id })
),
el("div", { className: "tab", dataTab: "imperative" }).append(
el("div", { className: "tab", "data-tab": "imperative" }).append(
el("h4", t`⚠️ Imperative Approach`),
el("p", t`Manually update the DOM in response to events:`),
el(code, { src: fileURL("./components/examples/scopes/imperative.js") })
el(code, { src: fileURL("./components/examples/scopes/imperative.js"), page_id })
),
el("div", { className: "tab", dataTab: "mixed" }).append(
el("div", { className: "tab", "data-tab": "mixed" }).append(
el("h4", t`❌ Mixed Approach`),
el("p", t`This approach should be avoided:`),
el(code, { src: fileURL("./components/examples/scopes/mixed.js") })
el(code, { src: fileURL("./components/examples/scopes/mixed.js"), page_id })
)
),
el(h3, t`Best Practices for Scopes and Components`),
el("ol").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Capture host early:")} Use ${el("code", "const { host } = scope")} at component start
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Define signals as constants:")} ${el("code", "const counter = S(0);")}
`),
el("li").append(T`
${el("strong", "Prefer declarative patterns:")} Use signals to drive UI updates rather than manual DOM
manipulation
el("li").append(...T`
${el("strong", "Prefer declarative patterns:")} Use signals to drive UI updates rather than manual DOM manipulation
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Keep components focused:")} Each component should do one thing well
`),
el("li").append(T`
${el("strong", "Add explicit cleanup:")} For resources not managed by dd<el>, use ${el("code",
"on.disconnected")}
el("li").append(...T`
${el("strong", "Add explicit cleanup:")} For resources not managed by DDE, use ${el("code", "on.disconnected")}
`)
),
@ -196,7 +194,7 @@ export function page({ pkg, info }){
el("dd", t`Use arrow functions or .bind() to preserve context`),
el("dt", t`Mixing declarative and imperative styles`),
el("dd", t`Choose one approach and be consistent throughout a component(s)`)
el("dd", t`Choose one approach and be consistent throughout a component`)
)
),

View File

@ -1,8 +1,8 @@
import { T, t } from "./utils/index.js";
export const info= {
title: t`Web Components`,
fullTitle: t`Using Web Components with dd<el>: Better Together`,
description: t`Using custom elements in combination with dd<el>`,
fullTitle: t`Using Web Components with DDE: Better Together`,
description: t`Using custom elements in combination with DDE`,
};
import { el } from "deka-dom-el";
@ -10,7 +10,7 @@ import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { mnemonic } from "./components/mnemonic/customElement-init.js";
import { code, pre } from "./components/code.html.js";
import { code } from "./components/code.html.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
const references= {
@ -49,55 +49,51 @@ const references= {
title: t`Everything you need to know about Shadow DOM (github repo praveenpuglia/shadow-dom-in-depth)`,
href: "https://github.com/praveenpuglia/shadow-dom-in-depth",
},
/** Decorators */
decorators: {
title: t`JavaScript Decorators: An In-depth Guide`,
href: "https://www.sitepoint.com/javascript-decorators-what-they-are/",
}
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
dd<el> pairs powerfully with ${el("a", references.mdn_web_components).append(el("strong", t`Web
Components`))} to create reusable, encapsulated custom elements with all the benefits of dd<el>s
declarative DOM construction and reactivity system.
el("p").append(...T`
DDE pairs powerfully with ${el("a", references.mdn_web_components).append(el("strong", t`Web Components`))}
to create reusable, encapsulated custom elements with all the benefits of DDE's declarative DOM
construction and reactivity system.
`),
el("div", { className: "callout" }).append(
el("h4", t`Why Combine dd<el> with Web Components?`),
el("h4", t`Why Combine DDE with Web Components?`),
el("ul").append(
el("li", t`Declarative DOM creation within your components`),
el("li", t`Reactive attribute updates through signals`),
el("li", t`Simplified event handling with the same events API`),
el("li", t`Clean component lifecycle management`),
),
el("li", t`Improved code organization with scopes`)
)
),
el(code, { src: fileURL("./components/examples/customElement/intro.js") }),
el(code, { src: fileURL("./components/examples/customElement/intro.js"), page_id }),
el(h3, t`Getting Started: Web Components Basics`),
el("p").append(T`
Web Components are a set of standard browser APIs that let you create custom HTML elements with
el("p").append(...T`
Web Components are a set of standard browser APIs that let you create custom HTML elements with
encapsulated functionality. They consist of three main technologies:
`),
el("ul").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Custom Elements:")} Create your own HTML tags with JS-defined behavior
`),
el("li").append(T`
${el("strong", "Shadow DOM:")} Encapsulate styles and markup within a component
el("li").append(...T`
${el("strong", "Shadow DOM:")} Encapsulate styles and markup within a component
`),
el("li").append(T`
${el("strong", "HTML Templates:")} Define reusable markup structures (${el("em",
"the dd<el> replaces this part")})
el("li").append(...T`
${el("strong", "HTML Templates:")} Define reusable markup structures
`)
),
el("p").append(T`
Lets start with a basic Custom Element example without dd<el> to establish the foundation:
el("p").append(...T`
Let's start with a basic Custom Element example without DDE to establish the foundation:
`),
el(code, { src: fileURL("./components/examples/customElement/native-basic.js") }),
el(code, { src: fileURL("./components/examples/customElement/native-basic.js"), page_id }),
el("div", { className: "note" }).append(
el("p").append(T`
el("p").append(...T`
For complete information on Web Components, see the
${el("a", references.mdn_custom_elements).append(el("strong", t`MDN documentation`))}.
Also, ${el("a", references.custom_elements_tips).append(el("strong", t`Handy Custom Elements Patterns`))}
@ -105,163 +101,159 @@ export function page({ pkg, info }){
`)
),
el(h3, t`dd<el> Integration: Step 1 - Event Handling`),
el("p").append(T`
The first step in integrating dd<el> with Web Components is enabling dd<el>s event system to work with your
el(h3, t`DDE Integration: Step 1 - Event Handling`),
el("p").append(...T`
The first step in integrating DDE with Web Components is enabling DDE's event system to work with your
Custom Elements. This is done with ${el("code", "customElementWithDDE")}, which makes your Custom Element
compatible with dd<el>s event handling. (${el("em").append(T`Notice that customElementWithDDE is
actually`)} ${el("a", { textContent: "decorator", ...references.decorators })})
compatible with DDE's event handling.
`),
el("div", { className: "function-table" }).append(
el("h4", t`customElementWithDDE`),
el("dl").append(
el("dt", t`Purpose`),
el("dd", t`Enables dd<el>s event system to work with your Custom Element`),
el("dd", t`Enables DDE's event system to work with your Custom Element`),
el("dt", t`Usage`),
el("dd", t`customElementWithDDE(YourElementClass)`),
el("dt", t`Benefits`),
el("dd", t`Allows using on.connected(), on.disconnected() or S.observedAttributes().`)
el("dd", t`Allows using on.connected(), on.disconnected(), etc. with your element`)
)
),
el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js") }),
el(example, { src: fileURL("./components/examples/customElement/customElementWithDDE.js"), page_id }),
el("div", { className: "tip" }).append(
el("p").append(T`
el("p").append(...T`
${el("strong", "Key Point:")} The ${el("code", "customElementWithDDE")} function adds event dispatching
to your Custom Element lifecycle methods, making them work seamlessly with dd<el>s event system.
to your Custom Element lifecycle methods, making them work seamlessly with DDE's event system.
`)
),
el(h3, t`dd<el> Integration: Step 2 - Rendering Components`),
el("p").append(T`
The next step is to use dd<el>s component rendering within your Custom Element. This is done with
${el("code", "customElementRender")}, which connects your dd<el> component function to the Custom Element.
el(h3, t`DDE Integration: Step 2 - Rendering Components`),
el("p").append(...T`
The next step is to use DDE's component rendering within your Custom Element. This is done with
${el("code", "customElementRender")}, which connects your DDE component function to the Custom Element.
`),
el("div", { className: "function-table" }).append(
el("h4", t`customElementRender`),
el("dl").append(
el("dt", t`Purpose`),
el("dd", t`Connects a dd<el> component function to a Custom Element`),
el("dd", t`Connects a DDE component function to a Custom Element`),
el("dt", t`Parameters`),
el("dd").append(
el("ol").append(
el("li", t`Target (usually this or this.shadowRoot)`),
el("li", t`Component function that returns a DOM tree`),
el("li", t`Optional: Attributes transformer function (empty by default or
S.observedAttributes)`)
el("li", t`Component function that returns a DOM tree`),
el("li", t`Optional: Attributes transformer function (default or S.observedAttributes)`)
)
),
el("dt", t`Returns`),
el("dd", t`The rendered DOM tree`)
)
),
el(example, { src: fileURL("./components/examples/customElement/dde.js") }),
el(example, { src: fileURL("./components/examples/customElement/dde.js"), page_id }),
el("div", { className: "note" }).append(
el("p").append(T`
In this example, were using Shadow DOM (${el("code", "this.attachShadow()")}) for encapsulation,
el("p").append(...T`
In this example, we're using Shadow DOM (${el("code", "this.attachShadow()")}) for encapsulation,
but you can also render directly to the element with ${el("code", "customElementRender(this, ...)")}.
`)
),
el(h3, t`Reactive Web Components with Signals`),
el("p").append(T`
One of the most powerful features of integrating dd<el> with Web Components is connecting HTML attributes
to dd<el>s reactive signals system. This creates truly reactive custom elements.
el("p").append(...T`
One of the most powerful features of integrating DDE with Web Components is connecting HTML attributes
to DDE's reactive signals system. This creates truly reactive custom elements.
`),
el("div", { className: "tip" }).append(
el("p").append(T`
el("p").append(...T`
${el("strong", "Two Ways to Handle Attributes:")}
`),
el("ol").append(
el("li").append(T`
Using standard attribute access (${el("code", "this.getAttribute(<name>)")}) - Passes attributes as
regular values (static)
el("li").append(...T`
${el("code", "observedAttributes")} - Passes attributes as regular values (static)
`),
el("li").append(T`
el("li").append(...T`
${el("code", "S.observedAttributes")} - Transforms attributes into signals (reactive)
`)
)
),
el("p").append(T`
Using the ${el("code", "S.observedAttributes")} creates a reactive connection between your elements
attributes and its internal rendering. When attributes change, your component automatically updates!
el("p").append(...T`
Using ${el("code", "S.observedAttributes")} creates a reactive connection between your element's attributes
and its internal rendering. When attributes change, your component automatically updates!
`),
el(example, { src: fileURL("./components/examples/customElement/observedAttributes.js") }),
el(example, { src: fileURL("./components/examples/customElement/observedAttributes.js"), page_id }),
el("div", { className: "callout" }).append(
el("h4", t`How S.observedAttributes Works`),
el("ol").append(
el("li", t`Takes each attribute listed in static observedAttributes`),
el("li", t`Creates a dd<el> signal for each one`),
el("li", t`Creates a DDE signal for each one`),
el("li", t`Automatically updates these signals when attributes change`),
el("li", t`Passes the signals to your component function`),
el("li", t`In opposite, updates of signals trigger attribute changes`),
el("li", t`Your component reacts to changes through signal subscriptions`)
)
),
el(h3, t`Working with Shadow DOM`),
el("p").append(T`
Shadow DOM provides encapsulation for your components styles and markup. When using dd<el> with Shadow DOM,
el("p").append(...T`
Shadow DOM provides encapsulation for your component's styles and markup. When using DDE with Shadow DOM,
you get the best of both worlds: encapsulation plus declarative DOM creation.
`),
el("div", { className: "illustration" }).append(
el("h4", t`Shadow DOM Encapsulation`),
el(pre, { content: `
<my-custom-element>
┌─────────────────────────┐
#shadow-root
el("pre").append(el("code", `
<my-custom-element>
  ┌─────────────────────────┐
    #shadow-root
Created with dd<el>
┌──────────────────┐
<div>
<h2>Title</h2>
<p>Content</p>
` })
      Created with DDE:
    ┌──────────────────┐
      <div>
       <h2>Title</h2>
       <p>Content</p>
`))
),
el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js") }),
el(example, { src: fileURL("./components/examples/customElement/shadowRoot.js"), page_id }),
el("p").append(T`
el("p").append(...T`
For more information on Shadow DOM, see
${el("a", { textContent: t`Using Shadow DOM`, ...references.mdn_shadow_dom_depth })}, or the comprehensive
${el("a", { textContent: t`Shadow DOM in Depth`, ...references.shadow_dom_depth })}.
`),
el(h3, t`Working with Slots`),
el("p").append(T`
el("p").append(...T`
Besides the encapsulation, the Shadow DOM allows for using the ${el("a", references.mdn_shadow_dom_slot).append(
el("strong", t`<slot>`), t` element(s)`)}. You can simulate this feature using ${el("code", "simulateSlots")}:
`),
el(example, { src: fileURL("./components/examples/customElement/simulateSlots.js") }),
el(example, { src: fileURL("./components/examples/customElement/simulateSlots.js"), page_id }),
el("div", { className: "function-table" }).append(
el("h4", t`simulateSlots`),
el("dl").append(
el("dt", t`Purpose`),
el("dd", t`Provides slot functionality when you cannot/do not want to use shadow DOM`),
el("dt", t`Parameters`),
el("dd", t`A mapping object of slot names to DOM elements`)
el("dd", t`A mapping object of slot names to DOM elements`)
)
),
el(h3, t`Best Practices for Web Components with dd<el>`),
el("p").append(T`
When combining dd<el> with Web Components, follow these recommendations:
el(h3, t`Best Practices for Web Components with DDE`),
el("p").append(...T`
When combining DDE with Web Components, follow these recommendations:
`),
el("ol").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Always use customElementWithDDE")} to enable event integration
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Prefer S.observedAttributes")} for reactive attribute connections
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Create reusable component functions")} that your custom elements render
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Use scope.host()")} to clean up event listeners and subscriptions
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Add setters and getters")} for better property access to your element
`)
),
@ -272,7 +264,7 @@ export function page({ pkg, info }){
el("dt", t`Events not firing properly`),
el("dd", t`Make sure you called customElementWithDDE before defining the element`),
el("dt", t`Attributes not updating`),
el("dd", t`Check that youve properly listed them in static observedAttributes`),
el("dd", t`Check that you've properly listed them in static observedAttributes`),
el("dt", t`Component not rendering`),
el("dd", t`Verify customElementRender is called in connectedCallback, not constructor`)
)

View File

@ -1,8 +1,8 @@
import { T, t } from "./utils/index.js";
export const info= {
title: t`Debugging`,
fullTitle: t`Debugging applications with dd<el>`,
description: t`Techniques for debugging applications using dd<el>, especially signals.`,
fullTitle: t`Debugging applications with deka-dom-el`,
description: t`Techniques for debugging applications using deka-dom-el, especially signals.`,
};
import { el } from "deka-dom-el";
@ -15,207 +15,178 @@ const fileURL= url=> new URL(url, import.meta.url);
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
el("p").append(...T`
Debugging is an essential part of application development. This guide provides techniques
and best practices for debugging applications built with dd<el>, with a focus on signals.
and best practices for debugging applications built with deka-dom-el, with a focus on signals.
`),
el(h3, t`Debugging signals`),
el("p").append(T`
el("p").append(...T`
Signals are reactive primitives that update the UI when their values change. When debugging signals,
you need to track their values, understand their dependencies, and identify why updates are or arent
happening.
you need to track their values, understand their dependencies, and identify why updates are or aren't happening.
`),
el("h4", t`Inspecting signal values`),
el("p").append(T`
The simplest way to debug a signal is to log its current value by calling the get or valueOf method:
el("p").append(...T`
The simplest way to debug a signal is to log its current value by calling the get method:
`),
el(code, { content: `
const signal = S(0);
console.log('Current value:', signal.valueOf());
`, language: "js" }),
el("div", { className: "warning" }).append(
el("p").append(T`
${el("code", "signal.get")} is OK, but in some situations may lead to unexpected results:
`),
el(code, { content: `
const signal = S(0);
const derived = S(()=> {
console.log('Current value:', signal.get());
// ↑ in rare cases this will register unwanted dependency
// but typically this is fine ↓
return signal.get() + 1;
});
`, language: "js" })
),
el("p").append(T`
You can also monitor signal changes by adding a listener:
const signal = S(0);
console.log('Current value:', signal.get());
// without triggering updates
console.log('Current value:', signal.valueOf());
`, page_id }),
el("p").append(...T`
You can also monitor signal changes by adding a listener:
`),
el(code, { content: `
// Log every time the signal changes
S.on(signal, value => console.log('Signal changed:', value));
`, language: "js" }),
el(code, {
content:
"// Log every time the signal changes\nS.on(signal, value => console.log('Signal changed:', value));",
page_id }),
el("h4", t`Debugging derived signals`),
el("p").append(T`
With derived signals (created with ${el("code", "S(() => computation))")}), debugging is a bit more complex
because the value depends on other signals. To understand why a derived signal isnt updating correctly:
el("p").append(...T`
With derived signals (created with ${el("code", "S(() => computation))")}), debugging is a bit more complex
because the value depends on other signals. To understand why a derived signal isn't updating correctly:
`),
el("ol").append(
el("li", t`Check that all dependency signals are updating correctly`),
el("li", t`Add logging/debugger inside the computation function to see when it runs`),
el("li", t`Add logging inside the computation function to see when it runs`),
el("li", t`Verify that the computation function actually accesses the signal values with .get()`)
),
el(example, { src: fileURL("./components/examples/debugging/consoleLog.js") }),
el("h4", t`Examining signal via DevTools`),
el("p").append(T`
${el("code", "<signal>.__dde_signal")} - A Symbol property used to identify and store the internal state of
signal objects. It contains the following information:
`),
el("ul").append(
// TODO: value?
el("li", t`listeners: A Set of functions called when the signal value changes`),
el("li", t`actions: Custom actions that can be performed on the signal`),
el("li", t`onclear: Functions to run when the signal is cleared`),
el("li", t`host: Reference to the host element/scope in which the signal was created`),
),
el("p").append(T`
…to determine the current value of the signal, call ${el("code", "signal.valueOf()")}. Dont hesitate to
use the debugger to inspect the signal object.
`),
el("h4", t`Debugging with breakpoints`),
el("p").append(T`
Effective use of breakpoints can help track signal flow:
`),
el("ul").append(
el("li").append(T`
Set breakpoints in signal update methods to track when values change
`),
el("li").append(T`
Use conditional breakpoints to only break when specific signals change to certain values
`),
el("li").append(T`
Set breakpoints in your signal computation functions to see when derived signals recalculate
`),
el("li").append(T`
Use performance profiling to identify bottlenecks in signal updates
`)
),
el(example, { src: fileURL("./components/examples/debugging/consoleLog.js"), page_id }),
el(h3, t`Common signal debugging issues`),
el("h4", t`Signal updates not triggering UI changes`),
el("p", t`If signal updates arent reflected in the UI, check:`),
el("p").append(...T`
If signal updates aren't reflected in the UI, check:
`),
el("ul").append(
el("li", t`That youre using signal.set() to update the value, not modifying objects/arrays directly`),
el("li", t`For mutable objects, ensure youre using actions or making proper copies before updating`),
el("li", t`That the signal is actually connected to the DOM element (check your S.el or attribute binding
code)`),
el("li").append(T`
That youre passing signal corecctly (without using ${el("code", "*.get()")}) and for ${el("code",
"S.el")} that you passing (derived) signals not a function (use ${el("code",
"S.el(S(()=> count.get() % 2), odd=> …)")}).
`),
el("li", t`That you're using signal.set() to update the value, not modifying objects/arrays directly`),
el("li", t`For mutable objects, ensure you're using actions or making proper copies before updating`),
el("li", t`That the signal is actually connected to the DOM element (check your S.el or attribute binding code)`)
),
el(code, { src: fileURL("./components/examples/debugging/mutations.js") }),
el(code, { src: fileURL("./components/examples/debugging/mutations.js"), page_id }),
el("h4", t`Memory leaks with signal listeners`),
el("p").append(T`
el("p").append(...T`
Signal listeners can cause memory leaks if not properly cleaned up. Always use AbortSignal
to cancel listeners when they are used ouside the dd<el> knowledge (el, assign, S.el, … auto cleanup
unnecessarily signals automatically).
to cancel listeners.
`),
el("h4", t`Performance issues with frequently updating signals`),
el("p", t`If you notice performance issues with signals that update very frequently:`),
el("p").append(...T`
If you notice performance issues with signals that update very frequently:
`),
el("ul").append(
el("li", t`Consider debouncing or throttling signal updates`),
el("li", t`Make sure derived signals dont perform expensive calculations unnecessarily`),
el("li", t`Make sure derived signals don't perform expensive calculations unnecessarily`),
el("li", t`Keep signal computations focused and minimal`)
),
el(code, { src: fileURL("./components/examples/debugging/debouncing.js") }),
el(code, { src: fileURL("./components/examples/debugging/debouncing.js"), page_id }),
el(h3, t`Browser DevTools tips for components and reactivity`),
el("p").append(T`
When debugging in the browser, dd<el> provides several helpful DevTools-friendly features:
el(h3, t`Browser DevTools tips for deka-dom-el`),
el("p").append(...T`
When debugging in the browser, deka-dom-el provides several helpful DevTools-friendly features:
`),
el("h4", t`Identifying components in the DOM`),
el("p").append(...T`
deka-dom-el marks components in the DOM with special comment nodes to help you identify component boundaries.
Components created with ${el("code", "el(ComponentFunction)")} are marked with comment nodes
${el("code", "<!--<dde:mark type=\"component\" name=\"MyComponent\" host=\"parentElement\"/>-->")} and
includes:
`),
el("ul").append(
el("li", "type - Identifies the type of marker (\"component\", \"reactive\", or \"later\")"),
el("li", "name - The name of the component function"),
el("li", "host - Indicates whether the host is \"this\" (for DocumentFragments) or \"parentElement\""),
),
el("h4", t`Finding reactive elements in the DOM`),
el("p").append(T`
When using ${el("code", "S.el()")}, dd<el> creates reactive elements in the DOM
el("p").append(...T`
When using ${el("code", "S.el()")}, deka-dom-el creates reactive elements in the DOM
that are automatically updated when signal values change. These elements are wrapped in special
comment nodes for debugging (to be true they are also used internally, so please do not edit them by hand):
`),
el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.html") }),
el("p").append(T`
This is particularly useful when debugging why a reactive section isnt updating as expected.
el(code, { src: fileURL("./components/examples/debugging/dom-reactive-mark.js"), page_id }),
el("p").append(...T`
This is particularly useful when debugging why a reactive section isn't updating as expected.
You can inspect the elements between the comment nodes to see their current state and the
signal connections through \`__dde_reactive\` of the host element.
`),
el("h4", t`Identifying components in the DOM`),
el("p").append(T`
dd<el> marks components in the DOM with special comment nodes to help you identify component boundaries.
Components created with ${el("code", "el(MyComponent)")} are marked with comment nodes
${el("code", `<!--<dde:mark type="component" name="MyComponent" host="parentElement"/>-->`)} and
includes:
el("h4", t`DOM inspection properties`),
el("p").append(...T`
Elements created with the deka-dom-el library have special properties to aid in debugging:
`),
el("ul").append(
el("li", t`type - Identifies the type of marker ("component", "reactive", …)`),
el("li", t`name - The name of the component function`),
el("li", t`host - Indicates whether the host is "this" (for DocumentFragments) or "parentElement"`),
),
el("h4", t`Identifying reactive elements in the DOM`),
el("p").append(T`
You can inspect (host) element relationships and bindings with signals in the DevTools console using
${el("code", "$0.__dde_reactive")} (for currently selected element). In the console you will see a list of
${el("code", `[ [ signal, listener ], element, property ]`)}, where:
`),
el("ul").append(
el("li", t`signal — the signal triggering the changes`),
el("li", t`listener — the listener function (this is an internal function for dd<el>)`),
el("li", t`element — the DOM element that is bound to the signal`),
el("li", t`property — the attribute or property name which is changing based on the signal`),
),
el("p").append(T`
…the structure of \`__dde_reactive\` utilizes the browsers behavior of packing the first field,
so you can see the element and property that changes in the console right away. These properties make it
easier to understand the reactive structure of your application when inspecting elements.
`),
el(example, { src: fileURL("./components/examples/signals/debugging-dom.js") }),
el("p", { className: "note" }).append(T`
el("p").append(...T`
${el("code", "<element>.__dde_reactive")} - An array property on DOM elements that tracks signal-to-element
relationships. This allows you to quickly identify which elements are reactive and what signals theyre
bound to. Each entry in the array contains:
relationships. This allows you to quickly identify which elements are reactive and what signals they're
bound to. Each entry in the array contains:
`),
el("ul").append(
el("li", "A pair of signal and listener function: [signal, listener]"),
el("li", "Additional context information about the element or attribute"),
el("li", "Automatically managed by signal.el(), signal.observedAttributes(), and processReactiveAttribute()")
),
el("p").append(...T`
These properties make it easier to understand the reactive structure of your application when inspecting elements.
`),
el(example, { src: fileURL("./components/examples/signals/debugging-dom.js"), page_id }),
el("h4", t`Examining signal connections`),
el("p").append(...T`
${el("code", "<signal>.__dde_signal")} - A Symbol property used to identify and store the internal state of
signal objects. It contains the following information:
`),
el("ul").append(
el("li", "listeners: A Set of functions called when the signal value changes"),
el("li", "actions: Custom actions that can be performed on the signal"),
el("li", "onclear: Functions to run when the signal is cleared"),
el("li", "host: Reference to the host element/scope"),
el("li", "defined: Stack trace information for debugging"),
el("li", "readonly: Boolean flag indicating if the signal is read-only")
),
el("p").append(...T`
…to determine the current value of the signal, call ${el("code", "signal.valueOf()")}.
`),
el("p").append(...T`
You can inspect (host) element relationships and bindings with signals in the DevTools console using
${el("code", "$0.__dde_reactive")} (for currently selected element). In the console you will see a list of
${el("code", "[ [ signal, listener ], element, property ]")}, where:
`),
el("ul").append(
el("li", "signal — the signal triggering the changes"),
el("li", "listener — the listener function (this is an internal function for DDE)"),
el("li", "element — the DOM element that is bound to the signal"),
el("li", "property — the attribute or property name which is changing based on the signal"),
),
el("p").append(...T`
…the structure of \`__dde_reactive\` utilizes the browser's behavior of packing the first field,
so you can see the element and property that changes in the console right away.
`),
el("h4", t`Inspecting events and listeners in DevTools`),
el("p").append(T`
Modern browser DevTools provide built-in tools for inspecting event listeners attached to DOM elements.
For example, in Firefox and Chrome, you can:
el("h4", t`Debugging with breakpoints`),
el("p").append(...T`
Effective use of breakpoints can help track signal flow:
`),
el("ol").append(
el("li", t`Select an element in the Elements/Inspector panel`),
el("li", t`Look for the "Event Listeners" tab or section`),
el("li", t`See all event listeners attached to the element, including those added by dd<el>`)
),
el("p").append(T`
Additionally, dd<el> provides special markers in the DOM that help identify debug information.
Look for comments with ${el("code", "dde:mark")}, ${el("code", "dde:disconnected")} and ${el("code",
"__dde_reactive")} which indicate components, reactive regions, and other internal relationships:
`),
el("figure").append(
el("img", {
src: "./assets/devtools.png",
alt: "Screenshot of DevTools showing usage of “event” button to inspect event listeners",
}),
el("figcaption", t`Firefox DevTools showing dd<el> debugging information with event listeners and reactive
markers`)
el("ul").append(
el("li").append(...T`
Set breakpoints in signal update methods to track when values change
`),
el("li").append(...T`
Use conditional breakpoints to only break when specific signals change to certain values
`),
el("li").append(...T`
Set breakpoints in your signal computation functions to see when derived signals recalculate
`),
el("li").append(...T`
Use performance profiling to identify bottlenecks in signal updates
`)
),
);
}

View File

@ -1,8 +1,8 @@
import { T, t } from "./utils/index.js";
export const info= {
title: t`Extensions and 3rd Party`,
fullTitle: t`Extending dd<el> with Third-Party Functionalities`,
description: t`How to extend dd<el> with third-party libraries and custom functionalities.`,
fullTitle: t`Extending deka-dom-el with Third-Party Functionalities`,
description: t`How to extend deka-dom-el with third-party libraries and custom functionalities.`,
};
import { el } from "deka-dom-el";
@ -14,256 +14,193 @@ const fileURL= url=> new URL(url, import.meta.url);
/** @param {import("./types.js").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("p").append(T`
dd<el> is designed with extensibility in mind. This page covers how to separate
el("p").append(...T`
deka-dom-el is designed with extensibility in mind. This page covers how to separate
third-party functionalities and integrate them seamlessly with the library, focusing on
proper resource cleanup and interoperability.
`),
el(h3, t`DOM Element Extensions with Addons`),
el("p").append(T`
The primary method for extending DOM elements in dd<el> is through the Addon pattern.
Addons are functions that take an element and applying some functionality to it. This pattern enables
a clean, functional approach to element enhancement.
el("p").append(...T`
The primary method for extending DOM elements in deka-dom-el is through the Addon pattern.
Addons are functions that take an element and applying some functionality to it. This pattern enables a
clean, functional approach to element enhancement.
`),
el("div", { className: "callout" }).append(
el("h4", t`What are Addons?`),
el("p").append(T`
el("p").append(...T`
Addons are simply functions with the signature: (element) => void. They:
`),
el("ul").append(
el("li", t`Accept a DOM element as input`),
el("li", t`Accept a DOM element as input`),
el("li", t`Apply some behavior, property, or attribute to the element`),
)
),
el(code, { content: `
// Basic structure of an addon
function myAddon(config) {
return function(element) {
// Apply functionality to element
element.dataset.myAddon = config.option;
};
}
// Basic structure of an addon
function myAddon(config) {
return function(element) {
// Apply functionality to element
element.dataset.myAddon = config.option;
};
}
// Using an addon
el("div", { id: "example" }, myAddon({ option: "value" }));
`, language: "js" }),
// Using an addon
el("div", { id: "example" }, myAddon({ option: "value" }));
`.trim(), page_id }),
el(h3, t`Resource Cleanup with Abort Signals`),
el("p").append(T`
el("p").append(...T`
When extending elements with functionality that uses resources like event listeners, timers,
or external subscriptions, its critical to clean up these resources when the element is removed
from the DOM. dd<el> provides utilities for this through AbortSignal integration.
or external subscriptions, it's critical to clean up these resources when the element is removed
from the DOM. deka-dom-el provides utilities for this through AbortSignal integration.
`),
el("div", { className: "tip" }).append(
el("p").append(T`
The ${el("code", "scope.signal")} property creates an AbortSignal that automatically
triggers when an element is disconnected from the DOM, making cleanup much easier to manage.
el("p").append(...T`
The ${el("code", "on.disconnectedAsAbort")} utility creates an AbortSignal that automatically
triggers when an element is disconnected from the DOM, making cleanup much easier to manage.
`)
),
el(code, { content: `
// Third-party library addon with proper cleanup
function externalLibraryAddon(config, signal) {
return function(element) {
// Initialize the third-party library
const instance = new ExternalLibrary(element, config);
// Third-party library addon with proper cleanup
function externalLibraryAddon(config, signal) {
return function(element) {
// Initialize the third-party library
const instance = new ExternalLibrary(element, config);
// Set up cleanup when the element is removed
signal.addEventListener('abort', () => {
instance.destroy();
});
// Set up cleanup when the element is removed
signal.addEventListener('abort', () => {
instance.destroy();
});
return element;
};
}
// dde component
function Component(){
const { signal }= scope;
return el("div", null, externalLibraryAddon({ option: "value" }, signal));
}
`, language: "js" }),
return element;
};
}
// dde component
function Component(){
const { host }= scope;
const signal= on.disconnectedAsAbort(host);
return el("div", null, externalLibraryAddon({ option: "value" }, signal));
}
`.trim(), page_id }),
el(h3, t`Building Library-Independent Extensions`),
el("p").append(T`
When creating extensions, its a good practice to make them as library-independent as possible.
el("p").append(...T`
When creating extensions, it's a good practice to make them as library-independent as possible.
This approach enables better interoperability and future-proofing.
`),
el("div", { className: "illustration" }).append(
el("h4", t`Library-Independent vs. Library-Dependent Extension`),
el("div", { className: "tabs" }).append(
el("div", { className: "tab" }).append(
el("h5", t`✅ Library-Independent`),
el(code, { content: `
function enhancementElement({ signal, ...config }) {
// do something
return function(element) {
// do something
signal.addEventListener('abort', () => {
// do cleanup
});
};
}
`, language: "js" })
function enhancementElement({ signal, ...config }) {
// do something
return function(element) {
// do something
signal.addEventListener('abort', () => {
// do cleanup
});
};
}
`.trim(), page_id })
),
el("div", { className: "tab" }).append(
el("h5", t`⚠️ Library-Dependent`),
el(code, { content: `
// Tightly coupled to dd<el>
function enhancementElement(config) {
return function(element) {
// do something
on.disconnected(()=> {
// do cleanup
})(element);
};
}
`, language: "js" })
// Tightly coupled to deka-dom-el
function enhancementElement(config) {
return function(element) {
// do something
on.disconnected(()=> {
// do cleanup
})(element);
};
}
`.trim(), page_id })
)
)
),
el(h3, t`Signal Extensions and Factory Patterns`),
el("p").append(T`
Unlike DOM elements, signal functionality in dd<el> currently lacks a standardized
el(h3, t`Signal Extensions and Future Compatibility`),
el("p").append(...T`
Unlike DOM elements, signal functionality in deka-dom-el currently lacks a standardized
way to create library-independent extensions. This is because signals are implemented
differently across libraries.
`),
el("div", { className: "note" }).append(
el("p").append(T`
el("p").append(...T`
In the future, JavaScript may include built-in signals through the
${el("a", { href: "https://github.com/tc39/proposal-signals", textContent: "TC39 Signals Proposal" })}.
dd<el> is designed with future compatibility in mind and will hopefully support these
deka-dom-el is designed with future compatibility in mind and will hopefully support these
native signals without breaking changes when they become available.
`)
),
el("h4", t`The Signal Factory Pattern`),
el("p").append(T`
A powerful approach for extending signal functionality is the "Signal Factory" pattern.
This approach encapsulates specific behavior in a function that creates and configures a signal.
`),
el(code, { content: `
/**
* Creates a signal for managing route state
*
* @param {typeof S} signal - The signal constructor
*/
function routerSignal(signal){
const initial = location.hash.replace("#", "") || "all";
return signal(initial, {
/**
* Set the current route
* @param {"all"|"active"|"completed"} hash - The route to set
*/
set(hash){
location.hash = hash;
this.value = hash;
}
});
}
// Usage
const pageS = routerSignal(S);
// Update URL hash and signal value in one operation
S.action(pageS, "set", "active");
// React to signal changes in the UI
el("nav").append(
el("a", {
href: "#",
className: S(()=> pageS.get() === "all" ? "selected" : ""),
textContent: "All"
})
);
`, language: "js" }),
el("div", { className: "callout" }).append(
el("h4", t`Benefits of Signal Factories`),
el("ul").append(
el("li", t`Encapsulate related behavior in a single, reusable function`),
el("li", t`Create domain-specific signals with custom actions`),
el("li", t`Improve maintainability by centralizing similar logic`),
el("li", t`Enable better testability by accepting the signal constructor as a parameter`),
el("li", t`Create a clear semantic boundary around related state operations`)
)
),
el("p").append(T`
Note how the factory accepts the signal constructor as a parameter, making it easier to test
and potentially migrate to different signal implementations in the future.
`),
el("h4", t`Other Signal Extension Approaches`),
el("p").append(T`
For simpler cases, you can also extend signals with clear interfaces and isolation to make
el("p").append(...T`
For now, when extending signals functionality, focus on clear interfaces and isolation to make
future migration easier.
`),
el(code, { content: `
// Signal extension with clear interface
function createEnhancedSignal(initialValue) {
const signal = S(initialValue);
// Signal extension with clear interface
function createEnhancedSignal(initialValue) {
const signal = S(initialValue);
// Extension functionality
const increment = () => signal.set(signal.get() + 1);
const decrement = () => signal.set(signal.get() - 1);
// Extension functionality
const increment = () => signal.set(signal.get() + 1);
const decrement = () => signal.set(signal.get() - 1);
// Return the original signal with added methods
return { signal, increment, decrement };
}
// Return the original signal with added methods
return Object.assign(signal, {
increment,
decrement
});
}
// Usage
const counter = createEnhancedSignal(0);
el("button", { textContent: "Increment", onclick: () => counter.increment() });
el("div", S.text\`Count: \${counter}\`);
`, language: "js" }),
el("div", { className: "tip" }).append(
el("p").append(T`
When designing signal extensions, consider creating specialized signals for common patterns like:
forms, API requests, persistence, animations, or routing. These can significantly reduce
boilerplate code in your applications.
`)
),
// Usage
const counter = createEnhancedSignal(0);
el("button")({ onclick: () => counter.increment() }, "Increment");
el("div", S.text\`Count: \${counter}\`);
`.trim(), page_id }),
el(h3, t`Using Signals Independently`),
el("p").append(T`
While signals are tightly integrated with DDEs DOM elements, you can also use them independently.
el("p").append(...T`
While signals are tightly integrated with DDE's DOM elements, you can also use them independently.
This can be useful when you need reactivity in non-UI code or want to integrate with other libraries.
`),
el("p").append(T`
el("p").append(...T`
There are two ways to import signals:
`),
el("ol").append(
el("li").append(T`
${el("strong", "Standard import")}: ${el("code", `import { S } from "deka-dom-el/signals";`)}
— This automatically registers signals with DDEs DOM reactivity system
el("li").append(...T`
${el("strong", "Standard import")}: ${el("code", "import { S } from \"deka-dom-el/signals\";")}
— This automatically registers signals with DDE's DOM reactivity system
`),
el("li").append(T`
${el("strong", "Independent import")}: ${el("code", `import { S } from "deka-dom-el/src/signals-lib";`)}
el("li").append(...T`
${el("strong", "Independent import")}: ${el("code", "import { S } from \"deka-dom-el/src/signals-lib\";")}
— This gives you just the signal system without DOM integration
`)
),
el(code, { content: `
// Independent signals without DOM integration
import { signal, isSignal } from "deka-dom-el/src/signals-lib";
el(code, { content: `// Independent signals without DOM integration
import { signal as S, isSignal } from "deka-dom-el/src/signals-lib";
// Create and use signals as usual
const count = signal(0);
const doubled = signal(() => count.get() * 2);
// Create and use signals as usual
const count = S(0);
const doubled = S(() => count.get() * 2);
// Subscribe to changes
signal.on(count, value => console.log(value));
// Subscribe to changes
S.on(count, value => console.log(value));
// Update signal value
count.set(5); // Logs: 5
console.log(doubled.get()); // 10
`, language: "js" }),
el("p").append(T`
// Update signal value
count.set(5); // Logs: 5
console.log(doubled.get()); // 10`, page_id }),
el("p").append(...T`
The independent signals API includes all core functionality (${el("code", "S()")}, ${el("code", "S.on()")},
${el("code", "S.action()")}).
`),
el("div", { className: "callout" }).append(
el("div", { class: "callout" }).append(
el("h4", t`When to Use Independent Signals`),
el("ul").append(
el("li", t`For non-UI state management in your application`),
@ -274,27 +211,23 @@ export function page({ pkg, info }){
el(h3, t`Best Practices for Extensions`),
el("ol").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Use AbortSignals for cleanup:")} Always implement proper resource cleanup with
${el("code", "scope.signal")} or similar mechanisms
${el("code", "on.disconnectedAsAbort")} or similar mechanisms
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Separate core logic from library adaptation:")} Make your core functionality work
with standard DOM APIs when possible
`),
el("li").append(T`
${el("strong", "Use signal factories for common patterns:")} Create reusable signal factories that encapsulate
domain-specific behavior and state logic
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Document clearly:")} Provide clear documentation on how your extension works
and what resources it uses
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Follow the Addon pattern:")} Keep to the (element) => element signature for
DOM element extensions
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Avoid modifying global state:")} Extensions should be self-contained and not
affect other parts of the application
`)
@ -308,17 +241,14 @@ export function page({ pkg, info }){
are disconnected`),
el("dt", t`Tight coupling with library internals`),
el("dd", t`Focus on standard DOM APIs and clean interfaces rather than depending on dd<el>
el("dd", t`Focus on standard DOM APIs and clean interfaces rather than depending on deka-dom-el
implementation details`),
el("dt", t`Mutating element prototypes`),
el("dd", t`Prefer compositional approaches with addons over modifying element prototypes`),
el("dt", t`Duplicating similar signal logic across components`),
el("dd", t`Use signal factories to encapsulate and reuse related signal behavior`),
el("dt", t`Complex initialization in addons`),
el("dd", t`Split complex logic into a separate initialization function that the addon can call`)
el("dd", t`Split complex logic into a separate initialization function that the addon can call`)
)
)
);

View File

@ -1,400 +0,0 @@
import { T, t } from "./utils/index.js";
export const info= {
title: t`Performance Optimization`,
fullTitle: t`Performance Optimization with dd<el>`,
description: t`Techniques for optimizing your dd<el> applications, focusing on memoization and efficient rendering.`,
};
import { el } from "deka-dom-el";
import { simplePage } from "./layout/simplePage.html.js";
import { example } from "./components/example.html.js";
import { h3 } from "./components/pageUtils.html.js";
import { code } from "./components/code.html.js";
import { mnemonic } from "./components/mnemonic/optimization-init.js";
/** @param {string} url */
const fileURL= url=> new URL(url, import.meta.url);
const references= {
/** memo documentation */
memo_docs: {
title: t`dd<el> memo API documentation`,
href: "https://github.com/jaandrle/deka-dom-el#memo-api",
},
/** AbortController */
mdn_abort: {
title: t`MDN documentation for AbortController`,
href: "https://developer.mozilla.org/en-US/docs/Web/API/AbortController",
},
/** Performance API */
mdn_perf: {
title: t`MDN documentation for Web Performance API`,
href: "https://developer.mozilla.org/en-US/docs/Web/API/Performance_API",
},
/** Virtual DOM */
virtual_dom: {
title: t`Virtual DOM concept explanation`,
href: "https://reactjs.org/docs/faq-internals.html#what-is-the-virtual-dom",
},
/** DocumentFragment */
mdn_fragment: {
title: t`MDN documentation for DocumentFragment`,
href: "https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment",
}
};
/** @param {import("./types.d.ts").PageAttrs} attrs */
export function page({ pkg, info }){
return el(simplePage, { info, pkg }).append(
el("p").append(T`
As your applications grow, performance becomes increasingly important. dd<el> provides several
techniques to optimize rendering performance, especially when dealing with large lists or frequently
updating components. This guide focuses on memoization and other optimization strategies.
`),
el("div", { className: "callout" }).append(
el("h4", t`dd<el> Performance Optimization: Key Benefits`),
el("ul").append(
el("li", t`Efficient memoization system for component reuse`),
el("li", t`Targeted re-rendering without virtual DOM overhead`),
el("li", t`Memory management through AbortSignal integration`),
el("li", t`Optimized signal updates for reactive UI patterns`),
el("li", t`Simple debugging for performance bottlenecks`)
)
),
el(code, { src: fileURL("./components/examples/optimization/intro.js") }),
el(h3, t`Memoization with memo: Native vs dd<el>`),
el("p").append(T`
In standard JavaScript applications, optimizing list rendering often involves manual caching
or relying on complex virtual DOM diffing algorithms. dd<el>'s ${el("code", "memo")} function
provides a simpler, more direct approach:
`),
el("div", { className: "illustration" }).append(
el("div", { className: "comparison" }).append(
el("div").append(
el("h5", t`Without Memoization`),
el(code, { content: `
// Each update to todosArray recreates all elements
function renderTodos(todosArray) {
return el("ul").append(
S.el(todosArray, todos => todos.map(todo=>
el("li", {
textContent: todo.text,
dataState: todo.completed ? "completed" : "",
})
))
);
}
`, language: "js" })
),
el("div").append(
el("h5", t`With dd<el>'s memo`),
el(code, { content: `
// With dd<el>s memoization
function renderTodos(todosArray) {
return el("ul").append(
S.el(todosArray, todos => todos.map(todo=>
// Reuses DOM elements when items havent changed
memo(todo.key, () =>
el("li", {
textContent: todo.text,
dataState: todo.completed ? "completed" : "",
})
)))
);
}
`, language: "js" })
)
)
),
el("p").append(T`
The ${el("a", references.memo_docs).append(el("code", "memo"))} function in dd<el> allows you to
cache and reuse DOM elements instead of recreating them on every render, which can
significantly improve performance for components that render frequently or contain heavy computations.
`),
el("p").append(T`
The memo system is particularly useful for:
`),
el("ul").append(
el("li", t`Lists that update frequently but where most items remain the same`),
el("li", t`Components with expensive rendering operations`),
el("li", t`Optimizing signal-driven UI updates`)
),
el(h3, t`Using memo with Signal Rendering`),
el("p").append(T`
The most common use case for memoization is within ${el("code", "S.el()")} when rendering lists with
${el("code", "map()")}:
`),
el(code, { content: `
S.el(todosSignal, todos =>
el("ul").append(
...todos.map(todo =>
// Use a unique identifiers
memo(todo.id, () =>
el(TodoItem, todo)
))))
`, language: "js" }),
el("p").append(T`
The ${el("code", "memo")} function in this context:
`),
el("ol").append(
el("li", t`Takes a unique key (todo.id) to identify this item`),
el("li", t`Caches the element created by the generator function`),
el("li", t`Returns the cached element on subsequent renders if the key remains the same`),
el("li", t`Only calls the generator function when rendering an item with a new key`)
),
el(example, { src: fileURL("./components/examples/optimization/memo.js") }),
el(h3, t`Creating Memoization Scopes`),
el("p").append(T`
The ${el("code", "memo()")} uses cache store defined via the ${el("code", "memo.scope")} function.
That is actually what the ${el("code", "S.el")} is doing under the hood:
`),
el(code, { content: `
import { memo } from "deka-dom-el";
// Create a memoization scope
const renderItem = memo.scope(function(item) {
return el().append(
el("h3", item.title),
el("p", item.description),
// Expensive rendering operations...
memo(item, ()=> el("div", { className: "expensive-component" }))
);
});
// Use the memoized function
const items = [/* array of items */];
const container = el("div").append(
...items.map(item => renderItem(item))
);
`, language: "js" }),
el("p").append(T`
The scope function accepts options to customize its behavior:
`),
el(code, { content: `
const renderList = memo.scope(function(list) {
return list.map(item =>
memo(item.id, () => el(ItemComponent, item))
);
}, {
// Only keep the cache from the most recent render
onlyLast: true,
// Clear cache when signal is aborted
signal: controller.signal
});
`, language: "js" }),
el("p").append(T`
You can use custom memo scope as function in (e. g. ${el("code", "S.el(signal, renderList)")}) and as
(Abort) signal use ${el("code", "scope.signal")}.
`),
el("div", { className: "function-table" }).append(
el("dl").append(
el("dt", t`onlyLast Option`),
el("dd").append(T`Only keeps the cache from the most recent function call,
which is useful when the entire collection is replaced. ${el("strong", "This is default behavior of ")
.append(el("code", "S.el"))}!`),
el("dt", t`signal Option`),
el("dd").append(T`An ${el("a", references.mdn_abort).append(el("code", "AbortSignal"))}
that will clear the cache when aborted, helping with memory management`)
)
),
el(h3, t`Additional Optimization Techniques`),
el("h4", t`Minimizing Signal Updates`),
el("p").append(T`
Signals are efficient, but unnecessary updates can impact performance:
`),
el("ul").append(
el("li", t`For frequently updating values (like scroll position), consider debouncing`),
el("li", t`Keep signal computations small and focused`),
),
el("h4", t`Optimizing List Rendering`),
el("p").append(T`
Beyond memoization, consider these approaches for optimizing list rendering:
`),
el("ul").append(
el("li", t`Virtualize long lists to only render visible items`),
el("li", t`Use stable, unique keys for list items`),
el("li", t`Batch updates to signals that drive large lists`),
el("li", t`Consider using a memo scope for the entire list component`)
),
el("div", { className: "tip" }).append(
el("p").append(T`
Memoization works best when your keys are stable and unique. Use IDs or other persistent
identifiers rather than array indices, which can change when items are reordered.
`),
el("p").append(T`
Alternatively you can use any “jsonable” value as key, when the primitive values arent enough.
`)
),
el("h4", t`Memory Management`),
el("p").append(T`
To prevent memory leaks and reduce memory consumption:
`),
el("ul").append(
el("li", t`Clear memo caches when components are removed`),
el("li", t`Use AbortSignals to manage memo lifetimes`),
el("li", t`Call S.clear() on signals that are no longer needed`),
el("li", t`Remove event listeners when elements are removed from the DOM`)
),
el("h4", t`Choosing the Right Optimization Approach`),
el("p").append(T`
While ${el("code", "memo")} is powerful, different scenarios call for different optimization techniques:
`),
el("div", { className: "function-table" }).append(
el("dl").append(
el("dt", t`memo`),
el("dd").append(T`
Best for list rendering where items rarely change or only their properties update.
${el("code", "todos.map(todo => memo(todo.id, () => el(TodoItem, todo)))")}
Use when you need to cache and reuse DOM elements to avoid recreating them on every render.
`),
el("dt", t`Signal computations`),
el("dd").append(T`
Ideal for derived values that depend on other signals and need to auto-update.
${el("code", "const totalPrice = S(() => items.get().reduce((t, i) => t + i.price, 0))")}
Use when calculated values need to stay in sync with changing source data.
`),
el("dt", t`Debouncing/Throttling`),
el("dd").append(T`
Essential for high-frequency events (scroll, resize) or rapidly changing input values.
${el("code", "debounce(e => searchQuery.set(e.target.value), 300)")}
Use to limit the rate at which expensive operations execute when triggered by fast events.
`),
el("dt", t`memo.scope`),
el("dd").append(T`
Useful for using memoization inside any function: ${el("code",
"const renderList = memo.scope(items => items.map(...))")}. Use to create isolated memoization
contexts that can be cleared or managed independently.
`),
el("dt", t`Stateful components`),
el("dd").append(T`
For complex UI components with internal state management.
${el("code", "el(ComplexComponent, { initialState, onChange })")}
Use when a component needs to encapsulate and manage its own state and lifecycle.
`)
)
),
el(h3, t`Known Issues and Limitations`),
el("p").append(T`
While memoization is a powerful optimization technique, there are some limitations and edge cases to be aware of:
`),
el("h4", t`Document Fragments and Memoization`),
el("p").append(T`
One important limitation to understand is how memoization interacts with
${el("a", references.mdn_fragment).append("DocumentFragment")} objects.
Functions like ${el("code", "S.el")} internally use DocumentFragment to efficiently handle multiple elements,
but this can lead to unexpected behavior with memoization.
`),
el(code, { content: `
// This pattern can lead to unexpected behavior
const memoizedFragment = memo("key", () => {
// Creates a DocumentFragment internally
return S.el(itemsSignal, items => items.map(item => el("div", item)));
});
// After the fragment is appended to the DOM, it becomes empty
container.append(memoizedFragment);
// On subsequent renders, the cached fragment is empty!
container.append(memoizedFragment); // Nothing gets appended
`, language: "js" }),
el("p").append(T`
This happens because a DocumentFragment is emptied when it's appended to the DOM. When the fragment
is cached by memo and reused, it's already empty.
`),
el("div", { className: "tip" }).append(
el("h5", t`Solution: Memoize Individual Items`),
el(code, { content: `
// Correct approach: memoize the individual items, not the fragment
S.el(itemsSignal, items => items.map(item =>
memo(item.id, () => el("div", item))
));
// Or use a container element instead of relying on a fragment
memo("key", () =>
el("div", { className: "item-container" }).append(
S.el(itemsSignal, items => items.map(item => el("div", item)))
)
);
`, language: "js" })
),
el("p").append(T`
Generally, you should either:
`),
el("ol").append(
el("li", t`Memoize individual items within the collection, not the entire collection result`),
el("li", t`Wrap the result in a container element instead of relying on fragment behavior`),
el("li", t`Be aware that S.el() and similar functions that return multiple elements are using fragments internally`)
),
el("div", { className: "note" }).append(
el("p").append(T`
This limitation isn't specific to dd<el> but is related to how DocumentFragment works in the DOM.
Once a fragment is appended to the DOM, its child nodes are moved from the fragment to the target element,
leaving the original fragment empty.
`)
),
el(h3, t`Performance Debugging`),
el("p").append(T`
To identify performance bottlenecks in your dd<el> applications:
`),
el("ol").append(
el("li").append(T`Use ${el("a", references.mdn_perf).append("browser performance tools")} to profile
rendering times`),
el("li", t`Check for excessive signal updates using S.on() listeners with console.log`),
el("li", t`Verify memo usage by inspecting cache hit rates`),
el("li", t`Look for components that render more frequently than necessary`)
),
el("div", { className: "note" }).append(
el("p").append(T`
For more details on debugging, see the ${el("a", { href: "p07-debugging.html", textContent: "Debugging" })} page.
`)
),
el(h3, t`Best Practices for Optimized Rendering`),
el("ol").append(
el("li").append(T`
${el("strong", "Use memo for list items:")} Memoize items in lists, especially when they contain complex components.
`),
el("li").append(T`
${el("strong", "Clean up with AbortSignals:")} Connect memo caches to component lifecycles using AbortSignals.
`),
el("li").append(T`
${el("strong", "Profile before optimizing:")} Identify actual bottlenecks before adding optimization.
`),
el("li").append(T`
${el("strong", "Use derived signals:")} Compute derived values efficiently with signal computations.
`),
el("li").append(T`
${el("strong", "Avoid memoizing fragments:")} Memoize individual elements or use container elements
instead of DocumentFragments.
`)
),
el(mnemonic),
);
}

View File

@ -1,8 +1,8 @@
import { T, t } from "./utils/index.js";
export const info= {
title: t`Server-Side Rendering (SSR)`,
fullTitle: t`Server-Side Rendering with dd<el>`,
description: t`Using dd<el> for server-side rendering with jsdom to generate static HTML.`,
fullTitle: t`Server-Side Rendering with deka-dom-el`,
description: t`Using deka-dom-el for server-side rendering with jsdom to generate static HTML.`,
};
import { el } from "deka-dom-el";
@ -14,40 +14,27 @@ const fileURL= url=> new URL(url, import.meta.url);
/** @param {import("./types.js").PageAttrs} attrs */
export function page({ pkg, info }){
const page_id= info.id;
return el(simplePage, { info, pkg }).append(
el("div", { className: "warning" }).append(
el("p").append(T`
el("p").append(...T`
This part of the documentation is primarily intended for technical enthusiasts and authors of
3rd-party libraries. It describes an advanced feature, not a core part of the library. Most users will
not need to implement this functionality directly in their applications. This capability will hopefully
be covered by third-party libraries or frameworks that provide simpler SSR integration using
dd<el>.
deka-dom-el.
`)
),
el("p").append(T`
dd<el> isnt limited to browser environments. Thanks to its flexible architecture,
el("p").append(...T`
deka-dom-el isn't limited to browser environments. Thanks to its flexible architecture,
it can be used for server-side rendering (SSR) to generate static HTML files.
This is achieved through integration with for example ${el("a", { href: "https://github.com/tmpvar/jsdom",
textContent: "jsdom" })}, a JavaScript implementation of web standards for Node.js.
textContent: "jsdom" })}, a JavaScript implementation of web standards for Node.js.
`),
el("p").append(T`
Additionally, you might consider using these alternative solutions:
`),
el("ul").append(
el("li").append(T`
${el("a", { href: "https://github.com/capricorn86/happy-dom", textContent: "happy-dom" })}
A JavaScript implementation of a web browser without its graphical user interface thats faster than jsdom
`),
el("li").append(T`
${el("a", { href: "https://github.com/WebReflection/linkedom", textContent: "linkedom" })}
A lightweight DOM implementation specifically designed for SSR with significantly better performance
than jsdom
`),
),
el(code, { src: fileURL("./components/examples/ssr/intro.js") }),
el(code, { src: fileURL("./components/examples/ssr/intro.js"), page_id }),
el(h3, t`Why Server-Side Rendering?`),
el("p").append(T`
el("p").append(...T`
SSR offers several benefits:
`),
el("ul").append(
@ -59,90 +46,90 @@ export function page({ pkg, info }){
),
el(h3, t`How jsdom Integration Works`),
el("p").append(T`
The jsdom export in dd<el> provides the necessary tools to use the library in Node.js
by integrating with jsdom. Heres what it does:
el("p").append(...T`
The jsdom export in deka-dom-el provides the necessary tools to use the library in Node.js
by integrating with jsdom. Here's what it does:
`),
el("ol").append(
el("li", t`Creates a virtual DOM environment in Node.js using jsdom`),
el("li", t`Registers DOM globals like HTMLElement, document, etc. for dd<el> to use`),
el("li", t`Sets an SSR flag in the environment to enable SSR-specific behaviors`),
el("li", t`Provides a promise queue system for managing async operations during rendering`),
el("li", t`Creates a virtual DOM environment in Node.js using jsdom`),
el("li", t`Registers DOM globals like HTMLElement, document, etc. for deka-dom-el to use`),
el("li", t`Sets an SSR flag in the environment to enable SSR-specific behaviors`),
el("li", t`Provides a promise queue system for managing async operations during rendering`),
el("li", t`Handles DOM property/attribute mapping differences between browsers and jsdom`)
),
el(code, { src: fileURL("./components/examples/ssr/start.js") }),
el(code, { src: fileURL("./components/examples/ssr/start.js"), page_id }),
el(h3, t`Basic SSR Example`),
el("p").append(T`
Heres a simple example of how to use dd<el> for server-side rendering in a Node.js script:
el("p").append(...T`
Here's a simple example of how to use deka-dom-el for server-side rendering in a Node.js script:
`),
el(code, { src: fileURL("./components/examples/ssr/basic-example.js") }),
el(code, { src: fileURL("./components/examples/ssr/basic-example.js"), page_id }),
el(h3, t`Building a Static Site Generator`),
el("p").append(T`
You can build a complete static site generator with dd<el>. In fact, this documentation site
is built using dd<el> for server-side rendering! Heres how the documentation build process works:
el(h3, t`Building a Static Site Generator`),
el("p").append(...T`
You can build a complete static site generator with deka-dom-el. In fact, this documentation site
is built using deka-dom-el for server-side rendering! Here's how the documentation build process works:
`),
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js") }),
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
el(h3, t`Working with Async Content in SSR`),
el("p").append(T`
The jsdom export includes a queue system to handle asynchronous operations during rendering.
el("p").append(...T`
The jsdom export includes a queue system to handle asynchronous operations during rendering.
This is crucial for components that fetch data or perform other async tasks.
`),
el(code, { src: fileURL("./components/examples/ssr/async-data.js") }),
el(code, { src: fileURL("./components/examples/ssr/async-data.js"), page_id }),
el(h3, t`Working with Dynamic Imports for SSR`),
el("p").append(T`
When structuring server-side rendering code, a crucial pattern to follow is using dynamic imports
el("p").append(...T`
When structuring server-side rendering code, a crucial pattern to follow is using dynamic imports
for both the deka-dom-el/jsdom module and your page components.
`),
el("p").append(T`
el("p").append(...T`
Why is this important?
`),
el("ul").append(
el("li").append(T`
el("li").append(...T`
${el("strong", "Static imports are hoisted:")} JavaScript hoists import statements to the top of the file,
executing them before any other code
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Environment registration timing:")} The jsdom module auto-registers the DOM environment
when imported, which must happen ${el("em", "after")} youve created your JSDOM instance and
when imported, which must happen ${el("em", "after")} you've created your JSDOM instance and
${el("em", "before")} you import your components using ${el("code", "import { el } from \"deka-dom-el\";")}.
`),
el("li").append(T`
el("li").append(...T`
${el("strong", "Correct initialization order:")} You need to control the exact sequence of:
create JSDOM register environment import components
`)
),
el("p").append(T`
el("p").append(...T`
Follow this pattern when creating server-side rendered pages:
`),
el(code, { src: fileURL("./components/examples/ssr/pages.js") }),
el(code, { src: fileURL("./components/examples/ssr/pages.js"), page_id }),
el(h3, t`SSR Considerations and Limitations`),
el("p").append(T`
When using dd<el> for SSR, keep these considerations in mind:
el("p").append(...T`
When using deka-dom-el for SSR, keep these considerations in mind:
`),
el("ul").append(
el("li", t`Browser-specific APIs like window.localStorage are not available in jsdom by default`),
el("li", t`Event listeners added during SSR wont be functional in the final HTML unless hydrated on the client`),
el("li", t`Event listeners added during SSR won't be functional in the final HTML unless hydrated on the client`),
el("li", t`Some DOM features may behave differently in jsdom compared to real browsers`),
el("li", t`For large sites, you may need to optimize memory usage by creating a new jsdom instance for each page`)
el("li", t`For large sites, you may need to optimize memory usage by creating a new jsdom instance for each page`)
),
el("p").append(T`
el("p").append(...T`
For advanced SSR applications, consider implementing hydration on the client-side to restore
interactivity after the initial render.
`),
el(h3, t`Real Example: How This Documentation is Built`),
el("p").append(T`
This documentation site itself is built using dd<el>s SSR capabilities.
el("p").append(...T`
This documentation site itself is built using deka-dom-el's SSR capabilities.
The build process collects all page components, renders them with jsdom, and outputs static HTML files.
`),
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js") }),
el(code, { src: fileURL("./components/examples/ssr/static-site-generator.js"), page_id }),
el("p").append(T`
el("p").append(...T`
The resulting static files can be deployed to any static hosting service,
providing fast loading times and excellent SEO without the need for client-side JavaScript
to render the initial content.

Some files were not shown because too many files have changed in this diff Show More