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

16 Commits

Author SHA1 Message Date
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
dba4e93b88 debug options for (e.g.) document.body 2025-02-22 16:20:24 +01:00
72227a80df build 2025-02-18 22:25:09 +01:00
b347bdffc1 🐛 Fixes update of dataset/classList 2025-02-18 22:03:36 +01:00
e884f871b0 better helloWorld.js 2024-12-13 19:37:52 +01:00
c391c6549b 🔤 original helloWorld 2024-12-13 15:52:24 +01:00
091546aaab 🔤 introduction example (#24) 2024-12-13 15:49:54 +01:00
eef4e8dfa6 Refact docs and examples (linting) (#22) 2024-12-13 15:04:52 +01:00
173 changed files with 22444 additions and 5773 deletions

26
.editorconfig Normal file
View File

@ -0,0 +1,26 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = tab
trim_trailing_whitespace = true
max_line_length = 120
[*.json]
max_line_length = unset
[*.yml]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false
[LICENSE]
max_line_length = unset
[dist/**]
indent_style = unset
max_line_length = unset
trim_trailing_whitespace = unset

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 }}

23
.github/workflows/pr.yml vendored Normal file
View File

@ -0,0 +1,23 @@
# https://nektosact.com/usage/index.html
# https://github.com/reviewdog/action-eclint
name: On PR
on:
workflow_dispatch:
pull_request:
branches: [main]
jobs:
pr:
name: Validates formatting and linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: reviewdog/action-eclint@d51e853275e707b64c0526881ada324f454c1110 # v1.7.1
with:
reporter: github-pr-check
eclint_flags: '--fix'
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0
with:
node-version: 20.16
- run: npm ci
- run: bs/lint.sh

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
dist/docs/
dist/.*
# Logs
logs
*.log

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

174
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,174 @@
# 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.
## Commit Guidelines
We use
[![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 -->
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.

189
README.md
View File

@ -1,80 +1,149 @@
**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)
**Alpha**
| [source code on GitHub](https://github.com/jaandrle/deka-dom-el)
| [*mirrored* on Gitea](https://gitea.jaandrle.cz/jaandrle/deka-dom-el)
| [![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 -->
<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)
***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*
```js
```javascript
// 🌟 Reactive component with clear separation of concerns
document.body.append(
el("h1", "Hello World 👋"),
el("p", "See some syntax examples here:"),
el("ul").append(
el("li").append(
el("a", { textContent: "Link to the library repo", title: "Deka DOM El — GitHub", href: "https://github.com/jaandrle/deka-dom-el" })
),
el("li").append(
"Use extended Vanilla JavaScript DOM/IDL API: ",
el("span", { textContent: "» this is a span with class=cN and data-a=A, data-b=B «", className: "cN", dataset: { a: "A", b: "B" } })
),
el("li").append(
el(component, { textContent: "A component", className: "example" }, on("change", console.log))
)
)
el(EmojiCounter, { initial: "🚀" })
);
function component({ textContent, className }){
const value= S("onchange");
function EmojiCounter({ initial }) {
// ✨ State - Define reactive data
const count = S(0);
const emoji = S(initial);
/** @param {HTMLOptionElement} el */
const isSelected= el=> (el.selected= el.value===initial);
// 🔄 View - UI updates automatically when signals change
return el().append(
el("p", { textContent, className }),
el("p", { className: [ className, "second-line" ] }).append(
"…with reactivity: ", el("em", { style: { fontWeight: "bold" }, ariaset: { live: "polite" }, textContent: value }),
el("p", {
className: "output",
textContent: S(() =>
`Hello World ${emoji.get().repeat(count.get())}`),
}),
// 🎮 Controls - Update state on events
el("button", { textContent: "Add Emoji" },
on("click", () => count.set(count.get() + 1))
),
el("input", { type: "text", value: value() }, on("change", event=> value(event.target.value)))
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 el("option", { value: textContent, textContent });
}
```
# Deka DOM Elements
Creating reactive elements, components and Web components using [IDL](https://developer.mozilla.org/en-US/docs/Glossary/IDL)/JavaScript DOM API and [**signals/observables**](#signals).
## Inspiration and suggested alternatives
- my previous library (mostly used internaly): [jaandrle/dollar_dom_component: Functional DOM components without JSX and virtual DOM.](https://github.com/jaandrle/dollar_dom_component)
- [vanjs-org/van: 🍦 VanJS: World's smallest reactive UI framework. Incredibly Powerful, Insanely Small - Everyone can build a useful UI app in an hour.](https://github.com/vanjs-org/van)
- [hyperhype/hyperscript: Create HyperText with JavaScript.](https://github.com/hyperhype/hyperscript)
- [adamhaile/S: S.js - Simple, Clean, Fast Reactive Programming in Javascript](https://github.com/adamhaile/S) ([adamhaile/surplus: High performance JSX web views for S.js applications](https://github.com/adamhaile/surplus))
- [potch/signals: a small reactive signals library](https://github.com/potch/signals)
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
[**signals/observables**](#understanding-signals).
## Why an another one?
This library falls somewhere between van/hyperscript and [solid-js](https://github.com/solidjs/solid) in terms of size, complexity,
and usability.
## Features at a Glance
Another goal is to proceed in the best spirit of functional programming. This involves starting with
pure JavaScript (DOM API) and gradually adding auxiliary functions, ranging from “minor” improvements
to the capability of writing complete declarative reactive UI templates.
-**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
-**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
As a result, any “internal” function (`assign`, `classListDeclarative`, `on`, `dispatchEvent`, …, `S`, …)
can be used independently, although they are primarily designed for use in combination. This can also,
hopefully, help in integrating the library into existing projects.
## Getting Started
To balance these requirements, numerous compromises have been made. To summarize:
- [ ] Library size: 1015kB minified (the original goal was a maximum of 10kB)
- [x] Optional use of *signals* with the ability to register *your own signals/observables implementation*
- [x] *No build step required*
- [x] Preference for a *declarative/functional* approach
- [x] Focus on zero/minimal dependencies
- [ ] Support for/enhancement of custom elements (web components)
- [x] Support for SSR ([jsdom](https://github.com/jsdom/jsdom))
- [ ] WIP providing types
### Documentation
## First steps
- [**Guide**](https://jaandrle.github.io/deka-dom-el)
- Documentation
- Installation
- npm
- [dist/](dist/) (`https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/`…)
- [**Documentation and Guide**](https://jaandrle.github.io/deka-dom-el)
- [**Examples**](https://jaandrle.github.io/deka-dom-el/p15-examples.html)
## Signals
- [Signals — whats going on behind the scenes | by Ryan Hoffnan | ITNEXT](https://itnext.io/signals-whats-going-on-behind-the-scenes-ec858589ea63)
- [The Evolution of Signals in JavaScript - DEV Community](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob)
- there is also [tc39/proposal-signals: A proposal to add signals to JavaScript.](https://github.com/tc39/proposal-signals)
- [Observer pattern - Wikipedia](https://en.wikipedia.org/wiki/Observer_pattern)
### Installation
```bash
npm install deka-dom-el --save
```
…or via CDN / Direct Script:
For CDN links and various build formats (ESM/IIFE, with/without signals, minified/unminified), see the [interactive
format selector](https://jaandrle.github.io/deka-dom-el/) on the documentation site.
```html
<!-- Example with IIFE build (creates a global DDE object) -->
<script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/iife-with-signals.min.js"></script>
<script>
const { el, S } = DDE;
// Your code here
</script>
<!-- Or with ES modules -->
<script type="module">
import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
// Your code here
</script>
```
## Why Another Library?
This library bridges the gap between minimal solutions like van/hyperscript and more comprehensive frameworks like
[solid-js](https://github.com/solidjs/solid), offering a balanced trade-off between size, complexity, and usability.
Following functional programming principles, dd\<el\> starts with pure JavaScript (DOM API) and gradually adds
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
Signals are the reactive backbone of Deka DOM Elements:
- [Signals — what's going on behind the scenes](https://itnext.io/signals-whats-going-on-behind-the-scenes-ec858589ea63)
- [The Evolution of Signals in JavaScript](https://dev.to/this-is-learning/the-evolution-of-signals-in-javascript-8ob)
- [TC39 Signals Proposal](https://github.com/tc39/proposal-signals) (future standard)
- [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) (underlying concept)
## Contributing
We welcome contributions from the community! Please see our [Contributing Guide](CONTRIBUTING.md) for details on how to
get started, coding standards, commit guidelines, and the pull request process.
## Inspiration and Alternatives
- [vanjs-org/van](https://github.com/vanjs-org/van) — World's smallest reactive UI framework
- [adamhaile/S](https://github.com/adamhaile/S) — Simple, clean, fast reactive programming
- [hyperhype/hyperscript](https://github.com/hyperhype/hyperscript) — Create HyperText with JavaScript
- [potch/signals](https://github.com/potch/signals) — A small reactive signals library
- [AseasRoa/paintor](https://github.com/AseasRoa/paintor) - JavaScript library for building reactive client-side user
interfaces or HTML code.
- [pota](https://pota.quack.uy/) — small and pluggable Reactive Web Renderer. It's compiler-less, includes an html
function, and a optimized babel preset in case you fancy JSX.
- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) —
Functional DOM components without JSX/virtual DOM
- [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

View File

@ -1 +1,19 @@
[jaandrle/bs: The simplest possible build system using executables](https://github.com/jaandrle/bs/)
## bs: Build system based on executables
This project uses [jaandrle/bs: The simplest possible build system using executable/bash scripts](
https://github.com/jaandrle/bs).
#### bs/build.js [main|signals] [--no-types|--help]
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

@ -1,68 +1,40 @@
#!/usr/bin/env -S npx nodejsscript
import { bundle as bundleDTS } from "dts-bundler";
import { build } from "./dev/.build.js"
const files= [ "index", "index-with-signals" ];
const filesOut= (file, mark= "esm")=> "dist/"+file.replace("index", mark);
const css= echo.css`
.info{ color: gray; }
`;
$.api("", true)
.option("--minify", "Level of minification [ no, full, partial (default) ]")
.action(async function main({ minify= "partial" }){
for(const file_root of files){
const file= file_root+".js";
echo("Processing: "+ file);
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));
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);
await toDDE(out, file_root);
}
$.exit(0);
async function toDDE(file, file_root){
const name= "dde";
const out= filesOut(file_root+".js", name);
echoVariant(`${out} (${file} → globalThis.${name})`)
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);
}
$.api("")
.command("main", "Build main files", { default: true })
.option("--no-types", "Also generate d.ts files", false)
.action(function main({ types }){
const regular = build({
files,
filesOut,
minify: "no",
types,
});
const min = build({
files,
filesOut(file, mark= "esm"){
const out= filesOut(file, mark);
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(function signals(){
const regular = build({
files: [ "signals" ],
filesOut(file){ return "dist/."+file; },
minify: "no",
iife: false,
});
return $.exit(regular);
})
.parse();
/** @param {"no"|"full"|"partial"} level */
function minifyOption(level= "partial"){
if("no"===level) return undefined;
if("full"===level) return "--minify";
return "--minify-syntax --minify-identifiers";
}
function echoVariant(name){
return echo("%c✓ "+name, css.info+css);
}
function filesOut(file, mark= "esm"){ return "dist/"+file.replace("index", mark); }

101
bs/dev/.build.js Normal file
View File

@ -0,0 +1,101 @@
#!/usr/bin/env -S npx nodejsscript
import { buildSync as esbuildSync } from "esbuild";
const css= echo.css`
.info{ color: gray; }
`;
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);
esbuild({ file, out, minify });
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(iife) toIIFE(file, file_root, types);
}
return 0;
function toIIFE(file, file_root, types){
const fileMark= "iife";
const name= "DDE";
const out= filesOut(file_root+".js", fileMark);
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 { minify: false };
if("full"===level) return { minify: true };
return { minifySyntax: true, minifyIdentifiers: true };
}
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

@ -1,10 +1,10 @@
#!/usr/bin/env -S npx nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
/* 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 { createHTMl } from "./docs/jsdom.js";
import { register } from "../jsdom.js";
import { register, queue } from "../jsdom.js";
const pkg= s.cat("package.json").xargs(JSON.parse);
if(s.test("-d", path_target.root)){
@ -14,6 +14,10 @@ if(s.test("-d", path_target.root)){
echo("Creating directory…");
s.mkdir("-p", path_target.root);
}
// Create assets directory in target
echo("Creating assets directory…");
s.mkdir("-p", path_target.root+"assets");
echo("Collecting list of pages…");
const pages= s.ls($.xdg.main`../docs/*.html.js`).map(addPage);
for(const { id, info } of pages){
@ -27,12 +31,20 @@ for(const { id, info } of pages){
serverDOM.document.body.append(
el(page, { pkg, info }),
);
await queue();
echo.use("-R", `Writing ${id}.html…`);
dispatchEvent("oneachrender", document);
s.echo(serverDOM.serialize()).to(path_target.root+id+".html");
}
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);
}
dispatchEvent("onssrend");
echo("Done");

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

View File

@ -1,671 +0,0 @@
//deka-dom-el library is available via global namespace `dde`
(()=> {
// src/signals-common.js
var k = {
isSignal(t) {
return !1;
},
processReactiveAttribute(t, e, n, r) {
return n;
}
};
function B(t, e = !0) {
return e ? Object.assign(k, t) : (Object.setPrototypeOf(t, k), t);
}
function W(t) {
return k.isPrototypeOf(t) && t !== k ? t : k;
}
// src/helpers.js
var T = (...t) => Object.prototype.hasOwnProperty.call(...t);
function S(t) {
return typeof t > "u";
}
function X(t) {
let e = typeof t;
return e !== "object" ? e : t === null ? "null" : Object.prototype.toString.call(t);
}
function q(t, e) {
if (!t || !(t instanceof AbortSignal))
return !0;
if (!t.aborted)
return t.addEventListener("abort", e), function() {
t.removeEventListener("abort", e);
};
}
function F(t, e) {
let { observedAttributes: n = [] } = t.constructor;
return n.reduce(function(r, o) {
return r[pt(o)] = e(t, o), r;
}, {});
}
function pt(t) {
return t.replace(/-./g, (e) => e[1].toUpperCase());
}
// src/dom-common.js
var d = {
setDeleteAttr: lt,
ssr: "",
D: globalThis.document,
F: globalThis.DocumentFragment,
H: globalThis.HTMLElement,
S: globalThis.SVGElement,
M: globalThis.MutationObserver
};
function lt(t, e, n) {
if (Reflect.set(t, e, n), !!S(n)) {
if (Reflect.deleteProperty(t, e), t instanceof d.H && t.getAttribute(e) === "undefined")
return t.removeAttribute(e);
if (Reflect.get(t, e) === "undefined")
return Reflect.set(t, e, "");
}
}
var O = "__dde_lifecyclesToEvents", _ = "dde:connected", C = "dde:disconnected", M = "dde:attributeChanged";
// src/dom.js
var A = [{
get scope() {
return d.D.body;
},
host: (t) => t ? t(d.D.body) : d.D.body,
prevent: !0
}], m = {
get current() {
return A[A.length - 1];
},
get host() {
return this.current.host;
},
preventDefault() {
let { current: t } = this;
return t.prevent = !0, t;
},
get state() {
return [...A];
},
push(t = {}) {
return A.push(Object.assign({}, this.current, { prevent: !1 }, t));
},
pushRoot() {
return A.push(A[0]);
},
pop() {
if (A.length !== 1)
return A.pop();
}
};
function Y(...t) {
return this.appendOriginal(...t), this;
}
function ht(t) {
return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t;
}
var $;
function P(t, e, ...n) {
let r = W(this), o = 0, c, i;
switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) {
case typeof t == "function": {
o = 1, m.push({ scope: t, host: (...v) => v.length ? (o === 1 ? n.unshift(...v) : v.forEach((h) => h(i)), void 0) : i }), c = t(e || void 0);
let a = c instanceof d.F;
if (c.nodeName === "#comment") break;
let l = P.mark({
type: "component",
name: t.name,
host: a ? "this" : "parentElement"
});
c.prepend(l), a && (i = l);
break;
}
case t === "#text":
c = j.call(this, d.D.createTextNode(""), e);
break;
case (t === "<>" || !t):
c = j.call(this, d.D.createDocumentFragment(), e);
break;
case !!$:
c = j.call(this, d.D.createElementNS($, t), e);
break;
case !c:
c = j.call(this, d.D.createElement(t), e);
}
return ht(c), i || (i = c), n.forEach((a) => a(i)), o && m.pop(), o = 2, c;
}
function Wt(t, e, n) {
typeof e != "object" && (n = e, e = t);
let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((i, a) => Reflect.set(i, a.name || r, a) && i, {}), c = T(o, r);
if (t.append = new Proxy(t.append, {
apply(i, a, l) {
if (l[0] === e) return i.apply(t, l);
if (!l.length) return t;
let v = d.D.createDocumentFragment();
for (let h of l) {
if (!h || !h.slot) {
c && v.append(h);
continue;
}
let x = h.slot, w = o[x];
vt(h, "remove", "slot"), w && (bt(w, h, n), Reflect.deleteProperty(o, x));
}
return c && (o[r].replaceWith(v), Reflect.deleteProperty(o, r)), t.append = i, t;
}
}), t !== e) {
let i = Array.from(t.childNodes);
i.forEach((a) => a.remove()), t.append(...i);
}
return e;
}
function bt(t, e, n) {
n && n(t, e);
try {
t.replaceWith(j(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
}
P.mark = function(t, e = !1) {
t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
let n = e ? "" : "/", r = d.D.createComment(`<dde:mark ${t}${d.ssr}${n}>`);
return e && (r.end = d.D.createComment("</dde:mark>")), r;
};
function qt(t) {
let e = this;
return function(...r) {
$ = t;
let o = P.call(e, ...r);
return $ = void 0, o;
};
}
var U = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d;
function j(t, ...e) {
if (!e.length) return t;
U.set(t, rt(t, this));
for (let [n, r] of Object.entries(Object.assign({}, ...e)))
nt.call(this, t, n, r);
return U.delete(t), t;
}
function nt(t, e, n) {
let { setRemoveAttr: r, s: o } = rt(t, this), c = this;
n = o.processReactiveAttribute(
t,
e,
n,
(a, l) => nt.call(c, t, a, l)
);
let [i] = e;
if (i === "=") return r(e.slice(1), n);
if (i === ".") return et(t, e.slice(1), n);
if (/(aria|data)([A-Z])/.test(e))
return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n);
switch (e === "className" && (e = "class"), e) {
case "xlink:href":
return r(e, n, "http://www.w3.org/1999/xlink");
case "textContent":
return tt(t, e, n);
case "style":
if (typeof n != "object") break;
/* falls through */
case "dataset":
return I(o, n, et.bind(null, t[e]));
case "ariaset":
return I(o, n, (a, l) => r("aria-" + a, l));
case "classList":
return gt.call(c, t, n);
}
return Et(t, e) ? tt(t, e, n) : r(e, n);
}
function rt(t, e) {
if (U.has(t)) return U.get(t);
let r = (t instanceof d.S ? xt : mt).bind(null, t, "Attribute"), o = W(e);
return { setRemoveAttr: r, s: o };
}
function gt(t, e) {
let n = W(this);
return I(
n,
e,
(r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o)
), t;
}
function Ft(t) {
return Array.from(t.children).forEach((e) => e.remove()), t;
}
function vt(t, e, n, r) {
return t instanceof d.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
}
function Et(t, e) {
if (!(e in t)) return !1;
let n = ot(t, e);
return !S(n.set);
}
function ot(t, e) {
if (t = Object.getPrototypeOf(t), !t) return {};
let n = Object.getOwnPropertyDescriptor(t, e);
return n || ot(t, e);
}
function I(t, e, n) {
if (!(typeof e != "object" || e === null))
return Object.entries(e).forEach(function([o, c]) {
o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c));
});
}
function ct(t) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t;
}
function mt(t, e, n, r) {
return t[(S(r) ? "remove" : "set") + e](n, ct(r));
}
function xt(t, e, n, r, o = null) {
return t[(S(r) ? "remove" : "set") + e + "NS"](o, n, ct(r));
}
function et(t, e, n) {
if (Reflect.set(t, e, n), !!S(n))
return Reflect.deleteProperty(t, e);
}
// src/events-observer.js
var D = d.M ? yt() : new Proxy({}, {
get() {
return () => {
};
}
});
function yt() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (s) => function(u) {
for (let f of u)
if (f.type === "childList") {
if (h(f.addedNodes, !0)) {
s();
continue;
}
x(f.removedNodes, !0) && s();
}
}, r = new d.M(n(a));
return {
observe(s) {
let u = new d.M(n(() => {
}));
return u.observe(s, { childList: !0, subtree: !0 }), () => u.disconnect();
},
onConnected(s, u) {
i();
let f = c(s);
f.connected.has(u) || (f.connected.add(u), f.length_c += 1);
},
offConnected(s, u) {
if (!t.has(s)) return;
let f = t.get(s);
f.connected.has(u) && (f.connected.delete(u), f.length_c -= 1, o(s, f));
},
onDisconnected(s, u) {
i();
let f = c(s);
f.disconnected.has(u) || (f.disconnected.add(u), f.length_d += 1);
},
offDisconnected(s, u) {
if (!t.has(s)) return;
let f = t.get(s);
f.disconnected.has(u) && (f.disconnected.delete(u), f.length_d -= 1, o(s, f));
}
};
function o(s, u) {
u.length_c || u.length_d || (t.delete(s), a());
}
function c(s) {
if (t.has(s)) return t.get(s);
let u = {
connected: /* @__PURE__ */ new WeakSet(),
length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0
};
return t.set(s, u), u;
}
function i() {
e || (e = !0, r.observe(d.D.body, { childList: !0, subtree: !0 }));
}
function a() {
!e || t.size || (e = !1, r.disconnect());
}
function l() {
return new Promise(function(s) {
(requestIdleCallback || requestAnimationFrame)(s);
});
}
async function v(s) {
t.size > 30 && await l();
let u = [];
if (!(s instanceof Node)) return u;
for (let f of t.keys())
f === s || !(f instanceof Node) || s.contains(f) && u.push(f);
return u;
}
function h(s, u) {
let f = !1;
for (let b of s) {
if (u && v(b).then(h), !t.has(b)) continue;
let N = t.get(b);
N.length_c && (b.dispatchEvent(new Event(_)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), f = !0);
}
return f;
}
function x(s, u) {
let f = !1;
for (let b of s)
u && v(b).then(x), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(w(b)), f = !0);
return f;
}
function w(s) {
return () => {
s.isConnected || (s.dispatchEvent(new Event(C)), t.delete(s));
};
}
}
// src/customElement.js
function Zt(t, e, n, r = _t) {
m.push({
scope: t,
host: (...i) => i.length ? i.forEach((a) => a(t)) : t
}), typeof r == "function" && (r = r.call(t, t));
let o = t[O];
o || wt(t);
let c = n.call(t, r);
return o || t.dispatchEvent(new Event(_)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(C, D.observe(e), { once: !0 }), m.pop(), e.append(c);
}
function wt(t) {
return J(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(_));
}), J(t.prototype, "disconnectedCallback", function(e, n, r) {
e.apply(n, r), (globalThis.queueMicrotask || setTimeout)(
() => !n.isConnected && n.dispatchEvent(new Event(C))
);
}), J(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r;
n.dispatchEvent(new CustomEvent(M, {
detail: [o, c]
})), e.apply(n, r);
}), t.prototype[O] = !0, t;
}
function J(t, e, n) {
t[e] = new Proxy(t[e] || (() => {
}), { apply: n });
}
function _t(t) {
return F(t, (e, n) => e.getAttribute(n));
}
// src/events.js
function Qt(t, e, n) {
return e || (e = {}), function(o, ...c) {
n && (c.unshift(o), o = typeof n == "function" ? n() : n);
let i = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(i);
};
}
function y(t, e, n) {
return function(o) {
return o.addEventListener(t, e, n), o;
};
}
var it = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
y.connected = function(t, e) {
return e = it(e), function(r) {
return r.addEventListener(_, t, e), r[O] ? r : r.isConnected ? (r.dispatchEvent(new Event(_)), r) : (q(e.signal, () => D.offConnected(r, t)) && D.onConnected(r, t), r);
};
};
y.disconnected = function(t, e) {
return e = it(e), function(r) {
return r.addEventListener(C, t, e), r[O] || q(e.signal, () => D.offDisconnected(r, t)) && D.onDisconnected(r, t), r;
};
};
var Z = /* @__PURE__ */ new WeakMap();
y.disconnectedAsAbort = function(t) {
if (Z.has(t)) return Z.get(t);
let e = new AbortController();
return Z.set(t, e), t(y.disconnected(() => e.abort())), e;
};
var At = /* @__PURE__ */ new WeakSet();
y.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(M, t, e), r[O] || At.has(r) || !d.M) return r;
let o = new d.M(function(i) {
for (let { attributeName: a, target: l } of i)
l.dispatchEvent(
new CustomEvent(M, { detail: [a, l.getAttribute(a)] })
);
});
return q(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
};
};
// src/signals-lib.js
var p = "__dde_signal";
function z(t) {
try {
return T(t, p);
} catch {
return !1;
}
}
var H = [], g = /* @__PURE__ */ new WeakMap();
function E(t, e) {
if (typeof t != "function")
return st(!1, t, e);
if (z(t)) return t;
let n = st(!0), r = function() {
let [o, ...c] = g.get(r);
if (g.set(r, /* @__PURE__ */ new Set([o])), H.push(r), dt(n, t()), H.pop(), !c.length) return;
let i = g.get(r);
for (let a of c)
i.has(a) || L(a, r);
};
return g.set(n[p], r), g.set(r, /* @__PURE__ */ new Set([n])), r(), n;
}
E.action = function(t, e, ...n) {
let r = t[p], { actions: o } = r;
if (!o || !(e in o))
throw new Error(`'${t}' has no action with name '${e}'!`);
if (o[e].apply(r, n), r.skip) return delete r.skip;
r.listeners.forEach((c) => c(r.value));
};
E.on = function t(e, n, r = {}) {
let { signal: o } = r;
if (!(o && o.aborted)) {
if (Array.isArray(e)) return e.forEach((c) => t(c, n, r));
Q(e, n), o && o.addEventListener("abort", () => L(e, n));
}
};
E.symbols = {
//signal: mark,
onclear: Symbol.for("Signal.onclear")
};
E.clear = function(...t) {
for (let n of t) {
let r = n[p];
r && (delete n.toJSON, r.onclear.forEach((o) => o.call(r)), e(n, r), delete n[p]);
}
function e(n, r) {
r.listeners.forEach((o) => {
if (r.listeners.delete(o), !g.has(o)) return;
let c = g.get(o);
c.delete(n), !(c.size > 1) && (n.clear(...c), g.delete(o));
});
}
};
var R = "__dde_reactive";
E.el = function(t, e) {
let n = P.mark({ type: "reactive" }, !0), r = n.end, o = d.D.createDocumentFragment();
o.append(n, r);
let { current: c } = m, i = {}, a = (l) => {
if (!n.parentNode || !r.parentNode)
return L(t, a);
let v = i;
i = {}, m.push(c);
let h = e(l, function(u, f) {
let b;
return T(v, u) ? (b = v[u], delete v[u]) : b = f(), i[u] = b, b;
});
m.pop(), Array.isArray(h) || (h = [h]);
let x = document.createComment("");
h.push(x), n.after(...h);
let w;
for (; (w = x.nextSibling) && w !== r; )
w.remove();
x.remove(), n.isConnected && St(c.host());
};
return Q(t, a), ft(t, a, n, e), a(t()), o;
};
function St(t) {
!t || !t[R] || (requestIdleCallback || setTimeout)(function() {
t[R] = t[R].filter(([e, n]) => n.isConnected ? !0 : (L(...e), !1));
});
}
var Ot = {
_set(t) {
this.value = t;
}
};
function Ct(t) {
return function(e, n) {
let r = (...c) => c.length ? e.setAttribute(n, ...c) : K(r), o = at(r, e.getAttribute(n), Ot);
return t[n] = o, o;
};
}
var G = "__dde_attributes";
E.observedAttributes = function(t) {
let e = t[G] = {}, n = F(t, Ct(e));
return y.attributeChanged(function({ detail: o }) {
/*! This maps attributes to signals (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/
let [c, i] = o, a = this[G][c];
if (a) return E.action(a, "_set", i);
})(t), y.disconnected(function() {
/*! This removes all signals mapped to attributes (`S.observedAttributes`).
* Investigate `__dde_attributes` key of the element.*/
E.clear(...Object.values(this[G]));
})(t), n;
};
var ut = {
isSignal: z,
processReactiveAttribute(t, e, n, r) {
if (!z(n)) return n;
let o = (c) => {
if (!t.isConnected)
return L(n, o);
r(e, c);
};
return Q(n, o), ft(n, o, t, e), n();
}
};
function ft(t, e, ...n) {
let { current: r } = m;
r.prevent || r.host(function(o) {
o[R] || (o[R] = [], y.disconnected(
() => (
/*!
* Clears all Signals listeners added in the current scope/host (`S.el`, `assign`, …?).
* You can investigate the `__dde_reactive` key of the element.
* */
o[R].forEach(([[c, i]]) => L(c, i, c[p] && c[p].host && c[p].host() === o))
)
)(o)), o[R].push([[t, e], ...n]);
});
}
function st(t, e, n) {
let r = t ? () => K(r) : (...o) => o.length ? dt(r, ...o) : K(r);
return at(r, e, n, t);
}
var Dt = Object.assign(/* @__PURE__ */ Object.create(null), {
stopPropagation() {
this.skip = !0;
}
}), V = class extends Error {
constructor() {
super();
let [e, ...n] = this.stack.split(`
`), r = e.slice(e.indexOf("@"), e.indexOf(".js:") + 4);
this.stack = n.find((o) => !o.includes(r));
}
};
function at(t, e, n, r = !1) {
let o = [];
X(n) !== "[object Object]" && (n = {});
let { onclear: c } = E.symbols;
n[c] && (o.push(n[c]), delete n[c]);
let { host: i } = m;
return Reflect.defineProperty(t, p, {
value: {
value: e,
actions: n,
onclear: o,
host: i,
listeners: /* @__PURE__ */ new Set(),
defined: new V().stack,
readonly: r
},
enumerable: !1,
writable: !1,
configurable: !0
}), t.toJSON = () => t(), t.valueOf = () => t[p] && t[p].value, Object.setPrototypeOf(t[p], Dt), t;
}
function Rt() {
return H[H.length - 1];
}
function K(t) {
if (!t[p]) return;
let { value: e, listeners: n } = t[p], r = Rt();
return r && n.add(r), g.has(r) && g.get(r).add(t), e;
}
function dt(t, e, n) {
if (!t[p]) return;
let r = t[p];
if (!(!n && r.value === e))
return r.value = e, r.listeners.forEach((o) => o(e)), e;
}
function Q(t, e) {
if (t[p])
return t[p].listeners.add(e);
}
function L(t, e, n) {
let r = t[p];
if (!r) return;
let o = r.listeners.delete(e);
if (n && !r.listeners.size) {
if (E.clear(t), !g.has(r)) return o;
let c = g.get(r);
if (!g.has(c)) return o;
g.get(c).forEach((i) => L(i, c, !0));
}
return o;
}
// signals.js
B(ut);
globalThis.dde= {
S: E,
assign: j,
assignAttribute: nt,
chainableAppend: ht,
classListDeclarative: gt,
createElement: P,
createElementNS: qt,
customElementRender: Zt,
customElementWithDDE: wt,
dispatchEvent: Qt,
el: P,
elNS: qt,
elementAttribute: vt,
empty: Ft,
isSignal: z,
lifecyclesToEvents: wt,
observedAttributes: _t,
on: y,
registerReactivity: B,
scope: m,
signal: E,
simulateSlots: Wt
};
})();

460
dist/dde.js vendored
View File

@ -1,460 +0,0 @@
//deka-dom-el library is available via global namespace `dde`
(()=> {
// src/signals-common.js
var C = {
isSignal(t) {
return !1;
},
processReactiveAttribute(t, e, n, r) {
return n;
}
};
function V(t, e = !0) {
return e ? Object.assign(C, t) : (Object.setPrototypeOf(t, C), t);
}
function L(t) {
return C.isPrototypeOf(t) && t !== C ? t : C;
}
// src/helpers.js
var q = (...t) => Object.prototype.hasOwnProperty.call(...t);
function E(t) {
return typeof t > "u";
}
function N(t, e) {
if (!t || !(t instanceof AbortSignal))
return !0;
if (!t.aborted)
return t.addEventListener("abort", e), function() {
t.removeEventListener("abort", e);
};
}
function F(t, e) {
let { observedAttributes: n = [] } = t.constructor;
return n.reduce(function(r, o) {
return r[J(o)] = e(t, o), r;
}, {});
}
function J(t) {
return t.replace(/-./g, (e) => e[1].toUpperCase());
}
// src/dom-common.js
var a = {
setDeleteAttr: K,
ssr: "",
D: globalThis.document,
F: globalThis.DocumentFragment,
H: globalThis.HTMLElement,
S: globalThis.SVGElement,
M: globalThis.MutationObserver
};
function K(t, e, n) {
if (Reflect.set(t, e, n), !!E(n)) {
if (Reflect.deleteProperty(t, e), t instanceof a.H && t.getAttribute(e) === "undefined")
return t.removeAttribute(e);
if (Reflect.get(t, e) === "undefined")
return Reflect.set(t, e, "");
}
}
var x = "__dde_lifecyclesToEvents", g = "dde:connected", y = "dde:disconnected", D = "dde:attributeChanged";
// src/dom.js
var v = [{
get scope() {
return a.D.body;
},
host: (t) => t ? t(a.D.body) : a.D.body,
prevent: !0
}], S = {
get current() {
return v[v.length - 1];
},
get host() {
return this.current.host;
},
preventDefault() {
let { current: t } = this;
return t.prevent = !0, t;
},
get state() {
return [...v];
},
push(t = {}) {
return v.push(Object.assign({}, this.current, { prevent: !1 }, t));
},
pushRoot() {
return v.push(v[0]);
},
pop() {
if (v.length !== 1)
return v.pop();
}
};
function $(...t) {
return this.appendOriginal(...t), this;
}
function Q(t) {
return t.append === $ || (t.appendOriginal = t.append, t.append = $), t;
}
var T;
function j(t, e, ...n) {
let r = L(this), o = 0, c, f;
switch ((Object(e) !== e || r.isSignal(e)) && (e = { textContent: e }), !0) {
case typeof t == "function": {
o = 1, S.push({ scope: t, host: (...b) => b.length ? (o === 1 ? n.unshift(...b) : b.forEach((h) => h(f)), void 0) : f }), c = t(e || void 0);
let d = c instanceof a.F;
if (c.nodeName === "#comment") break;
let p = j.mark({
type: "component",
name: t.name,
host: d ? "this" : "parentElement"
});
c.prepend(p), d && (f = p);
break;
}
case t === "#text":
c = O.call(this, a.D.createTextNode(""), e);
break;
case (t === "<>" || !t):
c = O.call(this, a.D.createDocumentFragment(), e);
break;
case !!T:
c = O.call(this, a.D.createElementNS(T, t), e);
break;
case !c:
c = O.call(this, a.D.createElement(t), e);
}
return Q(c), f || (f = c), n.forEach((d) => d(f)), o && S.pop(), o = 2, c;
}
function bt(t, e, n) {
typeof e != "object" && (n = e, e = t);
let r = Symbol.for("default"), o = Array.from(e.querySelectorAll("slot")).reduce((f, d) => Reflect.set(f, d.name || r, d) && f, {}), c = q(o, r);
if (t.append = new Proxy(t.append, {
apply(f, d, p) {
if (p[0] === e) return f.apply(t, p);
if (!p.length) return t;
let b = a.D.createDocumentFragment();
for (let h of p) {
if (!h || !h.slot) {
c && b.append(h);
continue;
}
let A = h.slot, _ = o[A];
tt(h, "remove", "slot"), _ && (X(_, h, n), Reflect.deleteProperty(o, A));
}
return c && (o[r].replaceWith(b), Reflect.deleteProperty(o, r)), t.append = f, t;
}
}), t !== e) {
let f = Array.from(t.childNodes);
f.forEach((d) => d.remove()), t.append(...f);
}
return e;
}
function X(t, e, n) {
n && n(t, e);
try {
t.replaceWith(O(e, { className: [e.className, t.className], dataset: { ...t.dataset } }));
} catch {
t.replaceWith(e);
}
}
j.mark = function(t, e = !1) {
t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
let n = e ? "" : "/", r = a.D.createComment(`<dde:mark ${t}${a.ssr}${n}>`);
return e && (r.end = a.D.createComment("</dde:mark>")), r;
};
function gt(t) {
let e = this;
return function(...r) {
T = t;
let o = j.call(e, ...r);
return T = void 0, o;
};
}
var P = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: U } = a;
function O(t, ...e) {
if (!e.length) return t;
P.set(t, B(t, this));
for (let [n, r] of Object.entries(Object.assign({}, ...e)))
z.call(this, t, n, r);
return P.delete(t), t;
}
function z(t, e, n) {
let { setRemoveAttr: r, s: o } = B(t, this), c = this;
n = o.processReactiveAttribute(
t,
e,
n,
(d, p) => z.call(c, t, d, p)
);
let [f] = e;
if (f === "=") return r(e.slice(1), n);
if (f === ".") return H(t, e.slice(1), n);
if (/(aria|data)([A-Z])/.test(e))
return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), r(e, n);
switch (e === "className" && (e = "class"), e) {
case "xlink:href":
return r(e, n, "http://www.w3.org/1999/xlink");
case "textContent":
return U(t, e, n);
case "style":
if (typeof n != "object") break;
/* falls through */
case "dataset":
return M(o, n, H.bind(null, t[e]));
case "ariaset":
return M(o, n, (d, p) => r("aria-" + d, p));
case "classList":
return Y.call(c, t, n);
}
return et(t, e) ? U(t, e, n) : r(e, n);
}
function B(t, e) {
if (P.has(t)) return P.get(t);
let r = (t instanceof a.S ? rt : nt).bind(null, t, "Attribute"), o = L(e);
return { setRemoveAttr: r, s: o };
}
function Y(t, e) {
let n = L(this);
return M(
n,
e,
(r, o) => t.classList.toggle(r, o === -1 ? void 0 : !!o)
), t;
}
function vt(t) {
return Array.from(t.children).forEach((e) => e.remove()), t;
}
function tt(t, e, n, r) {
return t instanceof a.H ? t[e + "Attribute"](n, r) : t[e + "AttributeNS"](null, n, r);
}
function et(t, e) {
if (!(e in t)) return !1;
let n = I(t, e);
return !E(n.set);
}
function I(t, e) {
if (t = Object.getPrototypeOf(t), !t) return {};
let n = Object.getOwnPropertyDescriptor(t, e);
return n || I(t, e);
}
function M(t, e, n) {
if (!(typeof e != "object" || e === null))
return Object.entries(e).forEach(function([o, c]) {
o && (c = t.processReactiveAttribute(e, o, c, n), n(o, c));
});
}
function Z(t) {
return Array.isArray(t) ? t.filter(Boolean).join(" ") : t;
}
function nt(t, e, n, r) {
return t[(E(r) ? "remove" : "set") + e](n, Z(r));
}
function rt(t, e, n, r, o = null) {
return t[(E(r) ? "remove" : "set") + e + "NS"](o, n, Z(r));
}
function H(t, e, n) {
if (Reflect.set(t, e, n), !!E(n))
return Reflect.deleteProperty(t, e);
}
// src/events-observer.js
var w = a.M ? ot() : new Proxy({}, {
get() {
return () => {
};
}
});
function ot() {
let t = /* @__PURE__ */ new Map(), e = !1, n = (i) => function(u) {
for (let s of u)
if (s.type === "childList") {
if (h(s.addedNodes, !0)) {
i();
continue;
}
A(s.removedNodes, !0) && i();
}
}, r = new a.M(n(d));
return {
observe(i) {
let u = new a.M(n(() => {
}));
return u.observe(i, { childList: !0, subtree: !0 }), () => u.disconnect();
},
onConnected(i, u) {
f();
let s = c(i);
s.connected.has(u) || (s.connected.add(u), s.length_c += 1);
},
offConnected(i, u) {
if (!t.has(i)) return;
let s = t.get(i);
s.connected.has(u) && (s.connected.delete(u), s.length_c -= 1, o(i, s));
},
onDisconnected(i, u) {
f();
let s = c(i);
s.disconnected.has(u) || (s.disconnected.add(u), s.length_d += 1);
},
offDisconnected(i, u) {
if (!t.has(i)) return;
let s = t.get(i);
s.disconnected.has(u) && (s.disconnected.delete(u), s.length_d -= 1, o(i, s));
}
};
function o(i, u) {
u.length_c || u.length_d || (t.delete(i), d());
}
function c(i) {
if (t.has(i)) return t.get(i);
let u = {
connected: /* @__PURE__ */ new WeakSet(),
length_c: 0,
disconnected: /* @__PURE__ */ new WeakSet(),
length_d: 0
};
return t.set(i, u), u;
}
function f() {
e || (e = !0, r.observe(a.D.body, { childList: !0, subtree: !0 }));
}
function d() {
!e || t.size || (e = !1, r.disconnect());
}
function p() {
return new Promise(function(i) {
(requestIdleCallback || requestAnimationFrame)(i);
});
}
async function b(i) {
t.size > 30 && await p();
let u = [];
if (!(i instanceof Node)) return u;
for (let s of t.keys())
s === i || !(s instanceof Node) || i.contains(s) && u.push(s);
return u;
}
function h(i, u) {
let s = !1;
for (let l of i) {
if (u && b(l).then(h), !t.has(l)) continue;
let m = t.get(l);
m.length_c && (l.dispatchEvent(new Event(g)), m.connected = /* @__PURE__ */ new WeakSet(), m.length_c = 0, m.length_d || t.delete(l), s = !0);
}
return s;
}
function A(i, u) {
let s = !1;
for (let l of i)
u && b(l).then(A), !(!t.has(l) || !t.get(l).length_d) && ((globalThis.queueMicrotask || setTimeout)(_(l)), s = !0);
return s;
}
function _(i) {
return () => {
i.isConnected || (i.dispatchEvent(new Event(y)), t.delete(i));
};
}
}
// src/customElement.js
function Dt(t, e, n, r = it) {
S.push({
scope: t,
host: (...f) => f.length ? f.forEach((d) => d(t)) : t
}), typeof r == "function" && (r = r.call(t, t));
let o = t[x];
o || ct(t);
let c = n.call(t, r);
return o || t.dispatchEvent(new Event(g)), e.nodeType === 11 && typeof e.mode == "string" && t.addEventListener(y, w.observe(e), { once: !0 }), S.pop(), e.append(c);
}
function ct(t) {
return k(t.prototype, "connectedCallback", function(e, n, r) {
e.apply(n, r), n.dispatchEvent(new Event(g));
}), k(t.prototype, "disconnectedCallback", function(e, n, r) {
e.apply(n, r), (globalThis.queueMicrotask || setTimeout)(
() => !n.isConnected && n.dispatchEvent(new Event(y))
);
}), k(t.prototype, "attributeChangedCallback", function(e, n, r) {
let [o, , c] = r;
n.dispatchEvent(new CustomEvent(D, {
detail: [o, c]
})), e.apply(n, r);
}), t.prototype[x] = !0, t;
}
function k(t, e, n) {
t[e] = new Proxy(t[e] || (() => {
}), { apply: n });
}
function it(t) {
return F(t, (e, n) => e.getAttribute(n));
}
// src/events.js
function _t(t, e, n) {
return e || (e = {}), function(o, ...c) {
n && (c.unshift(o), o = typeof n == "function" ? n() : n);
let f = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
return o.dispatchEvent(f);
};
}
function R(t, e, n) {
return function(o) {
return o.addEventListener(t, e, n), o;
};
}
var G = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
R.connected = function(t, e) {
return e = G(e), function(r) {
return r.addEventListener(g, t, e), r[x] ? r : r.isConnected ? (r.dispatchEvent(new Event(g)), r) : (N(e.signal, () => w.offConnected(r, t)) && w.onConnected(r, t), r);
};
};
R.disconnected = function(t, e) {
return e = G(e), function(r) {
return r.addEventListener(y, t, e), r[x] || N(e.signal, () => w.offDisconnected(r, t)) && w.onDisconnected(r, t), r;
};
};
var W = /* @__PURE__ */ new WeakMap();
R.disconnectedAsAbort = function(t) {
if (W.has(t)) return W.get(t);
let e = new AbortController();
return W.set(t, e), t(R.disconnected(() => e.abort())), e;
};
var st = /* @__PURE__ */ new WeakSet();
R.attributeChanged = function(t, e) {
return typeof e != "object" && (e = {}), function(r) {
if (r.addEventListener(D, t, e), r[x] || st.has(r) || !a.M) return r;
let o = new a.M(function(f) {
for (let { attributeName: d, target: p } of f)
p.dispatchEvent(
new CustomEvent(D, { detail: [d, p.getAttribute(d)] })
);
});
return N(e.signal, () => o.disconnect()) && o.observe(r, { attributes: !0 }), r;
};
};
globalThis.dde= {
assign: O,
assignAttribute: z,
chainableAppend: Q,
classListDeclarative: Y,
createElement: j,
createElementNS: gt,
customElementRender: Dt,
customElementWithDDE: ct,
dispatchEvent: _t,
el: j,
elNS: gt,
elementAttribute: tt,
empty: vt,
lifecyclesToEvents: ct,
observedAttributes: it,
on: R,
registerReactivity: V,
scope: S,
simulateSlots: bt
};
})();

File diff suppressed because it is too large Load Diff

1477
dist/esm-with-signals.js vendored

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 {};

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

File diff suppressed because one or more lines are too long

970
dist/esm.d.ts vendored

File diff suppressed because it is too large Load Diff

1025
dist/esm.js vendored

File diff suppressed because it is too large Load Diff

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 {};

1
dist/esm.min.js vendored Normal file

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 {};

1031
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 {};

702
dist/iife.js vendored Normal file
View File

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

861
dist/iife.min.d.ts vendored 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

60
docs/assets/favicon.svg Normal file
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>

After

Width:  |  Height:  |  Size: 3.3 KiB

92
docs/assets/logo.svg Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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>

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,28 +1,179 @@
import { registerClientFile, styles } from "../ssr.js";
const host= "."+code.name;
styles.css`
${host}{
--shiki-color-text: #e9eded;
--shiki-color-background: #212121;
/* Code block styling */
${host} {
/* Theme for dark mode - matches Flems/CodeMirror dark theme */
--shiki-color-text: #f8f8f2;
--shiki-color-background: var(--code-bg);
--shiki-token-constant: #82b1ff;
--shiki-token-string: #c3e88d;
--shiki-token-comment: #546e7a;
--shiki-token-keyword: #c792ea;
--shiki-token-parameter: #AA0000;
--shiki-token-parameter: #fd971f;
--shiki-token-function: #80cbae;
--shiki-token-string-expression: #c3e88d;
--shiki-token-punctuation: var(--code);
--shiki-token-link: #EE0000;
--shiki-token-punctuation: #89ddff;
--shiki-token-link: #82aaff;
--shiki-token-variable: #f8f8f2;
--shiki-token-number: #f78c6c;
--shiki-token-boolean: #82b1ff;
--shiki-token-tag: #f07178;
--shiki-token-attribute: #ffcb6b;
--shiki-token-property: #82b1ff;
--shiki-token-operator: #89ddff;
--shiki-token-regex: #c3e88d;
--shiki-token-class: #ffcb6b;
/* Basic styling */
white-space: pre;
tab-size: 2; /* TODO: allow custom tab size?! */
overflow: scroll;
tab-size: 2;
overflow: auto;
border-radius: var(--border-radius);
font-family: var(--font-mono);
font-size: .85rem;
line-height: 1.5;
position: relative;
margin-block: 1rem;
max-width: 100%;
}
${host}[data-js=todo]{
/* Light mode overrides to match GitHub-like theme */
@media (prefers-color-scheme: light) {
${host} {
--shiki-color-text: #24292e;
--shiki-color-background: var(--code-bg);
--shiki-token-constant: #005cc5;
--shiki-token-string: #22863a;
--shiki-token-comment: #6a737d;
--shiki-token-keyword: #d73a49;
--shiki-token-parameter: #e36209;
--shiki-token-function: #6f42c1;
--shiki-token-string-expression: #22863a;
--shiki-token-punctuation: #24292e;
--shiki-token-link: #0366d6;
--shiki-token-variable: #24292e;
--shiki-token-number: #005cc5;
--shiki-token-boolean: #005cc5;
--shiki-token-tag: #22863a;
--shiki-token-attribute: #005cc5;
--shiki-token-property: #005cc5;
--shiki-token-operator: #d73a49;
--shiki-token-regex: #032f62;
--shiki-token-class: #6f42c1;
}
}
/* Support for theme toggles */
html[data-theme="light"] ${host} {
--shiki-color-text: #24292e;
--shiki-color-background: var(--code-bg);
--shiki-token-constant: #005cc5;
--shiki-token-string: #22863a;
--shiki-token-comment: #6a737d;
--shiki-token-keyword: #d73a49;
--shiki-token-parameter: #e36209;
--shiki-token-function: #6f42c1;
--shiki-token-string-expression: #22863a;
--shiki-token-punctuation: #24292e;
--shiki-token-link: #0366d6;
--shiki-token-variable: #24292e;
--shiki-token-number: #005cc5;
--shiki-token-boolean: #005cc5;
--shiki-token-tag: #22863a;
--shiki-token-attribute: #005cc5;
--shiki-token-property: #005cc5;
--shiki-token-operator: #d73a49;
--shiki-token-regex: #032f62;
--shiki-token-class: #6f42c1;
}
html[data-theme="dark"] ${host} {
--shiki-color-text: #f8f8f2;
--shiki-color-background: var(--code-bg);
--shiki-token-constant: #82b1ff;
--shiki-token-string: #c3e88d;
--shiki-token-comment: #546e7a;
--shiki-token-keyword: #c792ea;
--shiki-token-parameter: #fd971f;
--shiki-token-function: #80cbae;
--shiki-token-string-expression: #c3e88d;
--shiki-token-punctuation: #89ddff;
--shiki-token-link: #82aaff;
--shiki-token-variable: #f8f8f2;
--shiki-token-number: #f78c6c;
--shiki-token-boolean: #82b1ff;
--shiki-token-tag: #f07178;
--shiki-token-attribute: #ffcb6b;
--shiki-token-property: #82b1ff;
--shiki-token-operator: #89ddff;
--shiki-token-regex: #c3e88d;
--shiki-token-class: #ffcb6b;
}
/* Code block with syntax highlighting waiting for JS */
${host}[data-js=todo] {
border: 1px solid var(--border);
border-radius: var(--standard-border-radius);
margin-bottom: 1rem;
margin-top: 18.4px; /* to fix shift when → dataJS=done */
padding: 1rem 1.4rem;
border-radius: var(--border-radius);
padding: 1rem;
background-color: var(--code-bg);
position: relative;
}
/* Add a subtle loading indicator */
${host}[data-js=todo]::before {
content: "Loading syntax highlighting...";
position: absolute;
top: 0.5rem;
right: 0.5rem;
font-size: 0.75rem;
color: var(--text-light);
background-color: var(--bg);
padding: 0.25rem 0.5rem;
border-radius: var(--border-radius);
opacity: 0.7;
}
/* All code blocks should have consistent font and sizing */
${host} code {
font-family: var(--font-mono);
font-size: inherit;
line-height: 1.5;
padding: 0;
}
${host} pre {
margin-block: 0;
font-size: inherit;
}
/* Ensure line numbers (if added) are styled appropriately */
${host} .line-number {
user-select: none;
opacity: 0.5;
margin-right: 1rem;
min-width: 1.5rem;
display: inline-block;
text-align: right;
}
/* If there's a copy button, style it */
${host} .copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
background-color: var(--bg);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--border-radius);
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
cursor: pointer;
opacity: 0;
transition: opacity 0.2s ease;
}
${host}:hover .copy-button {
opacity: 1;
}
`;
import { el } from "deka-dom-el";
@ -33,31 +184,48 @@ 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 {"js"|"ts"|"html"|"css"|"shell"} [attrs.language="js"] Language of the code
* @param {string} [attrs.page_id] ID of the page, if setted it registers shiki
* */
export function code({ id, src, content, language= "js", className= host.slice(1), page_id }){
if(src) content= s.cat(src);
content= normalizeIndentation(content);
let dataJS;
if(page_id){
registerClientPart(page_id);
dataJS= "todo";
}
return el("div", { id, className, dataJS }).append(
el("code", { className: "language-"+language, textContent: content })
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){
if(is_registered[page_id]) return;
// Add Shiki with a more reliable loading method
document.head.append(
el("script", { src: "https://cdn.jsdelivr.net/npm/shiki@0.9", defer: true }),
// 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 }),
);
registerClientFile(
new URL("./code.js.js", import.meta.url),
el("script", { type: "module" })
{
head: el("script", { type: "module" }),
}
);
is_registered[page_id]= true;
}
/** @param {string} src */
function normalizeIndentation(src){
const lines= src.split("\n");
const min_indent= Math.min(...lines.map(line=> line.search(/\S/)).filter(i=> i >= 0));
return lines.map(line=> line.slice(min_indent)).join("\n");
}

View File

@ -1,12 +1,61 @@
const highlighter= await globalThis.shiki.getHighlighter({
theme: "css-variables",
langs: ["js", "ts", "css", "html", "shell"],
});
const codeBlocks= document.querySelectorAll('code[class*="language-"]');
try {
// Initialize Shiki with our custom theme
const highlighter = await globalThis.shiki.getHighlighter({
theme: "css-variables",
langs: ["javascript", "typescript", "css", "html", "shell"],
});
codeBlocks.forEach((block)=> {
const lang= block.className.replace("language-", "");
block.parentElement.dataset.js= "done";
const html= highlighter.codeToHtml(block.textContent, { lang });
block.innerHTML= html;
});
// Find all code blocks that need highlighting
const codeBlocks = document.querySelectorAll('div[data-js="todo"] code[class*="language-"]');
// Process each code block
codeBlocks.forEach((block) => {
try {
// Get the language from the class
const langClass = block.className.match(/language-(\w+)/);
// Map the language to Shiki format
let lang = langClass ? langClass[1] : 'javascript';
if (lang === 'js') lang = 'javascript';
if (lang === 'ts') lang = 'typescript';
// Mark the container as processed
block.parentElement.dataset.js = "done";
// Highlight the code
const code = block.textContent;
const html = highlighter.codeToHtml(code, { lang });
// Insert the highlighted HTML
block.innerHTML = html;
// Add copy button functionality
const copyBtn = document.createElement('button');
copyBtn.className = 'copy-button';
copyBtn.textContent = 'Copy';
copyBtn.setAttribute('aria-label', 'Copy code to clipboard');
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(code).then(() => {
copyBtn.textContent = 'Copied!';
setTimeout(() => {
copyBtn.textContent = 'Copy';
}, 2000);
});
});
// Add the copy button to the code block container
block.parentElement.appendChild(copyBtn);
} catch (err) {
console.error('Error highlighting code block:', err);
// Make sure we don't leave the block in a pending state
block.parentElement.dataset.js = "error";
}
});
} catch (err) {
console.error('Failed to initialize Shiki:', err);
// Fallback: at least mark blocks as processed so they don't show loading indicator
document.querySelectorAll('div[data-js="todo"]').forEach(block => {
block.dataset.js = "error";
});
}

View File

@ -0,0 +1,176 @@
import { 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({ page_id }){
registerClientPart(page_id);
return el(ireland, {
src: fileURL("./converter.js.js"),
exportName: "converter",
page_id,
});
}
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,16 +1,110 @@
import { styles } from "../ssr.js";
const host= "."+example.name;
styles.css`
${host}{
${host} {
grid-column: full-main;
width: 100%;
max-width: calc(9/5 * var(--body-max-width));
height: calc(3/5 * var(--body-max-width));
margin-inline: auto;
height: calc(4/6 * var(--body-max-width));
border-radius: var(--border-radius);
box-shadow: var(--shadow);
border: 1px solid var(--border);
}
${host} .runtime {
background-color: whitesmoke;
}
/* CodeMirror styling to match our theme */
.CodeMirror {
height: 100% !important;
font-family: var(--font-mono) !important;
font-size: .85rem !important;
line-height: 1.5 !important;
}
/* Dark mode styles for CodeMirror */
.CodeMirror, .CodeMirror-gutters {
background: #212121 !important;
border: 1px solid white;
background: var(--code-bg) !important;
color: var(--text) !important;
}
/* Light mode adjustments for CodeMirror - using CSS variables */
@media (prefers-color-scheme: light) {
/* Core syntax elements */
.cm-s-material .cm-keyword { color: var(--shiki-token-keyword, #d73a49) !important; }
.cm-s-material .cm-atom { color: var(--shiki-token-constant, #005cc5) !important; }
.cm-s-material .cm-number { color: var(--shiki-token-number, #005cc5) !important; }
.cm-s-material .cm-def { color: var(--shiki-token-function, #6f42c1) !important; }
.cm-s-material .cm-variable { color: var(--shiki-token-variable, #24292e) !important; }
.cm-s-material .cm-variable-2 { color: var(--shiki-token-variable, #24292e) !important; }
.cm-s-material .cm-variable-3 { color: var(--shiki-token-variable, #24292e) !important; }
.cm-s-material .cm-property { color: var(--shiki-token-property, #005cc5) !important; }
.cm-s-material .cm-operator { color: var(--shiki-token-operator, #d73a49) !important; }
.cm-s-material .cm-comment { color: var(--shiki-token-comment, #6a737d) !important; }
.cm-s-material .cm-string { color: var(--shiki-token-string, #22863a) !important; }
.cm-s-material .cm-string-2 { color: var(--shiki-token-string, #22863a) !important; }
.cm-s-material .cm-tag { color: var(--shiki-token-tag, #22863a) !important; }
.cm-s-material .cm-attribute { color: var(--shiki-token-attribute, #005cc5) !important; }
.cm-s-material .cm-bracket { color: var(--shiki-token-punctuation, #24292e) !important; }
.cm-s-material .cm-punctuation { color: var(--shiki-token-punctuation, #24292e) !important; }
.cm-s-material .cm-link { color: var(--shiki-token-link, #0366d6) !important; }
.cm-s-material .cm-error { color: #f44336 !important; }
}
/* Handle theme toggle */
html[data-theme="light"] .CodeMirror {
background: #f5f7fa !important;
}
html[data-theme="light"] .CodeMirror-gutters {
background: #f5f7fa !important;
border-right: 1px solid #e5e7eb !important;
}
/* Also apply the same styles to CodeMirror with data-theme */
html[data-theme="light"] .cm-s-material .cm-keyword { color: var(--shiki-token-keyword, #d73a49) !important; }
html[data-theme="light"] .cm-s-material .cm-atom { color: var(--shiki-token-constant, #005cc5) !important; }
html[data-theme="light"] .cm-s-material .cm-number { color: var(--shiki-token-number, #005cc5) !important; }
html[data-theme="light"] .cm-s-material .cm-def { color: var(--shiki-token-function, #6f42c1) !important; }
html[data-theme="light"] .cm-s-material .cm-variable { color: var(--shiki-token-variable, #24292e) !important; }
html[data-theme="light"] .cm-s-material .cm-variable-2 { color: var(--shiki-token-variable, #24292e) !important; }
html[data-theme="light"] .cm-s-material .cm-variable-3 { color: var(--shiki-token-variable, #24292e) !important; }
html[data-theme="light"] .cm-s-material .cm-property { color: var(--shiki-token-property, #005cc5) !important; }
html[data-theme="light"] .cm-s-material .cm-operator { color: var(--shiki-token-operator, #d73a49) !important; }
html[data-theme="light"] .cm-s-material .cm-comment { color: var(--shiki-token-comment, #6a737d) !important; }
html[data-theme="light"] .cm-s-material .cm-string { color: var(--shiki-token-string, #22863a) !important; }
html[data-theme="light"] .cm-s-material .cm-string-2 { color: var(--shiki-token-string, #22863a) !important; }
html[data-theme="light"] .cm-s-material .cm-tag { color: var(--shiki-token-tag, #22863a) !important; }
html[data-theme="light"] .cm-s-material .cm-attribute { color: var(--shiki-token-attribute, #005cc5) !important; }
html[data-theme="light"] .cm-s-material .cm-bracket { color: var(--shiki-token-punctuation, #24292e) !important; }
html[data-theme="light"] .cm-s-material .cm-punctuation { color: var(--shiki-token-punctuation, #24292e) !important; }
html[data-theme="light"] .cm-s-material .cm-link { color: var(--shiki-token-link, #0366d6) !important; }
html[data-theme="light"] .cm-s-material .cm-error { color: #f44336 !important; }
/* Mobile adjustments */
@media (max-width: 767px) {
${host} {
height: 50vh;
}
${host} main {
flex-grow: 1;
display: flex;
flex-direction: column;
}
${host} main > * {
width: 100%;
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;
}
}
`;
@ -24,15 +118,16 @@ import { relative } from "node:path";
* @param {object} attrs
* @param {URL} attrs.src Example code file path
* @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
* @param {"normal"|"big"} [attrs.variant="normal"] Size of the example
* @param {string} attrs.page_id ID of the page
* */
export function example({ src, language= "js", page_id }){
export function example({ src, language= "js", variant= "normal", page_id }){
registerClientPart(page_id);
const content= s.cat(src).toString()
.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ' from "./esm-with-signals.js";');
const id= "code-example-"+generateCodeId(src);
return el().append(
el(code, { id, content, language, className: example.name }),
el(code, { id, content, language, className: example.name }, el=> el.dataset.variant= variant),
elCode({ id, content, extension: "."+language })
);
}

View File

@ -0,0 +1,374 @@
/**
* 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);
}
.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':
document.querySelector('.lightbox-prev-btn').click();
break;
case 'ArrowRight':
document.querySelector('.lightbox-next-btn').click();
break;
}
}
// Build the gallery UI
return el("div", { className: "gallery-container" }).append(
// Gallery header
el("header", { className: "gallery-header" }).append(
el("h1", "Interactive Image Gallery"),
el("p", "Click on any image to view it in the lightbox. Use arrow keys for navigation.")
),
// Filter options
el("div", { className: "gallery-filters" }).append(
el("button", {
classList: { active: S(() => filterTag.get() === 'all') },
textContent: "All"
}, onFilterChange('all')),
el("button", {
classList: { active: S(() => filterTag.get() === 'nature') },
textContent: "Nature"
}, onFilterChange('nature')),
el("button", {
classList: { active: S(() => filterTag.get() === 'urban') },
textContent: "Urban"
}, onFilterChange('urban')),
el("button", {
classList: { active: S(() => filterTag.get() === 'people') },
textContent: "People"
}, onFilterChange('people'))
),
// Image grid
el("div", { className: "gallery-grid" }).append(
S.el(imagesToDisplay, images =>
images.map(image =>
memo(image.id, ()=>
el("div", {
className: "gallery-item",
dataTag: image.alt.toLowerCase()
}).append(
el("img", {
src: image.src,
alt: image.alt,
loading: "lazy"
}, onImageClick(image.id)),
el("div", { className: "gallery-item-caption" }).append(
el("h3", image.title),
el("p", image.alt)
)
)
)
)
)
),
// Lightbox (only shown when an image is selected)
S.el(isLightboxOpen, open => !open
? el()
: el("div", { className: "lightbox-overlay" }, on("click", closeLightbox)).append(
el("div", {
className: "lightbox-content",
onClick: e => e.stopPropagation() // Prevent closing when clicking inside
}).append(
el("button", {
className: "lightbox-close-btn",
"aria-label": "Close lightbox"
}, on("click", closeLightbox)).append("×"),
el("button", {
className: "lightbox-prev-btn",
"aria-label": "Previous image"
}, on("click", onPrevImage)).append(""),
el("button", {
className: "lightbox-next-btn",
"aria-label": "Next image"
}, on("click", onNextImage)).append(""),
S.el(selectedImage, img => !img
? el()
: el("div", { className: "lightbox-image-container" }).append(
el("img", {
src: img.src,
alt: img.alt,
className: "lightbox-image"
}),
el("div", { className: "lightbox-caption" }).append(
el("h2", img.title),
el("p", img.alt)
)
)
)
)
)
),
);
}
// Render the component
document.body.append(
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
el(ImageGallery)
),
el("style", `
.gallery-container {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.gallery-header {
text-align: center;
margin-bottom: 2rem;
}
.gallery-header h1 {
margin-bottom: 0.5rem;
color: #333;
}
.gallery-header p {
color: #666;
}
.gallery-filters {
display: flex;
justify-content: center;
margin-bottom: 2rem;
flex-wrap: wrap;
}
.gallery-filters button {
background: none;
border: none;
padding: 0.5rem 1.5rem;
margin: 0 0.5rem;
font-size: 1rem;
cursor: pointer;
border-radius: 30px;
transition: all 0.3s ease;
color: #555;
}
.gallery-filters button:hover {
background: #f0f0f0;
}
.gallery-filters button.active {
background: #4a90e2;
color: white;
}
.gallery-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 1.5rem;
}
.gallery-item {
position: relative;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
cursor: pointer;
}
.gallery-item:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
display: block;
transition: transform 0.5s ease;
}
.gallery-item:hover img {
transform: scale(1.05);
}
.gallery-item-caption {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
color: white;
padding: 1rem;
transform: translateY(100%);
transition: transform 0.3s ease;
}
.gallery-item:hover .gallery-item-caption {
transform: translateY(0);
}
.gallery-item-caption h3 {
margin: 0 0 0.5rem;
font-size: 1.2rem;
}
.gallery-item-caption p {
margin: 0;
font-size: 0.9rem;
opacity: 0.8;
}
/* Lightbox styles */
.lightbox-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
padding: 2rem;
}
.lightbox-content {
position: relative;
max-width: 90%;
max-height: 90%;
}
.lightbox-image-container {
overflow: hidden;
border-radius: 4px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
background: #000;
}
.lightbox-image {
max-width: 100%;
max-height: 80vh;
display: block;
margin: 0 auto;
}
.lightbox-caption {
background: #222;
color: white;
padding: 1rem;
text-align: center;
}
.lightbox-caption h2 {
margin: 0 0 0.5rem;
}
.lightbox-caption p {
margin: 0;
opacity: 0.8;
}
.lightbox-close-btn,
.lightbox-prev-btn,
.lightbox-next-btn {
background: rgba(0, 0, 0, 0.5);
color: white;
border: none;
font-size: 1.5rem;
width: 50px;
height: 50px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background 0.3s ease;
position: absolute;
}
.lightbox-close-btn:hover,
.lightbox-prev-btn:hover,
.lightbox-next-btn:hover {
background: rgba(0, 0, 0, 0.8);
}
.lightbox-close-btn {
top: -25px;
right: -25px;
}
.lightbox-prev-btn {
left: -25px;
top: 50%;
transform: translateY(-50%);
}
.lightbox-next-btn {
right: -25px;
top: 50%;
transform: translateY(-50%);
}
@media (max-width: 768px) {
.gallery-container {
padding: 1rem;
}
.gallery-grid {
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
}
.lightbox-prev-btn,
.lightbox-next-btn {
width: 40px;
height: 40px;
font-size: 1.2rem;
}
}
`)
);

View File

@ -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,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(300px, 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

@ -7,9 +7,9 @@ export class HTMLCustomElement extends HTMLElement{
static observedAttributes= [ "attr" ];
connectedCallback(){
customElementRender(
this,
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,17 +1,14 @@
import {
customElementRender,
customElementWithDDE,
observedAttributes,
el, on, scope
el, on, scope,
} from "deka-dom-el";
import { S } from "deka-dom-el/signals";
export class HTMLCustomElement extends HTMLElement{
static tagName= "custom-element";
static observedAttributes= [ "attr" ];
connectedCallback(){
console.log(observedAttributes(this));
customElementRender(
this,
this.attachShadow({ mode: "open" }),
ddeComponent,
S.observedAttributes
@ -24,10 +21,10 @@ export class HTMLCustomElement extends HTMLElement{
/** @param {{ attr: ddeSignal<string, {}> }} props */
function ddeComponent({ attr }){
scope.host(
on.connected(e=> console.log(e.target.outerHTML)),
on.connected(e=> console.log(( /** @type {HTMLParagraphElement} */ (e.target)).outerHTML)),
);
return el().append(
el("p", S(()=> `Hello from Custom Element with attribute '${attr()}'`))
el("p", S(()=> `Hello from Custom Element with attribute '${attr.get()}'`))
);
}
customElementWithDDE(HTMLCustomElement);

View File

@ -17,11 +17,7 @@ function ddeComponent(){
export class A extends HTMLElement{
static tagName= "custom-element-without";
connectedCallback(){
customElementRender(
this,
this,
ddeComponent
);
customElementRender(this, ddeComponent);
}
}
customElementWithDDE(A);
@ -30,7 +26,6 @@ export class B extends HTMLElement{
static tagName= "custom-element-open";
connectedCallback(){
customElementRender(
this,
this.attachShadow({ mode: "open" }),
ddeComponent
);
@ -42,7 +37,6 @@ export class C extends HTMLElement{
static tagName= "custom-element-closed";
connectedCallback(){
customElementRender(
this,
this.attachShadow({ mode: "closed" }),
ddeComponent
);

View File

@ -8,7 +8,7 @@ export class HTMLCustomElement extends HTMLElement{
static tagName= "custom-slotting";
connectedCallback(){
const c= ()=> simulateSlots(this, ddeComponent());
customElementRender(this, this, c);
customElementRender(this, c);
}
}
customElementWithDDE(HTMLCustomElement);

View File

@ -0,0 +1,15 @@
// Debugging a (derived) signal with `console.log`
import { S } from "deka-dom-el/signals";
const name= S("Alice");
const greeting = S(() => {
// log derived signals
const log = "Hello, " + name.get();
console.log(log);
console.log(name.valueOf());
return log;
});
// log signals in general
S.on(greeting, value => console.log("Greeting changed to:", value));
name.set("Bob"); // Should trigger computation and listener`)

View File

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

View File

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

View File

@ -0,0 +1,15 @@
import { S } from "deka-dom-el/signals";
// Wrong - direct mutation doesn't trigger updates
const todos1 = S([{ text: "Learn signals", completed: false }]);
todos1.get().push({ text: "Debug signals", completed: false }); // Won't trigger updates!
// Correct - using .set() with a new array
todos1.set([...todos1.get(), { text: "Debug signals", completed: false }]);
// Better - using actions
const todos2 = S([], {
add(text) {
this.value.push({ text, completed: false });
}
});
S.action(todos2, "add", "Debug signals");

View File

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

View File

@ -0,0 +1,11 @@
import { el } from "deka-dom-el";
// Chainable, natural nesting
// append() returns parent element
// making chains easy and intuitive
document.body.append(
el("div").append(
el("h1", "Title"),
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

@ -11,7 +11,7 @@ document.body.append(
);
function component({ className, textContent }){
return el("div", { className: [ "class1", className ] }).append(
return el("div", { className: [ "class1", className ].join(" ") }).append(
el("p", textContent)
);
}

View File

@ -2,8 +2,8 @@ import { elNS, assign } from "deka-dom-el";
const elSVG= elNS("http://www.w3.org/2000/svg");
const elMath= elNS("http://www.w3.org/1998/Math/MathML");
document.body.append(
elSVG("svg"), // see https://developer.mozilla.org/en-US/docs/Web/SVG and https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
elMath("math") // see https://developer.mozilla.org/en-US/docs/Web/MathML and https://developer.mozilla.org/en-US/docs/Web/MathML/Global_attributes
elSVG("svg"), // see https://developer.mozilla.org/en-US/docs/Web/SVG and https://developer.mozilla.org/en-US/docs/Web/API/SVGElement // editorconfig-checker-disable-line
elMath("math") // see https://developer.mozilla.org/en-US/docs/Web/MathML and https://developer.mozilla.org/en-US/docs/Web/MathML/Global_attributes // editorconfig-checker-disable-line
);
console.log(

View File

@ -0,0 +1,19 @@
// Create element with properties
const button = document.createElement('button');
button.textContent = "Click me";
button.className = "primary";
button.disabled = true;
// Or using Object.assign()
const button2 = Object.assign(
document.createElement('button'),
{
textContent: "Click me",
className: "primary",
disabled: true
}
);
// Add to DOM
document.body.append(button);
document.body.append(button2);

View File

@ -0,0 +1,15 @@
// Verbose, needs temp variables
const div = document.createElement('div');
const h1 = document.createElement('h1');
h1.textContent = 'Title';
div.append(h1);
const p = document.createElement('p');
p.textContent = 'Paragraph';
div.append(p);
// append doesn't return parent
// so chaining is not possible
// Add to DOM
document.body.append(div);

View File

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

View File

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

View File

@ -0,0 +1,8 @@
import { el, on } from "deka-dom-el";
// Using events as addons - chainable approach
el("button", {
textContent: "click me",
},
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

@ -0,0 +1,2 @@
// Standard DOM event listener approach
element.addEventListener('click', callback, options);

View File

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

View File

@ -0,0 +1,13 @@
// pseudocode
// Mixed concerns make code hard to maintain
const button = document.querySelector('button');
let count = 0;
button.addEventListener('click', () => {
count++;
document.querySelector('p').textContent =
'Clicked ' + count + ' times';
if (count > 10)
button.disabled = true;
});

View File

@ -1,6 +1,14 @@
// pseudo code!
const onchage=
event=>
console.log("Reacting to the:", event); // A
input.addEventListener("change", onchange); // B
input.dispatchEvent(new Event("change")); // C
// pseudocode
// 1. Create state
const count = S(0);
// 2. React to state changes
S.on(count, value => {
updateUI(value);
if (value > 10) disableButton();
});
// 3. Update state on events
button.addEventListener('click', () => {
count.set(count.get() + 1);
});

View File

@ -1,15 +1,30 @@
import { el } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
const clicks= S(0); // A
document.body.append(
el().append(
el("p", S(()=>
"Hello World "+"🎉".repeat(clicks()) // B
)),
// A HelloWorld component using the 3PS pattern
function HelloWorld({ emoji = "🚀" }) {
// 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())}`)
}),
// PART 3: Update state in response to events
el("button", {
type: "button",
onclick: ()=> clicks(clicks()+1), // C
textContent: "Fire",
textContent: "Add emoji",
// When clicked, update the state
onclick: () => clicks.set(clicks.get() + 1)
})
)
);
}
// Use the component in your app
document.body.append(
el(HelloWorld, { emoji: "🎉" })
);

View File

@ -0,0 +1,36 @@
import { el, on } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
document.body.append(
el(HelloWorldComponent, { initial: "🚀" })
);
/** @typedef {"🎉" | "🚀"} Emoji */
/** @param {{ initial: Emoji }} attrs */
function HelloWorldComponent({ initial }){
const clicks= S(0);
const emoji= S(initial);
/** @param {HTMLOptionElement} el */
const isSelected= el=> (el.selected= el.value===initial);
// @ts-expect-error 2339: The <select> has only two options with {@link Emoji}
const onChange= on("change", event=> emoji(event.target.value));
return el().append(
el("p", {
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.set(clicks.get() + 1)),
on("keyup", ()=> clicks.set(clicks.get() - 2)),
),
el("select", null, onChange).append(
el(OptionComponent, "🎉", isSelected),//OR { textContent: "🎉" }
el(OptionComponent, "🚀", isSelected),//OR { textContent: "🚀" }
)
);
}
function OptionComponent({ textContent }){
return el("option", { value: textContent, textContent })
}

View File

@ -0,0 +1,37 @@
import { el } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
const className = "client-side-counter";
document.body.append(
el("style").append(`
.${className} {
border: 1px dashed #ccc;
padding: 1em;
margin: 1em;
}
`.trim())
);
export function CounterStandard() {
// Create reactive state with a signal
const count = S(0);
// Create UI components that react to state changes
return el("div", { className }).append(
el("h4", "Client-Side Counter"),
el("div", {
// The textContent updates automatically when count changes
textContent: S(() => `Count: ${count.get()}`),
}),
el("div", { className: "controls" }).append(
el("button", {
onclick: () => count.set(count.get() - 1),
textContent: "-",
}),
el("button", {
onclick: () => count.set(count.get() + 1),
textContent: "+",
})
)
);
}

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,388 @@
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, todos => !todos.length
? el()
: el("footer", { className: "footer" }).append(
el("span", { className: "todo-count" }).append(
noOfLeft()
),
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()}`
})
)
)
),
),
todos.length - todosRemainingS.get() === 0
? el()
: memo("delete", () =>
el("button",
{ textContent: "Clear completed", className: "clear-completed" },
onClearCompleted)
)
)
)
);
function noOfLeft(){
const length = todosRemainingS.get();
return el("strong").append(
length + " ",
length === 1 ? "item left" : "items left"
)
}
}
/**
* 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

@ -1,4 +1,6 @@
import { el, empty, on } from "deka-dom-el";
import { el, on } from "deka-dom-el";
/** @param {HTMLElement} el */
const empty= el=> Array.from(el.children).forEach(c=> c.remove());
document.body.append(
el(component),
el("button", {
@ -12,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,35 +1,26 @@
/* PSEUDO-CODE!!! */
import { el } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function component(){
/* prepare changeable data */
const dataA= S("data");
const dataB= S("data");
/* define data flow (can be asynchronous) */
fetchAPI().then(data_new=> dataA(data_new));
setTimeout(()=> dataB("DATA"));
/* declarative UI */
return el().append(
el("h1", {
textContent: "Example",
/* declarative attribute(s) */
classList: { declarative: dataB }
}),
el("ul").append(
/* declarative element(s) */
S.el(dataA, data=> data.map(d=> el("li", d)))
),
el("ul").append(
/* declarative component(s) */
S.el(dataA, data=> data.map(d=> el(subcomponent, d)))
)
function Counter() {
// Define state
const count = S(0);
// Define behavior
const increment = () => count.set(count.get() + 1);
// Define data flow
setTimeout(increment, 1000);
// or fetchAPI().then(increment);
// Declarative UI (how to render data/`count`)
// …automatically updates when changes
return el("div").append(
// declarative element(s)
el("p", S(() => "Count: " + count.get())),
el("button", {
onclick: increment,
textContent: "Increment",
// declarative attribute(s)
disabled: S(() => count.get() >= 10)
})
);
}
function subcomponent({ id }){
/* prepare changeable data */
const textContent= S("…");
/* define data flow (can be asynchronous) */
fetchAPI(id).then(text=> textContent(text));
/* declarative UI */
return el("li", { textContent, dataId: id });
}

View File

@ -1,31 +1,25 @@
/* PSEUDO-CODE!!! */
import { el, on, scope } from "deka-dom-el";
function component(){
const { host }= scope;
const ul= el("ul");
const ac= new AbortController();
fetchAPI({ signal: ac.signal }).then(data=> {
data.forEach(d=> ul.append(el("li", d)));
});
host(
/* element was remove before data fetched */
on.disconnected(()=> ac.abort())
import { el, scope } from "deka-dom-el";
function Counter() {
const { host } = scope;
let count = 0;
const counterText = el("p", "Count: 0");
// Manually update DOM element
const increment = () => {
count++;
counterText.textContent = "Count: " + count;
host().querySelector("button").disabled = count >= 10;
};
setTimeout(increment, 1000);
// or fetchAPI().then(increment);
return el("div").append(
counterText,
el("button", {
onclick: increment,
textContent: "Increment"
})
);
return ul;
/**
* NEVER EVER!!
* let data;
* fetchAPI().then(d=> data= O(d));
*
* OR NEVER EVER!!
* const ul= el("ul");
* fetchAPI().then(d=> {
* const data= O("data");
* ul.append(el("li", data));
* });
*
* // THE HOST IS PROBABLY DIFFERENT THAN
* // YOU EXPECT AND OBSERVABLES MAY BE
* // UNEXPECTEDLY REMOVED!!!
* */
}

View File

@ -0,0 +1,38 @@
/* PSEUDO-CODE!!! */
import { el, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function Counter() {
const { host } = scope;
let count = S(0);
const counterText = el("p", "Count: 0");
S.on(count, c=> counterText.textContent= "Count: " + c);
// Manually update DOM element
const increment = () => {
count.set(count.get() + 1);
// NEVER EVER
// count = S(count.get() + 1);
// THE HOST IS PROBABLY DIFFERENT THAN
// YOU EXPECT AND SIGNAL MAY BE
// UNEXPECTEDLY REMOVED!!!
S.on(count, (count)=>
host().querySelector("button").disabled = count >= 10
);
};
setTimeout(()=> {
// ok, BUT consider extract to separate function
// see section below for more info
const ok= S(0);
S.on(ok, console.log);
setInterval(()=> ok.set(ok.get() + 1), 100);
}, 100);
return el("div").append(
counterText,
el("button", {
onclick: increment,
textContent: "Increment"
})
);
}

View File

@ -0,0 +1,45 @@
import { el, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
function CounterWithIsolatedTimer() {
const { host } = scope;
// 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);
// 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);
}
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)
);

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);
}
});
@ -22,9 +22,9 @@ const onsubmit= on("submit", function(event){
S.action(todos, "push", data.get("todo"));
break;
case "E"/*dit*/: {
const last= todos().at(-1);
const last= todos.get().at(-1);
if(!last) break;
last(data.get("todo"));
last.set(data.get("todo"));
break;
}
case "R"/*emove*/:

View File

@ -1,15 +1,15 @@
import { S } from "deka-dom-el/signals";
const signal= S(0);
// computation pattern
const double= S(()=> 2*signal());
const double= S(()=> 2*signal.get());
const ac= new AbortController();
S.on(signal, v=> console.log("signal", v), { signal: ac.signal });
S.on(double, v=> console.log("double", v), { signal: ac.signal });
signal(signal()+1);
signal.set(signal.get()+1);
const interval= 5 * 1000;
const id= setInterval(()=> signal(signal()+1), interval);
const id= setInterval(()=> signal.set(signal.get()+1), interval);
ac.signal.addEventListener("abort",
()=> setTimeout(()=> clearInterval(id), 2*interval));

View File

@ -0,0 +1,20 @@
import { S } from "deka-dom-el/signals";
// Debugging a derived signal
const name = S('Alice');
const greeting = S(() => {
console.log('Computing greeting...');
return 'Hello, ' + name.get();
});
// Monitor the derived signal
S.on(greeting, value => console.log('Greeting changed to:', value));
// Later update the dependency
name.set('Bob'); // Should trigger computation and listener
// Console output:
// Computing greeting...
// Greeting changed to: Hello, Alice
// Computing greeting...
// Greeting changed to: Hello, Bob

View File

@ -0,0 +1,38 @@
import { el, on, scope } from "deka-dom-el";
import { S } from "deka-dom-el/signals";
// Create a component with reactive elements
function ReactiveCounter() {
const count = S(0);
scope.host(on.connected(ev=>
console.log(ev.target.__dde_reactive)
));
const counter = el('div', {
// This element will be added into the __dde_reactive property
textContent: count,
});
const incrementBtn = el('button', {
textContent: 'Increment',
onclick: () => count.set(count.get() + 1)
});
// Dynamic section will be added into __dde_signal property
const counterInfo = S.el(count, value =>
el('p', `Current count is ${value}`)
);
return el('div', { id: 'counter' }).append(
counter,
incrementBtn,
counterInfo
);
}
document.body.append(
el(ReactiveCounter),
);
// In DevTools console:
const counter = document.querySelector('#counter');
setTimeout(()=> console.log(counter.__dde_reactive), 1000); // See reactive bindings

View File

@ -0,0 +1,13 @@
import { S } from "deka-dom-el/signals";
// Create base signals
const firstName = S("John");
const lastName = S("Doe");
// Create a derived signal
const fullName = S(() => firstName.get() + " " + lastName.get());
// The fullName signal updates automatically when either dependency changes
S.on(fullName, name => console.log("Name changed to:", name));
firstName.set("Jane"); // logs: "Name changed to: Jane Doe"

View File

@ -3,8 +3,8 @@ const count= S(0);
import { el } from "deka-dom-el";
document.body.append(
el("p", S(()=> "Currently: "+count())),
el("p", { classList: { red: S(()=> count()%2) }, dataset: { count }, textContent: "Attributes example" })
el("p", S(()=> "Currently: "+count.get())),
el("p", { classList: { red: S(()=> count.get()%2 === 0) }, dataset: { count }, textContent: "Attributes example" }),
);
document.head.append(
el("style", ".red { color: red; }")
@ -12,4 +12,4 @@ document.head.append(
const interval= 5 * 1000;
setTimeout(clearInterval, 10*interval,
setInterval(()=> count(count()+1), interval));
setInterval(()=> count.set(count.get()+1), interval));

View File

@ -2,7 +2,7 @@ import { S } from "deka-dom-el/signals";
const count= S(0, {
add(){ this.value= this.value + Math.round(Math.random()*10); }
});
const numbers= S([ count() ], {
const numbers= S([ count.get() ], {
push(next){ this.value.push(next); }
});
@ -22,5 +22,5 @@ document.body.append(
const interval= 5*1000;
setTimeout(clearInterval, 10*interval, setInterval(function(){
S.action(count, "add");
S.action(numbers, "push", count());
S.action(numbers, "push", count.get());
}, interval));

View File

@ -1,10 +1,10 @@
import { S } from "deka-dom-el/signals";
// α — `signal` represents a reactive value
// PART 1 — `signal` represents a reactive value
const signal= S(0);
// β — just reacts on signal changes
// PART 2 — just reacts on signal changes
S.on(signal, console.log);
// γ — just updates the value
const update= ()=> signal(signal()+1);
// PART 3 — just updates the value
const update= ()=> signal.set(signal.get()+1);
update();
const interval= 5*1000;

View File

@ -0,0 +1,47 @@
// Handling async data in SSR
import { JSDOM } from "jsdom";
import { register, queue } from "deka-dom-el/jsdom";
async function renderWithAsyncData() {
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
const { el } = await register(dom);
// Create a component that fetches data
const { AsyncComponent } = await import("./components/AsyncComponent.js");
// Render the page
dom.window.document.body.append(
el("h1", "Page with Async Data"),
el(AsyncComponent)
);
// IMPORTANT: Wait for all queued operations to complete
await queue();
// Now the HTML includes all async content
const html = dom.serialize();
console.log(html);
}
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

@ -0,0 +1,48 @@
// Basic SSR Example
import { JSDOM } from "jsdom";
import { register, queue } from "deka-dom-el/jsdom";
import { writeFileSync } from "node:fs";
async function renderPage() {
// Create a jsdom instance
const dom = new JSDOM("<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head><body></body></html>");
// Register with deka-dom-el and get the el function
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),
el("nav").append(
el("ul").append(
el("li").append(el("a", { href: "/" }, "Home")),
el("li").append(el("a", { href: "/about" }, "About")),
el("li").append(el("a", { href: "/contact" }, "Contact"))
)
)
);
}
// Create the page content
dom.window.document.body.append(
el(Header, { title: "My Static Site" }),
el("main").append(
el("h2", "Welcome!"),
el("p", "This page was rendered with deka-dom-el on the server.")
),
el("footer", "© 2025 My Company")
);
// Wait for any async operations
await queue();
// Get the HTML and write it to a file
const html = dom.serialize();
writeFileSync("index.html", html);
console.log("Page rendered successfully!");
}
renderPage().catch(console.error);

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 { register, unregister, queue } from "deka-dom-el/jsdom";

View File

@ -0,0 +1,36 @@
// ❌ WRONG: Static imports are hoisted and will register before JSDOM is created
import { register } from "deka-dom-el/jsdom";
import { el } from "deka-dom-el";
import { Header } from "./components/Header.js";
// ✅ CORRECT: Use dynamic imports to ensure proper initialization order
import { JSDOM } from "jsdom";
async function renderPage() {
// 1. Create JSDOM instance first
const dom = new JSDOM(`<!DOCTYPE html><html><body></body></html>`);
// 2. Dynamically import jsdom module
const { register, queue } = await import("deka-dom-el/jsdom");
// 3. Register and get el function
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");
// 5. Render components
const body = dom.window.document.body;
el(body).append(
el(Header, { title: "My Page" }),
el(Content, { text: "This is server-rendered content" })
);
// 6. Wait for async operations
await queue();
// 7. Get HTML and clean up
return dom.serialize();
}

View File

@ -0,0 +1,27 @@
// Basic jsdom integration example
import { JSDOM } from "jsdom";
import { register, unregister, queue } from "deka-dom-el/jsdom";
// Create a jsdom instance
const dom = new JSDOM("<!DOCTYPE html><html><body></body></html>");
// Register the dom with deka-dom-el
const { el } = await register(dom);
// Use deka-dom-el normally
dom.window.document.body.append(
el("div", { className: "container" }).append(
el("h1", "Hello, SSR World!"),
el("p", "This content was rendered on the server.")
)
);
// Wait for any async operations to complete
await queue();
// Get the rendered HTML
const html = dom.serialize();
console.log(html);
// Clean up when done
unregister();

View File

@ -0,0 +1,45 @@
// Building a simple static site generator
import { JSDOM } from "jsdom";
import { register, queue } from "deka-dom-el/jsdom";
import { writeFileSync, mkdirSync } from "node:fs";
async function buildSite() {
// Define pages to build
const pages = [
{ id: "index", title: "Home", component: "./pages/home.js" },
{ id: "about", title: "About", component: "./pages/about.js" },
{ id: "docs", title: "Documentation", component: "./pages/docs.js" }
];
// Create output directory
mkdirSync("./dist/docs", { recursive: true });
// Build each page
for (const page of pages) {
// Create a fresh jsdom instance for each page
const dom = new JSDOM("<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head><body></body></html>");
// Register with deka-dom-el
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
dom.window.document.body.append(
el(PageComponent, { title: page.title, pages })
);
// Wait for any async operations
await queue();
// Write the HTML to a file
const html = dom.serialize();
writeFileSync(`./dist/docs/${page.id}.html`, html);
console.log(`Built page: ${page.id}.html`);
}
}
buildSite().catch(console.error);

View File

@ -0,0 +1,83 @@
import { styles } 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({ page_id }){
return el(ireland, {
src: new URL("./getLibraryUrl.js.js", import.meta.url),
exportName: "getLibraryUrl",
page_id,
});
}

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