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

12 Commits

Author SHA1 Message Date
f2c85ec983 🐛 📺 v0.9.5-alpha (#44)
*  📺 package (version and publint)

* 🔤 docs typos

* 🐛 this address / fixes #43

*  tiny el optimalization

* 🔤 improve docs wording

* 🔤 case-studies/products.js (AbortSignal)

* 🔤 🐛 case-studies/image-gallery.js
2025-03-21 14:43:25 +01:00
4c450ae763 🐛 🔤 v0.9.4-alpha (#42)
* 🐛 fixes #41

*  adjust package size limits

* 🔤

* 📺 requestIdleCallback doesn need to be global

* 🔤 corrects irland page headers

* 📺 version

*  Signal ← SignalReadonly

* 🐛 ensures only one disconncetd listener

…for cleanup

*  🔤 Better build and improve texting

* 🐛 logo alignemt (due to gh)

* 🔤 md enhancements

* 🔤  products
2025-03-19 17:10:43 +01:00
04f93345f8 🐛 📺 npm pkg fix 2025-03-17 14:39:15 +01:00
5076771410 🐛 🔤 v0.9.3-alpha (#40)
* 🔤

*  Replaces defined with name/host

* 🐛 __dde_reactive

*  v0.9.3

* 🔤 examples+best paractises

* 🐛 📺 fixes npm run docs

*  finalizes v0.9.3-alpha

* 🔤 📺 coc tabs

* 🔤
2025-03-17 14:21:03 +01:00
f0dfdfde54 v0.9.2 — 🐛 types, on.defer and other small (#36)
* 🔤  T now uses DocumentFragment

* 🔤

* 🔤 

* 🐛 lint

*  cleanup

*  🔤 lib download

*  🔤 ui

*  reorganize files

*  on.host

* 🐛 on.* types

*  🔤 cdn

* 🔤 converter

* 🐛 signal.set(value, force)

*  🔤

* 🔤  converter - convert also comments

*  bs/build

* 🔤 ui p14

* 🔤

* 🔤 Examples

* 🔤

* 🐛 now only el(..., string|number)

* 🐛 fixes #38

* 🔤

*  on.host → on.defer

* 🔤

* 📺
2025-03-16 11:30:42 +01:00
25d475ec04 🔤 🐛 v0.9.1-alpha (#30)
* :tap: removed on.attributeChanged and static observedAttributes

*  import optimalization

*  scope.signal

* 🔤 🐛

*  🐛 registerReactivity and types

* 🔤

* 

* 🔤

* 🐛 Node in enviroment

*  todos

* 

*  🔤

*  lint

*  memo

* 🔤 🐛 memo

*  🔤 todomvc

* 🐛 types

* 🔤 p08 signal factory

* 🔤  types

*  🔤 lint

* 🔤

* 🔤

* 🔤

* 🔤

* 📺
2025-03-12 18:37:42 +01:00
e1f321004d Merge remote-tracking branch 'origin/main' 2025-03-07 21:50:05 +01:00
47c5fda8d6 🐛 DDE + dd<el> in md 2025-03-07 21:47:49 +01:00
7f3b818fa5 🐛 DDE + dd<el> in md 2025-03-07 21:45:44 +01:00
d742d960ac 🔤 dd<el>, iief 2025-03-07 21:32:15 +01:00
d56d5e45d5 🔤 just typos + status updates 2025-03-07 14:54:48 +01:00
4366027658 dde and docs improvements (#27)
*  🎉

*  wip

* 🔤

*  wip

*  wip

*  Refatc signals to .get/.set syntax #26

* 🐛 Better types for on*

* 🔤

* 🔤

* 🐛 coumputed signal

* 🔤  Docs UI/UX

*  🔤 UI enhancements

*  (bs) (un)min

* 🔤 adds debugging

* 🔤 ssr

* 🔤

*  bs/lint

* 🔤

* 🔤 UI

* 🔤 updates texts

* 🔤UI

*  dispatch

* 🔤 events

* 🔤 elements

* 🔤 intro

* 🐛 fixes completitions for el with components

* 🐛 wrong file(s) in git

* 🔤 logo

* 🐛 🔤 types 3ps

* 🔤 ui/ux

* 🔤

* 🔤

* 🔤 scopes

* 🔤

* 🔤 ui/ux

* 🔤

*  issignal

* 🔤 improvemens

*  irelands

* 🔤 UI/UX/wording

* 🐛 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)

* 🔤 logos

*  better? dts builds

* Update README.md
2025-03-07 14:40:45 +01:00
144 changed files with 17756 additions and 6898 deletions

40
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,40 @@
---
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 -->

22
.github/ISSUE_TEMPLATE/documentation.md vendored Normal file
View File

@ -0,0 +1,22 @@
---
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

@ -0,0 +1,29 @@
---
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 -->

39
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,39 @@
<!--
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 -->

18
.github/workflows/npm-publish.yml vendored Normal file
View File

@ -0,0 +1,18 @@
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 Normal file
View File

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

134
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,134 @@
# 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

177
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,177 @@
# 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.

161
README.md
View File

@ -1,56 +1,53 @@
**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
**Alpha**
| [Docs&Examples](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"))
***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(
el(EmojiCounter, { initial: "🚀" })
el(EmojiCounter, { initial: "🚀" }),
);
function EmojiCounter({ initial }) {
// ✨ State - Define reactive data
const count = S(0);
const emoji = S(initial);
// ✨ - Define reactive data
const count = S(0);
const emoji = S(initial);
const textContent = S(() => `Hello World ${emoji.get().repeat(count.get())}`);
/** @param {HTMLOptionElement} el */
const isSelected= el=> (el.selected= el.value===initial);
// 🔄 - UI updates automatically when signals change
return el().append(
el("p", { textContent, className: "output" }),
// 🔄 View - UI updates automatically when signals change
return el().append(
el("p", {
className: "output",
textContent: S(() =>
`Hello World ${emoji.get().repeat(clicks.get())}`),
}),
// 🎮 - Update state on events
el("button", { textContent: "Add Emoji" },
on("click", () => count.set(count.get() + 1)),
),
// 🎮 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),
)
);
el("select", null,
on.defer(el=> el.value= initial),
on("change", e => emoji.set(e.target.value)),
).append(
el(Option, "🎉"),
el(Option, "🚀"),
el(Option, "💖"),
),
);
}
function Option({ textContent }){
return Ol("option", { value: textContent, textContent });
return el("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
@ -59,43 +56,63 @@ 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
-**Declarative & functional approach** for clean, maintainable code
-**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, 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.
A key advantage: any internal function (`assign`, `classListDeclarative`, `on`, `dispatchEvent`, `S`, etc.) can be used independently while also working seamlessly together. This modular approach makes it easier to integrate the library into existing projects.
- **Minimalized footprint** — ~10-15kB minified bundle (original goal 10kB), **zero**/minimal dependencies and
small in-memory size (auto-releasing resources as much as possible)
-**Declarative & functional approach support** for clean, maintainable code
-**Signals and events** for reactive UI
- **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
#### npm
```bash
# TBD
# npm install deka-dom-el
npm install deka-dom-el --save
```
#### Direct Script
…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
<script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/dde-with-signals.min.js"></script>
<!-- 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">
const { el, S } = dde;
import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
// Your code here
</script>
```
### Documentation
## Why Another Library?
- [**Interactive Guide**](https://jaandrle.github.io/deka-dom-el): WIP
- [Examples](./examples/): TBD/WIP
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
auxiliary functions. These range from minor improvements to advanced features for building complete declarative
reactive UI templates.
A key advantage: any internal function (`assign`, `classListDeclarative`, `on`, `dispatchEvent`, `S`, etc.) can be used
independently while also working seamlessly together. This modular approach makes it easier to integrate the library
into existing projects.
## Understanding Signals
@ -106,10 +123,24 @@ 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
- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) - Functional DOM components without JSX/virtual DOM
- [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)

View File

@ -2,15 +2,18 @@
This project uses [jaandrle/bs: The simplest possible build system using executable/bash scripts](
https://github.com/jaandrle/bs).
#### bs/build.js [--minify|--help]
#### bs/build.js [main|signals] [--no-types|--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,30 +4,33 @@ const files= [ "index", "index-with-signals" ];
$.api("")
.command("main", "Build main files", { default: true })
.action(async function main(){
const regular = await build({
.option("--no-types", "Also generate d.ts files", false)
.action(function main({ types }){
const regular = build({
files,
filesOut,
minify: "no",
types,
});
const min = await build({
const min = build({
files,
filesOut(file, mark= "esm"){
const out= filesOut(file, mark);
const idx= out.lastIndexOf(".");
const idx= out.indexOf(".");
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(async function signals(){
const regular = await build({
.action(function signals(){
const regular = build({
files: [ "signals" ],
filesOut(file){ return "dist/."+file; },
minify: "no",
dde: false,
iife: false,
});
return $.exit(regular);
})

View File

@ -1,65 +1,101 @@
#!/usr/bin/env -S npx nodejsscript
import { bundle as bundleDTS } from "dts-bundler";
import { buildSync as esbuildSync } from "esbuild";
const css= echo.css`
.info{ color: gray; }
`;
export async function build({ files, filesOut, minify= "partiala", dde= true }){
export function build({ files, filesOut, minify= "partial", iife= true, types= true }){
for(const file_root of files){
const file= file_root+".js";
echo(`Processing ${file} (minified: ${minify})`);
const out= filesOut(file);
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));
esbuild({ file, out, minify });
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(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);
}
if(dde) await toDDE(out, file_root);
if(iife) toIIFE(file, file_root, types);
}
return 0;
async function toDDE(file, file_root){
const name= "dde";
const out= filesOut(file_root+".js", name);
echoVariant(`${out} (${file} → globalThis.${name})`)
function toIIFE(file, file_root, types){
const fileMark= "iife";
const name= "DDE";
const out= filesOut(file_root+".js", fileMark);
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);
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);
}
}
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 undefined;
if("full"===level) return "--minify";
return "--minify-syntax --minify-identifiers";
if("no"===level) return { minify: false };
if("full"===level) return { minify: true };
return { minifySyntax: true, minifyIdentifiers: true };
}
function echoVariant(name){
return echo("%c✓ "+name, css.info+css);
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);
}

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, dispatchEvent, t } from "../docs/ssr.js";
import { path_target, pages as pages_registered, styles, currentPageId, 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,6 +28,7 @@ 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 }),
);
@ -42,7 +43,7 @@ s.echo(styles.content).to(path_target.css+styles.name);
// Copy assets
echo("Copying assets…");
if(s.test("-d", "docs/assets")) {
s.cp("-r", "docs/assets/*", path_target.assets);
s.cp("-r", "docs/assets/*", path_target.assets);
}
dispatchEvent("onssrend");

View File

@ -1,5 +1,5 @@
import { JSDOM } from "jsdom";
const html_default= "<!doctype html><html><head><meta charset=\"utf-8\"></head><body></body></html>";
const html_default= "<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"></head><body></body></html>";
let keys= [];
let dom= null;
import { relative } from 'node:path';

View File

@ -1,5 +1,12 @@
#!/usr/bin/env bash
set -eou pipefail
npx editorconfig-checker -format gcc
# 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 size-limit
npx jshint index.js src
npx publint

1012
dist/dde-with-signals.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

667
dist/dde.js vendored
View File

@ -1,667 +0,0 @@
//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

File diff suppressed because one or more lines are too long

View File

@ -1,606 +0,0 @@
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 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>
/* 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

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

@ -0,0 +1,860 @@
// 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

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

@ -1,533 +0,0 @@
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 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>
/* 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

1000
dist/esm.d.ts vendored

File diff suppressed because it is too large Load Diff

803
dist/esm.js vendored
View File

@ -1,4 +1,5 @@
// src/helpers.js
var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a);
function isUndef(value) {
return typeof value === "undefined";
}
@ -8,6 +9,9 @@ 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);
}
@ -21,53 +25,18 @@ function onAbort(signal, listener) {
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());
function requestIdle() {
return new Promise(function(resolve) {
(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
});
}
// 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
// src/dom-lib/common.js
var enviroment = {
setDeleteAttr,
ssr: "",
D: globalThis.document,
N: globalThis.Node,
F: globalThis.DocumentFragment,
H: globalThis.HTMLElement,
S: globalThis.SVGElement,
@ -88,276 +57,7 @@ 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
// src/dom-lib/events-observer.js
var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
get() {
return () => {
@ -468,18 +168,13 @@ 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, Node)) return out;
if (!isInstance(element, enviroment.N)) return out;
for (const el of store.keys()) {
if (el === element || !isInstance(el, Node)) continue;
if (el === element || !isInstance(el, enviroment.N)) continue;
if (element.contains(el))
out.push(el);
}
@ -521,8 +216,372 @@ function connectionsChangesObserverConstructor() {
}
}
// src/customElement.js
function customElementRender(target, render, props = observedAttributes2) {
// 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 = /** @type {Element} */
tag(attributes || void 0);
if (el.nodeName === "#comment") break;
const is_fragment = isInstance(el, enviroment.F);
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,
@ -563,81 +622,40 @@ 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);
};
// 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 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;
};
memo.isScope = function(obj) {
return obj[memoMark];
};
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)] })
);
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 c = onAbort(options.signal, () => observer.disconnect());
if (c) observer.observe(element, { attributes: true });
return element;
};
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;
};
export {
assign,
@ -651,9 +669,8 @@ export {
dispatchEvent,
createElement as el,
createElementNS as elNS,
elementAttribute,
lifecyclesToEvents,
observedAttributes2 as observedAttributes,
memo,
on,
queue,
registerReactivity,

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

@ -0,0 +1,859 @@
// 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

862
dist/iife-with-signals.d.ts vendored Normal file
View File

@ -0,0 +1,862 @@
// 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 {};

1029
dist/iife-with-signals.js vendored Normal file

File diff suppressed because it is too large Load Diff

862
dist/iife-with-signals.min.d.ts vendored Normal file
View File

@ -0,0 +1,862 @@
// 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 {};

3
dist/iife-with-signals.min.js vendored Normal file

File diff suppressed because one or more lines are too long

861
dist/iife.d.ts vendored Normal file
View File

@ -0,0 +1,861 @@
// 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 {};

703
dist/iife.js vendored Normal file
View File

@ -0,0 +1,703 @@
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 = /** @type {Element} */
tag(attributes || void 0);
if (el.nodeName === "#comment") break;
const is_fragment = isInstance(el, enviroment.F);
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 Normal file
View File

@ -0,0 +1,861 @@
// 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 Normal file

File diff suppressed because one or more lines are too long

BIN
docs/assets/devtools.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@ -1,28 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#333333" />
<stop offset="100%" stop-color="#222222" />
</linearGradient>
<linearGradient id="textGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#e32c2c" />
<stop offset="100%" stop-color="#ff5252" />
</linearGradient>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="0.5" stdDeviation="0.5" flood-color="#000" flood-opacity="0.3"/>
</filter>
</defs>
<!-- Square background with rounded corners -->
<rect x="2" y="2" width="28" height="28" rx="4" ry="4" fill="url(#bgGradient)" />
<!-- Subtle code brackets as background element -->
<g opacity="0.15" fill="#fff">
<path d="M10,7.5 L6.25,16 L10,24.5" stroke="#fff" stroke-width="1" fill="none"/>
<path d="M22,7.5 L25.75,16 L22,24.5" stroke="#fff" stroke-width="1" fill="none"/>
</g>
<!-- lowercase dde letters -->
<text x="16" y="21" text-anchor="middle" font-family="'Fira Code', 'JetBrains Mono', 'Source Code Pro', 'SF Mono', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace" font-size="14" font-weight="bold" fill="url(#textGradient)" filter="url(#shadow)">dde</text>
</svg>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
version="1.1"
id="svg2"
sodipodi:docname="favicon.svg"
xml:space="preserve"
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"><defs
id="defs2" /><sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="9.3249707"
inkscape:cx="38.230683"
inkscape:cy="28.150223"
inkscape:window-width="1278"
inkscape:window-height="1023"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><rect
x="0"
y="0"
width="64"
height="64"
rx="10"
fill="#2b2b2b"
id="rect1" /><g
transform="matrix(0.98009485,0,0,1.1964871,0.58334513,-7.2134367)"
id="g2"
style="stroke:#666666"><g
id="g3"
transform="matrix(1.0027126,0,0,0.88864615,3.8540039,4.3371297)"
style="stroke:#666666"><path
d="M 8.880742,11.550781 2.9817709,32 8.880742,52.449219"
stroke="#ff5252"
stroke-width="4.25375"
fill="none"
id="path1"
style="stroke:#666666" /><path
d="M 47.330923,11.568743 53.261141,32 47.330923,52.431257"
stroke="#ff5252"
stroke-width="4.26309"
fill="none"
id="path2"
style="stroke:#666666" /></g></g><path
d="m 11.726038,32.656946 q 0,-2.685867 0.823088,-4.721927 0.823088,-2.036061 2.166022,-3.404987 1.342933,-1.360261 3.07575,-2.053387 1.732818,-0.693128 3.552275,-0.693128 4.505325,0 6.844629,2.659875 2.339303,2.668539 2.339303,7.780349 0,0.519846 -0.01733,1.083011 -0.02599,0.563166 -0.06931,0.90973 H 17.227733 q 0,1.992739 1.646176,3.145062 1.646176,1.14366 4.245402,1.14366 1.602856,0 3.058423,-0.346564 1.446902,-0.346563 2.443272,-0.693127 l 0.736447,4.548646 q -1.386253,0.476525 -2.945789,0.797097 -1.559536,0.329235 -3.508954,0.329235 -2.599226,0 -4.652615,-0.675799 -2.062053,-0.667135 -3.508955,-1.984075 -1.455566,-1.325605 -2.235335,-3.275025 -0.779767,-1.94942 -0.779767,-4.548646 m 13.645936,-2.1227 q 0,-0.823088 -0.216602,-1.585528 -0.216602,-0.753776 -0.693127,-1.360263 -0.476525,-0.606485 -1.212973,-0.979042 -0.736447,-0.363891 -1.819457,-0.363891 -1.039691,0 -1.793467,0.346563 -0.762439,0.346564 -1.264955,0.95305 -0.493854,0.606485 -0.771105,1.386253 -0.285915,0.77977 -0.372555,1.602856 z m 26.901988,11.263311 q -0.129961,0.08664 -0.589158,0.303244 -0.450533,0.216602 -1.18698,0.459196 -0.736448,0.23393 -1.793466,0.407211 -1.065682,0.173282 -2.408616,0.173282 -3.682236,0 -5.371734,-2.192014 -1.689496,-2.18335 -1.689496,-6.385432 V 17.278193 H 33.602856 V 12.85951 h 10.960069 v 22.093419 q 0,2.079381 0.823089,2.815829 0.823088,0.736447 2.07938,0.736447 1.602856,0 2.685867,-0.433204 1.08301,-0.433205 1.429574,-0.563166 z"
id="path1-1"
style="fill:#ff5252;stroke-width:0.866409" /></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,33 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<!-- Gradients and effects -->
<defs>
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#333333" />
<stop offset="100%" stop-color="#222222" />
</linearGradient>
<linearGradient id="textGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#e32c2c" />
<stop offset="100%" stop-color="#ff5252" />
</linearGradient>
<filter id="shadow" x="-10%" y="-10%" width="120%" height="120%">
<feDropShadow dx="0" dy="2" stdDeviation="2" flood-color="#000" flood-opacity="0.3"/>
</filter>
<filter id="glow" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="4" result="blur"/>
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
</defs>
<!-- Square background with rounded corners -->
<rect x="18" y="18" width="220" height="220" rx="20" ry="20" fill="url(#bgGradient)" />
<!-- Subtle code brackets as background element -->
<g opacity="0.15" fill="#fff" filter="url(#glow)">
<path d="M80,60 L50,128 L80,196" stroke="#fff" stroke-width="8" fill="none"/>
<path d="M176,60 L206,128 L176,196" stroke="#fff" stroke-width="8" fill="none"/>
</g>
<!-- lowercase dde letters with shadow effect -->
<text x="128" y="154" text-anchor="middle" font-family="'Fira Code', 'JetBrains Mono', 'Source Code Pro', 'SF Mono', Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace" font-size="100" font-weight="bold" fill="url(#textGradient)" filter="url(#shadow)">dde</text>
</svg>
<svg
width="256"
height="256"
viewBox="0 0 256 256"
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
id="defs4"><linearGradient
id="bgGradient"
x1="18"
y1="18"
x2="238"
y2="238"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.1363636,0,0,1.1363636,-17.454544,-17.454544)"><stop
offset="0"
stop-color="#3b3b3b"
id="stop1" /><stop
offset="1"
stop-color="#2b2b2b"
id="stop2" /></linearGradient><filter
id="glow"
x="-0.089563958"
y="-0.082460006"
width="1.1791279"
height="1.16492"><feGaussianBlur
stdDeviation="4"
result="blur"
id="feGaussianBlur4"
in="SourceGraphic" /><feComposite
in="SourceGraphic"
in2="blur"
operator="over"
id="feComposite4"
result="composite-0" /></filter></defs><rect
x="3"
y="3"
width="250"
height="250"
fill="url(#bgGradient)"
id="rect4"
style="fill:url(#bgGradient);stroke-width:1.13636"
ry="50" /><g
id="g2"
transform="translate(0.4430186,-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
d="m 80,60 -30,68 30,68"
stroke="#ffffff"
stroke-width="8"
fill="none"
id="path4" /><path
d="m 176,60 30,68 -30,68"
stroke="#ffffff"
stroke-width="8"
fill="none"
id="path5" /></g><path
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"
id="path1"
style="fill:#ff5252;fill-opacity:0.664797;stroke-width:2.1097" /></g></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,4 +1,4 @@
import { registerClientFile, styles } from "../ssr.js";
import { page_id, registerClientFile, styles } from "../ssr.js";
const host= "."+code.name;
styles.css`
/* Code block styling */
@ -177,6 +177,9 @@ ${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
@ -184,56 +187,35 @@ 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 {"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
* @param {Language} [attrs.language="-s"] Language of the code
* */
export function code({ id, src, content, language= "js", className= host.slice(1), page_id }){
if(src) content= s.cat(src);
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);
let dataJS;
if(page_id){
registerClientPart(page_id);
if(language!== "-"){
registerClientPart();
dataJS= "todo";
}
return el("div", { id, className, dataJS }).append(
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= {};
/** @param {string} page_id */
function registerClientPart(page_id){
function registerClientPart(){
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(
@ -245,3 +227,9 @@ function registerClientPart(page_id){
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

@ -0,0 +1,175 @@
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

@ -0,0 +1,384 @@
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,9 +1,8 @@
import { styles } from "../ssr.js";
import { page_id, 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);
@ -84,7 +83,6 @@ 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;
@ -96,6 +94,18 @@ 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();
@ -108,15 +118,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 {string} attrs.page_id ID of the page
* @param {"normal"|"big"} [attrs.variant="normal"] Size of the example
* */
export function example({ src, language= "js", page_id }){
export function example({ src, language= "js", variant= "normal" }){
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(code, { id, content, language, className: example.name }, el=> el.dataset.variant= variant),
elCode({ id, content, extension: "."+language })
);
}

View File

@ -0,0 +1,375 @@
/**
* 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

@ -0,0 +1,412 @@
/**
* 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':
onPrevImage(e);
break;
case 'ArrowRight':
onNextImage(e);
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()
}, onImageClick(image.id)).append(
el("img", {
src: image.src,
alt: image.alt,
loading: "lazy"
}),
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",
ariaLabel: "Close lightbox",
}, on("click", closeLightbox)).append("×"),
el("button", {
className: "lightbox-prev-btn",
ariaLabel: "Previous image",
}, on("click", onPrevImage)).append(""),
el("button", {
className: "lightbox-next-btn",
ariaLabel: "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

@ -0,0 +1,339 @@
/**
* 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

@ -0,0 +1,509 @@
import { el, on, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
export function ProductCatalog() {
const { signal }= scope;
const itemsPerPage = 5;
const products = asyncSignal(S,
fetchProducts,
{ initial: [], keepLast: true, signal });
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, signal?: AbortSignal }} options - Configuration options
* @returns {Object} Status signals and control methods
*/
export function asyncSignal(S, invoker, { initial, keepLast, signal } = {}) {
/** @type {(s: AbortSignal) => AbortSignal} */
const anySignal = !signal || !AbortSignal.any // TODO: make better
? s=> s
: s=> AbortSignal.any([s, signal]);
// 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: anySignal(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

@ -0,0 +1,715 @@
/**
* 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,7 +8,8 @@ export class HTMLCustomElement extends HTMLElement{
connectedCallback(){
customElementRender(
this.attachShadow({ mode: "open" }),
ddeComponent
ddeComponent,
this
);
}
set attr(value){ this.setAttribute("attr", value); }

View File

@ -2,7 +2,6 @@
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 `observedAttributes`)
// listen to attribute changes (see `S.observedAttributes`)
}
disconnectedCallback(){
// nice place to clean up

View File

@ -1,7 +1,6 @@
import {
customElementRender,
customElementWithDDE,
observedAttributes,
el, on, scope,
} from "deka-dom-el";
import { S } from "deka-dom-el/signals";
@ -9,7 +8,6 @@ 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,5 +1,6 @@
import { S } from "deka-dom-el/signals";
// Debouncing signal updates
// ===== Approach 1: Traditional debouncing with utility function =====
function debounce(func, wait) {
let timeout;
return (...args)=> {
@ -8,8 +9,59 @@ 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));
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));

View File

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

View File

@ -2,9 +2,9 @@ import { el } from "deka-dom-el";
// Create element with properties
const button = el("button", {
textContent: "Click me",
className: "primary",
disabled: true
textContent: "Click me",
className: "primary",
disabled: true,
});
// Shorter and more expressive

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import { el, on } from "deka-dom-el";
// Third approach - append with on addon
el("button", {
textContent: "click me"
textContent: "click me"
}).append(
on("click", (e) => console.log("Clicked!", e))
on("click", (e) => console.log("Clicked!", e))
);

View File

@ -2,6 +2,6 @@ import { el } from "deka-dom-el";
// Using events with HTML attribute style
el("button", {
textContent: "click me",
"=onclick": "console.log(event)"
textContent: "click me",
"=onclick": "console.log(event)"
});

View File

@ -2,7 +2,7 @@ import { el, on } from "deka-dom-el";
// Using events as addons - chainable approach
el("button", {
textContent: "click me",
textContent: "click me",
},
on("click", (e) => console.log("Clicked!", e))
on("click", (e) => console.log("Clicked!", e))
);

View File

@ -0,0 +1,19 @@
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,15 +1,28 @@
import { el, on } from "deka-dom-el";
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));
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."),
);
}
document.body.append(
paragraph,
el("button", "Update attribute", on("click", ()=> paragraph.setAttribute("test", Math.random().toString()))),
" ",
el("button", "Remove", on("click", ()=> paragraph.remove()))
el(allLifecycleEvents),
el("button", "Remove Element", on("click", function(){
this.previousSibling.remove();
}))
);
/** @param {Partial<CustomEvent>} event */

View File

@ -2,6 +2,6 @@ import { el } from "deka-dom-el";
// Using events with property assignment
el("button", {
textContent: "click me",
onclick: console.log
textContent: "click me",
onclick: console.log
});

View File

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

View File

@ -4,11 +4,11 @@ const count = S(0);
// 2. React to state changes
S.on(count, value => {
updateUI(value);
if (value > 10) disableButton();
updateUI(value);
if (value > 10) disableButton();
});
// 3. Update state on events
button.addEventListener('click', () => {
count.set(count.get() + 1);
count.set(count.get() + 1);
});

View File

@ -1,30 +1,30 @@
import { el, on } from "deka-dom-el";
import { el } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
// A HelloWorld component using the 3PS pattern
function HelloWorld({ emoji = "🚀" }) {
// PART 1: Create reactive state
const clicks = S(0);
// PART 1: Create reactive state
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())}`)
}),
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 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: "🎉" })
el(HelloWorld, { emoji: "🎉" })
);

View File

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

@ -0,0 +1,2 @@
// 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

@ -0,0 +1,115 @@
// 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

@ -0,0 +1,381 @@
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("Text changed! "+(new Date()).toString())
textContent.set("Text changed! "+(new Date()).toString())
});
return el("p", textContent, onclickChange);
}

View File

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

View File

@ -2,44 +2,44 @@ import { el, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function CounterWithIsolatedTimer() {
const { host } = scope;
const { host } = scope;
// Main component state
const count = S(0);
// Main component state
const count = S(0);
// Create a timer in an isolated scope
scope.isolate(() => {
// These subscriptions won't be tied to the component lifecycle
// They would continue to run even if the component was removed
const timer = S(0);
// Create a timer in an isolated scope
scope.isolate(() => {
// These subscriptions won't be tied to the component lifecycle
// They would continue to run even if the component was removed
const timer = S(0);
// Not recommended for real applications!
// Just demonstrating scope isolation
setInterval(() => {
timer.set(timer.get() + 1);
console.log(`Timer: ${timer.get()}`);
}, 1000);
});
// Not recommended for real applications!
// Just demonstrating scope isolation
setInterval(() => {
timer.set(timer.get() + 1);
console.log(`Timer: ${timer.get()}`);
}, 1000);
});
// Normal component functionality within main scope
function increment() {
count.set(count.get() + 1);
}
// Normal component functionality within main scope
function increment() {
count.set(count.get() + 1);
}
return el("div").append(
el("p").append(
"Count: ",
el("#text", S(() => count.get()))
),
el("button", {
textContent: "Increment",
onclick: increment
}),
el("p", "An isolated timer runs in console")
);
return el("div").append(
el("p").append(
"Count: ",
el("#text", S(() => count.get()))
),
el("button", {
textContent: "Increment",
onclick: increment
}),
el("p", "An isolated timer runs in console")
);
}
// Usage
document.body.append(
el(CounterWithIsolatedTimer)
el(CounterWithIsolatedTimer)
);

View File

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

View File

@ -1,6 +1,5 @@
// 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() {
@ -8,23 +7,7 @@ async function renderWithAsyncData() {
const { el } = await register(dom);
// Create a component that fetches data
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)
);
}
const { AsyncComponent } = await import("./components/AsyncComponent.js");
// Render the page
dom.window.document.body.append(
@ -41,3 +24,24 @@ 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,6 +11,7 @@ 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,6 +17,7 @@ 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.js";
import { register, unregister, queue } from "deka-dom-el/jsdom";
// 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", { recursive: true });
mkdirSync("./dist/docs", { recursive: true });
// Build each page
for (const page of pages) {
@ -23,6 +23,7 @@ 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
@ -35,7 +36,7 @@ async function buildSite() {
// Write the HTML to a file
const html = dom.serialize();
writeFileSync(`./dist/${page.id}.html`, html);
writeFileSync(`./dist/docs/${page.id}.html`, html);
console.log(`Built page: ${page.id}.html`);
}

View File

@ -0,0 +1,83 @@
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

@ -0,0 +1,92 @@
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,3 +1,33 @@
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";
@ -13,18 +43,24 @@ 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 = {} }) {
// relative src against the current directory
const path= "./"+relative(dir, src.pathname);
const id = "ireland-" + generateComponentId(src);
const element = el.mark({ type: "ireland", name: ireland.name });
queue(import(path).then(module => {
const component = module[exportName];
element.replaceWith(el(component, props, mark(id)));
}));
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)
);
if(!componentsRegistry.size)
addEventListener("oneachrender", registerClientPart);

View File

@ -11,10 +11,6 @@ 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)",

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",
" — simple element containing only text (accepts string, number or signal)",
),
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

@ -13,18 +13,23 @@ export function mnemonic(){
". To connect to custom element see following page, else it is simulated by MutationObserver."
),
el("li").append(
el("code", "dispatchEvent(<event>[, <options>])(element)"),
" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
el("code", "on.defer(<identity>=> <identity>)(<identity>)"),
" — calls callback later",
),
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("code", "dispatchEvent(<event>[, <options>])(element)"),
" — just ", el("code", "<element>.dispatchEvent(new Event(<event>[, <options>]))")
),
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

@ -0,0 +1,15 @@
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,6 +14,10 @@ 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,10 +14,6 @@ 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",
@ -29,6 +25,11 @@ 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

@ -74,13 +74,11 @@ export function h3({ textContent, id }){
if(!id) id= "h-"+textContent.toLowerCase().replaceAll(/\s/g, "-").replaceAll(/[^a-z-]/g, "");
return el("h3", { id }).append(
el("a", {
className: "heading-anchor",
href: "#"+id,
textContent: "#",
title: `Link to this section: ${textContent}`,
"aria-label": `Link to section ${textContent}`
className: "heading-anchor",
href: "#"+id,
title: `Link to this section: ${textContent}`,
}),
" ",
"# ",
textContent,
);
}

View File

@ -0,0 +1,50 @@
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

@ -0,0 +1,14 @@
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,7 +16,6 @@ styles.css`
SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
--body-max-width: 40rem;
--sidebar-width: 20rem;
--header-height: 4rem;
--border-radius: 0.375rem;
@ -73,6 +72,7 @@ styles.css`
html {
scroll-behavior: smooth;
tab-size: var(--tab-size, 2rem);
}
/* Accessibility improvements */
@ -86,7 +86,7 @@ html {
}
:focus-visible {
outline: 3px solid hsl(231, 48%, 70%);
outline: 3px solid var(--primary-light);
outline-offset: 2px;
}
@ -193,6 +193,34 @@ 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 {
@ -215,8 +243,8 @@ body {
}
@media (min-width: 768px) {
body {
grid-template-rows: var(--header-height) 1fr;
grid-template-columns: var(--sidebar-width) 1fr;
grid-template-rows: auto 1fr;
grid-template-columns: auto 1fr;
grid-template-areas:
"header header"
"sidebar content";
@ -234,7 +262,7 @@ body > main {
}
body > main > *, body > main slot > * {
width: 100%;
max-width: 100%;
max-width: calc(var(--body-max-width) * 5/3);
margin-inline: auto;
grid-column: main;
}
@ -250,7 +278,7 @@ h2 {
/* Section headings with better visual hierarchy */
body > main h3, body > main h4 {
scroll-margin-top: calc(var(--header-height) + 1rem);
scroll-margin-top: 1rem;
}
/* Make clickable heading links for better navigation */
@ -267,9 +295,8 @@ body > main h3, body > main h4 {
/* Boxes */
.illustration{
grid-column: full-main;
width: calc(100% - .75em);
}
.illustration:not(:has( .comparison)){
.illustration:not(:has( .comparison)):not(:has( .tabs)) {
grid-column: main;
pre {

View File

@ -1,9 +1,10 @@
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`A lightweight, 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`Reactive DOM library for creating dynamic UIs with a declarative syntax`,
};
import { el } from "deka-dom-el";
@ -11,10 +12,15 @@ 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= {
w_mvv:{
npm: {
title: t`NPM package page for dd<el>`,
href: "https://www.npmjs.com/package/deka-dom-el",
},
w_mvv: {
title: t`Wikipedia: Modelviewviewmodel`,
href: "https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel",
},
@ -25,28 +31,32 @@ 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 (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("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("div", { className: "callout" }).append(
el("h4", t`What Makes DDE Special`),
el("h4", t`Key Benefits of dd<el>`),
el("ul").append(
el("li", t`No build step required — use directly in the browser`),
el("li", t`Lightweight core (~1015kB minified) with zero dependencies`),
el("li", t`Minimalized footprint:`),
el("ul").append(
el("li", t`lightweight core (~1015kB minified)`),
el("li", t`…without unnecessary dependencies (0 at now 😇)`),
el("li", t`auto-releasing resources with focus on performance and development experience`),
),
el("li", t`Natural DOM API — work with real DOM nodes, not abstractions`),
el("li", t`Built-in reactivity with powerful signals system`),
el("li", t`Built-in (but optional) reactivity with simplified but powerful signals system`),
el("li", t`Clean code organization with the 3PS pattern`)
)
),
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js"), page_id }),
el(example, { src: fileURL("./components/examples/introducing/helloWorld.js") }),
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
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
organize your UI code into three distinct areas, making your applications more maintainable and easier
to reason about.
`),
@ -54,62 +64,116 @@ 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"), page_id }),
el(code, { src: fileURL("./components/examples/introducing/3ps-before.js") }),
),
el("div", { className: "tab" }).append(
el("h5", t`DDE's 3PS Pattern`),
el(code, { src: fileURL("./components/examples/introducing/3ps.js"), page_id }),
el("h5", t`dd<el>'s 3PS Pattern`),
el(code, { src: fileURL("./components/examples/introducing/3ps.js") }),
)
)
),
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 application's reactive data using signals
el("li").append(T`
${el("strong", "Create State")}: Define your applications reactive data using signals
`),
el("li").append(...T`
${el("strong", "Bind to Elements")}: Define how UI elements react to state changes
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("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 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.
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.
`),
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. You'll learn more about this in the
reusable pieces of UI with encapsulated state and behavior. Youll 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`How to Use This Documentation`),
el("p").append(...T`
This guide will take you through DDE's features step by step:
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("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("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("p").append(...T`
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("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("p").append(T`
Each section builds on the previous ones, so we recommend following them in order.
Let's get started with the basics of creating elements!
Lets get started with the basics of creating elements!
`),
);
}

View File

@ -105,13 +105,8 @@ ${host_nav} a .nav-number {
}
@media (max-width: 767px) {
${host_nav} {
padding: 0.75rem;
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 0.5rem;
border-bottom: 1px solid var(--border);
border-right: none;
flex-flow: row wrap;
justify-content: center;
}
@ -121,14 +116,7 @@ ${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;
}
@ -163,7 +151,7 @@ export function header({ info: { href, title, description }, pkg }){
),
el("span", {
className: "version-badge",
"aria-label": "Version",
ariaLabel: "Version",
textContent: pkg.version || ""
})
),
@ -177,13 +165,13 @@ export function header({ info: { href, title, description }, pkg }){
function nav({ href, pkg }){
return el("nav", {
role: "navigation",
"aria-label": "Main navigation",
ariaLabel: "Main navigation",
className: nav.name
}).append(
el("a", {
href: pkg.homepage,
className: "github-link",
"aria-label": "View on GitHub",
ariaLabel: "View on GitHub",
target: "_blank",
rel: "noopener noreferrer",
}).append(
@ -197,11 +185,11 @@ function nav({ href, pkg }){
return el("a", {
href: isIndex ? "./" : p.href,
title: p.description || `Go to ${p.title}`,
"aria-current": isCurrent ? "page" : null,
ariaCurrent: isCurrent ? "page" : null,
}).append(
el("span", {
className: "nav-number",
"aria-hidden": "true",
ariaHidden: "true",
textContent: `${i+1}. `
}),
p.title

View File

@ -3,6 +3,7 @@ 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 }){
@ -26,6 +27,9 @@ export function simplePage({ pkg, info }){
// Navigation between pages
el(prevNext, info)
)
),
// Scroll to top button
el(scrollTop),
));
}

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