mirror of
https://github.com/jaandrle/deka-dom-el
synced 2025-07-01 12:22:15 +02:00
Compare commits
12 Commits
9a5909eb90
...
v0.9.5-alp
Author | SHA1 | Date | |
---|---|---|---|
f2c85ec983 | |||
4c450ae763 | |||
04f93345f8
|
|||
5076771410 | |||
f0dfdfde54 | |||
25d475ec04 | |||
e1f321004d | |||
47c5fda8d6
|
|||
7f3b818fa5
|
|||
d742d960ac | |||
d56d5e45d5
|
|||
4366027658 |
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
40
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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
22
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal 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 -->
|
29
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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
39
.github/pull_request_template.md
vendored
Normal 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
18
.github/workflows/npm-publish.yml
vendored
Normal 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 }}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
dist/docs/
|
||||
dist/.*
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
3
.npmrc
Normal file
3
.npmrc
Normal 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
134
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,134 @@
|
||||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
andrle.jan@centrum.cz.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
177
CONTRIBUTING.md
Normal file
177
CONTRIBUTING.md
Normal file
@ -0,0 +1,177 @@
|
||||
# Contributing to Deka DOM Elements
|
||||
|
||||
Thank you for your interest in contributing to Deka DOM Elements (dd<el> or DDE)! This document provides guidelines and
|
||||
instructions for contributing to the project.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [Getting Started](#getting-started)
|
||||
- [Development Workflow](#development-workflow)
|
||||
- [Commit Guidelines](#commit-guidelines)
|
||||
- [Pull Request Process](#pull-request-process)
|
||||
- [Issue Guidelines](#issue-guidelines)
|
||||
- [Coding Standards](#coding-standards)
|
||||
- [Testing](#testing)
|
||||
- [Documentation](#documentation)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please be respectful and inclusive in your interactions with other contributors. We aim to foster a welcoming community
|
||||
where everyone feels comfortable participating.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. **Fork the repository**:
|
||||
- Click the "Fork" button on the GitHub repository
|
||||
|
||||
2. **Clone your fork**:
|
||||
```bash
|
||||
git clone https://github.com/YOUR-USERNAME/deka-dom-el.git
|
||||
cd deka-dom-el
|
||||
```
|
||||
|
||||
3. **Set up the development environment**:
|
||||
```bash
|
||||
npm ci
|
||||
```
|
||||
|
||||
4. **Add the upstream repository**:
|
||||
```bash
|
||||
git remote add upstream https://github.com/jaandrle/deka-dom-el.git
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **Create a new branch**:
|
||||
```bash
|
||||
git checkout -b your-feature-branch
|
||||
```
|
||||
Use descriptive branch names that reflect the changes you're making.
|
||||
|
||||
2. **Make your changes**:
|
||||
- Write clean, modular code
|
||||
- Follow the project's coding standards (see [Coding Standards](#coding-standards))
|
||||
- Include relevant tests for your changes
|
||||
|
||||
3. ~**Run tests**:~
|
||||
```bash
|
||||
#npm test
|
||||
```
|
||||
|
||||
4. **Build the project**:
|
||||
```bash
|
||||
npm run build
|
||||
#or
|
||||
bs/build.js
|
||||
```
|
||||
|
||||
5. **Preview documentation changes** (if applicable):
|
||||
```bash
|
||||
npm run docs
|
||||
#or
|
||||
bs/docs.js
|
||||
```
|
||||
|
||||
…see [BS folder](./bs/README.md) for more info.
|
||||
|
||||
## Categorizing [](https://robinpokorny.github.io/git3moji/) <!-- editorconfig-checker-disable-line -->
|
||||
We use [git3moji](https://git3moji.netlify.app/) for commit messages, issue titles, pull request titles and in other
|
||||
areas. To make categorizing quick and consistent.
|
||||
|
||||
## Commit Guidelines
|
||||
|
||||
We use [git3moji](https://git3moji.netlify.app/) for commit messages. This helps keep the commit history clear and
|
||||
consistent.
|
||||
|
||||
```
|
||||
:emoji: Short summary of the change
|
||||
```
|
||||
…for example:
|
||||
|
||||
```
|
||||
:bug: Fix signal update not triggering on nested properties
|
||||
:zap: Improve event delegation performance
|
||||
:abc: Add documentation for custom elements
|
||||
```
|
||||
|
||||
## Pull Request Process
|
||||
|
||||
1. **Push your changes**:
|
||||
```bash
|
||||
git push origin your-feature-branch
|
||||
```
|
||||
|
||||
2. **Open a Pull Request**:
|
||||
- Go to the repository on GitHub
|
||||
- Click "New Pull Request"
|
||||
- Select your branch
|
||||
- Provide a clear description of your changes
|
||||
|
||||
3. **PR Guidelines**:
|
||||
- Use a clear, descriptive title with the appropriate git3moji
|
||||
- Reference any related issues
|
||||
- Explain what the changes do and why they are needed
|
||||
- List any dependencies that are required for the change
|
||||
- Include screenshots or examples if applicable
|
||||
|
||||
4. **Code Review**:
|
||||
- Address any feedback from reviewers
|
||||
- Make necessary changes and push to your branch
|
||||
- The PR will be updated automatically
|
||||
|
||||
5. **Merge**:
|
||||
- Once approved, a maintainer will merge your PR
|
||||
- The main branch is protected, so you cannot push directly to it
|
||||
|
||||
## Issue Guidelines
|
||||
|
||||
When creating an issue, please use the appropriate template and include as much information as possible:
|
||||
|
||||
### Bug Reports
|
||||
|
||||
- Use the `:bug:` emoji in the title
|
||||
- Clearly describe the issue
|
||||
- Include steps to reproduce
|
||||
- Mention your environment (browser, OS, etc.)
|
||||
- Add screenshots if applicable
|
||||
|
||||
### Feature Requests
|
||||
|
||||
- Use the `:zap:` emoji in the title
|
||||
- Describe the feature clearly
|
||||
- Explain why it would be valuable
|
||||
- Include examples or mockups if possible
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
- Use the `:abc:` emoji in the title
|
||||
- Identify what documentation needs improvement
|
||||
- Suggest specific changes or additions
|
||||
|
||||
## Coding Standards
|
||||
|
||||
- Follow the existing code style in the project
|
||||
- Use meaningful variable and function names
|
||||
- Keep functions small and focused
|
||||
- Add comments for complex logic
|
||||
- Use TypeScript types appropriately
|
||||
|
||||
<!--
|
||||
## Testing
|
||||
|
||||
- Add tests for new features
|
||||
- Update tests for modified code
|
||||
- Ensure all tests pass before submitting a PR
|
||||
-->
|
||||
|
||||
## Documentation
|
||||
|
||||
- Update the documentation when you add or modify features
|
||||
- Document both API usage and underlying concepts
|
||||
- Use clear, concise language
|
||||
- Include examples where appropriate
|
||||
|
||||
---
|
||||
|
||||
Thank you for contributing to Deka DOM Elements! Your efforts help make the project better for everyone.
|
201
README.md
201
README.md
@ -1,95 +1,146 @@
|
||||
**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**
|
||||
| [Docs&Examples](https://jaandrle.github.io/deka-dom-el "Official documentation and guide site")
|
||||
| [NPM](https://www.npmjs.com/package/deka-dom-el "Official NPM package page")
|
||||
| [GitHub](https://github.com/jaandrle/deka-dom-el "Official GitHub repository")
|
||||
([*Gitea*](https://gitea.jaandrle.cz/jaandrle/deka-dom-el "GitHub repository mirror on my own Gitea instance"))
|
||||
|
||||
***Vanilla for flavouring — a full-fledged feast for large projects***
|
||||
|
||||
*…use simple DOM API by default and library tools and logic when you need them*
|
||||
|
||||
```javascript
|
||||
// 🌟 Reactive component with clear separation of concerns
|
||||
document.body.append(
|
||||
el(HelloWorldComponent, { initial: "🚀" })
|
||||
el(EmojiCounter, { 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));
|
||||
|
||||
function EmojiCounter({ initial }) {
|
||||
// ✨ - Define reactive data
|
||||
const count = S(0);
|
||||
const emoji = S(initial);
|
||||
const textContent = S(() => `Hello World ${emoji.get().repeat(count.get())}`);
|
||||
|
||||
// 🔄 - UI updates automatically when signals change
|
||||
return el().append(
|
||||
el("p", {
|
||||
textContent: S(() => `Hello World ${emoji().repeat(clicks())}`),
|
||||
className: "example",
|
||||
ariaLive: "polite", //OR ariaset: { live: "polite" },
|
||||
dataset: { example: "Example" }, //OR dataExample: "Example",
|
||||
}),
|
||||
el("button",
|
||||
{ textContent: "Fire", type: "button" },
|
||||
on("click", ()=> clicks(clicks() + 1)),
|
||||
on("keyup", ()=> clicks(clicks() - 2)),
|
||||
el("p", { textContent, className: "output" }),
|
||||
|
||||
// 🎮 - Update state on events
|
||||
el("button", { textContent: "Add Emoji" },
|
||||
on("click", () => count.set(count.get() + 1)),
|
||||
),
|
||||
|
||||
el("select", null,
|
||||
on.defer(el=> el.value= initial),
|
||||
on("change", e => emoji.set(e.target.value)),
|
||||
).append(
|
||||
el(Option, "🎉"),
|
||||
el(Option, "🚀"),
|
||||
el(Option, "💖"),
|
||||
),
|
||||
el("select", null, onChange).append(
|
||||
el(OptionComponent, "🎉", isSelected),//OR { textContent: "🎉" }
|
||||
el(OptionComponent, "🚀", isSelected),//OR { textContent: "🚀" }
|
||||
)
|
||||
);
|
||||
}
|
||||
function OptionComponent({ textContent }){
|
||||
return el("option", { value: textContent, textContent })
|
||||
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).
|
||||
*…use simple DOM API by default and library tools and logic when you need them*
|
||||
|
||||
## 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)
|
||||
<p align="center">
|
||||
<img src="docs/assets/logo.svg" alt="Deka DOM Elements Logo" width="180" height="180">
|
||||
</p>
|
||||
|
||||
## 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.
|
||||
# Deka DOM Elements (dd\<el\> or DDE)
|
||||
|
||||
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.
|
||||
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).
|
||||
|
||||
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.
|
||||
## Features at a Glance
|
||||
|
||||
To balance these requirements, numerous compromises have been made. To summarize:
|
||||
- [ ] Library size: 10–15kB 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
|
||||
- ✅ **No build step required** — use directly in browsers or Node.js
|
||||
- ✅ **Minimalized footprint** — ~10-15kB minified bundle (original goal 10kB), **zero**/minimal dependencies and
|
||||
small in-memory size (auto-releasing resources as much as possible)
|
||||
- ✅ **Declarative & functional approach support** for clean, maintainable code
|
||||
- ✅ **Signals and events** for reactive UI
|
||||
- ✅ **Memoization for performance** — optimize rendering with intelligent caching
|
||||
- ☑️ **Optional build-in signals** with support for custom reactive implementations (#39)
|
||||
- ☑️ **Server-side rendering** support via [jsdom](https://github.com/jsdom/jsdom)
|
||||
- ✅ **TypeScript support**
|
||||
- ✅ **Support for debugging with browser DevTools** without extensions
|
||||
- ☑️ **Enhanced Web Components** support
|
||||
|
||||
## 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/`…)
|
||||
## Getting Started
|
||||
|
||||
## 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)
|
||||
### Quick Links
|
||||
|
||||
- [**Documentation and Guide**](https://jaandrle.github.io/deka-dom-el)
|
||||
- [**Examples**](https://jaandrle.github.io/deka-dom-el/p15-examples.html)
|
||||
- [**Changelog**](https://github.com/jaandrle/deka-dom-el/releases)
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install deka-dom-el --save
|
||||
```
|
||||
|
||||
…or via CDN / Direct Script:
|
||||
|
||||
For CDN links and various build formats (ESM/IIFE, with/without signals, minified/unminified), see the [interactive
|
||||
format selector](https://jaandrle.github.io/deka-dom-el/#h-getting-started) on the documentation site.
|
||||
|
||||
```html
|
||||
<!-- Example with IIFE build (creates a global DDE object) -->
|
||||
<script src="https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/iife-with-signals.min.js"></script>
|
||||
<script>
|
||||
const { el, S } = DDE;
|
||||
// Your code here
|
||||
</script>
|
||||
|
||||
<!-- Or with ES modules -->
|
||||
<script type="module">
|
||||
import { el, S } from "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/esm-with-signals.min.js";
|
||||
// Your code here
|
||||
</script>
|
||||
```
|
||||
|
||||
## 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.
|
||||
- [TarekRaafat/eleva](https://github.com/TarekRaafat/eleva) — A minimalist, lightweight, pure vanilla JavaScript
|
||||
frontend runtime framework.
|
||||
- [didi/mpx](https://github.com/didi/mpx) — Mpx,一款具有优秀开发体验和深度性能优化的增强型跨端小程序框架
|
||||
- [mxjp/rvx](https://github.com/mxjp/rvx) — A signal based frontend framework
|
||||
- [jaandrle/dollar_dom_component](https://github.com/jaandrle/dollar_dom_component) —
|
||||
Functional DOM components without JSX/virtual DOM (my old library)
|
||||
|
@ -2,15 +2,18 @@
|
||||
This project uses [jaandrle/bs: The simplest possible build system using executable/bash scripts](
|
||||
https://github.com/jaandrle/bs).
|
||||
|
||||
#### bs/build.js [--minify|--help]
|
||||
#### bs/build.js [main|signals] [--no-types|--help]
|
||||
Generates alternative versions of the project (other than native ESM code).
|
||||
Also generates typescript definitions.
|
||||
|
||||
#### bs/docs.js
|
||||
Generates documentation, from `docs/`. Uses “SSR” technique, using deka-dom-el itself.
|
||||
|
||||
For running use `npx serve dist/docs`.
|
||||
|
||||
#### bs/lint.sh
|
||||
Lints size of the project, jshint. See configs:
|
||||
|
||||
- `package.json`: key `size-limit`
|
||||
- `package.json`: key `jshintConfig`
|
||||
- `.editorconfig`
|
||||
|
99
bs/build.js
99
bs/build.js
@ -1,71 +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
101
bs/dev/.build.js
Normal 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);
|
||||
}
|
17
bs/docs.js
17
bs/docs.js
@ -2,9 +2,9 @@
|
||||
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */// editorconfig-checker-disable-line
|
||||
echo("Building static documentation files…");
|
||||
echo("Preparing…");
|
||||
import { path_target, pages as pages_registered, styles, dispatchEvent, t } from "../docs/ssr.js";
|
||||
import { path_target, pages as pages_registered, styles, currentPageId, dispatchEvent, t } from "../docs/ssr.js";
|
||||
import { createHTMl } from "./docs/jsdom.js";
|
||||
import { register } 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){
|
||||
@ -24,15 +28,24 @@ for(const { id, info } of pages){
|
||||
);
|
||||
const { el }= await register(serverDOM.dom);
|
||||
const { page }= await import(`../docs/${id}.html.js`);
|
||||
currentPageId(id)
|
||||
serverDOM.document.body.append(
|
||||
el(page, { pkg, info }),
|
||||
);
|
||||
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");
|
||||
|
||||
|
@ -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';
|
||||
|
11
bs/lint.sh
11
bs/lint.sh
@ -1,5 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eou pipefail
|
||||
npx editorconfig-checker -format gcc
|
||||
# if $1=vim -no-color
|
||||
one=${1:-''}
|
||||
additional=''
|
||||
[ "$one" = 'vim' ] && additional='-no-color'
|
||||
npx editorconfig-checker -format gcc ${additional}
|
||||
[ "$one" = 'vim' ] && additional='--reporter unix'
|
||||
npx jshint index.js src ${additional}
|
||||
[ "$one" = 'vim' ] && exit 0
|
||||
npx size-limit
|
||||
npx jshint index.js src
|
||||
npx publint
|
||||
|
670
dist/dde-with-signals.js
vendored
670
dist/dde-with-signals.js
vendored
@ -1,670 +0,0 @@
|
||||
//deka-dom-el library is available via global namespace `dde`
|
||||
(()=> {
|
||||
// src/signals-common.js
|
||||
var T = {
|
||||
isSignal(t) {
|
||||
return !1;
|
||||
},
|
||||
processReactiveAttribute(t, e, r, n) {
|
||||
return r;
|
||||
}
|
||||
};
|
||||
function H(t, e = !0) {
|
||||
return e ? Object.assign(T, t) : (Object.setPrototypeOf(t, T), t);
|
||||
}
|
||||
function j(t) {
|
||||
return T.isPrototypeOf(t) && t !== T ? t : T;
|
||||
}
|
||||
|
||||
// src/helpers.js
|
||||
var I = (...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 P(t, e) {
|
||||
if (!t || !(t instanceof AbortSignal))
|
||||
return !0;
|
||||
if (!t.aborted)
|
||||
return t.addEventListener("abort", e), function() {
|
||||
t.removeEventListener("abort", e);
|
||||
};
|
||||
}
|
||||
function W(t, e) {
|
||||
let { observedAttributes: r = [] } = t.constructor;
|
||||
return r.reduce(function(n, o) {
|
||||
return n[dt(o)] = e(t, o), n;
|
||||
}, {});
|
||||
}
|
||||
function dt(t) {
|
||||
return t.replace(/-./g, (e) => e[1].toUpperCase());
|
||||
}
|
||||
|
||||
// src/dom-common.js
|
||||
var d = {
|
||||
setDeleteAttr: pt,
|
||||
ssr: "",
|
||||
D: globalThis.document,
|
||||
F: globalThis.DocumentFragment,
|
||||
H: globalThis.HTMLElement,
|
||||
S: globalThis.SVGElement,
|
||||
M: globalThis.MutationObserver,
|
||||
q: (t) => t || Promise.resolve()
|
||||
};
|
||||
function pt(t, e, r) {
|
||||
if (Reflect.set(t, e, r), !!S(r)) {
|
||||
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", w = "dde:connected", y = "dde:disconnected", k = "dde:attributeChanged";
|
||||
|
||||
// src/dom.js
|
||||
function Mt(t) {
|
||||
return d.q(t);
|
||||
}
|
||||
var A = [{
|
||||
get scope() {
|
||||
return d.D.body;
|
||||
},
|
||||
host: (t) => t ? t(d.D.body) : d.D.body,
|
||||
prevent: !0
|
||||
}], x = {
|
||||
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 lt(t) {
|
||||
return t.append === Y || (t.appendOriginal = t.append, t.append = Y), t;
|
||||
}
|
||||
var $;
|
||||
function M(t, e, ...r) {
|
||||
let n = j(this), o = 0, c, u;
|
||||
switch ((Object(e) !== e || n.isSignal(e)) && (e = { textContent: e }), !0) {
|
||||
case typeof t == "function": {
|
||||
o = 1;
|
||||
let i = (...h) => h.length ? (o === 1 ? r.unshift(...h) : h.forEach((E) => E(u)), void 0) : u;
|
||||
x.push({ scope: t, host: i }), c = t(e || void 0);
|
||||
let p = c instanceof d.F;
|
||||
if (c.nodeName === "#comment") break;
|
||||
let v = M.mark({
|
||||
type: "component",
|
||||
name: t.name,
|
||||
host: p ? "this" : "parentElement"
|
||||
});
|
||||
c.prepend(v), p && (u = v);
|
||||
break;
|
||||
}
|
||||
case t === "#text":
|
||||
c = q.call(this, d.D.createTextNode(""), e);
|
||||
break;
|
||||
case (t === "<>" || !t):
|
||||
c = q.call(this, d.D.createDocumentFragment(), e);
|
||||
break;
|
||||
case !!$:
|
||||
c = q.call(this, d.D.createElementNS($, t), e);
|
||||
break;
|
||||
case !c:
|
||||
c = q.call(this, d.D.createElement(t), e);
|
||||
}
|
||||
return lt(c), u || (u = c), r.forEach((i) => i(u)), o && x.pop(), o = 2, c;
|
||||
}
|
||||
M.mark = function(t, e = !1) {
|
||||
t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
|
||||
let r = e ? "" : "/", n = d.D.createComment(`<dde:mark ${t}${d.ssr}${r}>`);
|
||||
return e && (n.end = d.D.createComment("</dde:mark>")), n;
|
||||
};
|
||||
function jt(t) {
|
||||
let e = this;
|
||||
return function(...n) {
|
||||
$ = t;
|
||||
let o = M.call(e, ...n);
|
||||
return $ = void 0, o;
|
||||
};
|
||||
}
|
||||
function Pt(t, e = t) {
|
||||
let r = "\xB9\u2070", n = "\u2713", o = Object.fromEntries(
|
||||
Array.from(e.querySelectorAll("slot")).filter((c) => !c.name.endsWith(r)).map((c) => [c.name += r, c])
|
||||
);
|
||||
if (t.append = new Proxy(t.append, {
|
||||
apply(c, u, i) {
|
||||
if (i[0] === e) return c.apply(t, i);
|
||||
for (let p of i) {
|
||||
let v = (p.slot || "") + r;
|
||||
try {
|
||||
bt(p, "remove", "slot");
|
||||
} catch {
|
||||
}
|
||||
let h = o[v];
|
||||
if (!h) return;
|
||||
h.name.startsWith(n) || (h.childNodes.forEach((E) => E.remove()), h.name = n + v), h.append(p);
|
||||
}
|
||||
return t.append = c, t;
|
||||
}
|
||||
}), t !== e) {
|
||||
let c = Array.from(t.childNodes);
|
||||
t.append(...c);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
var F = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: tt } = d;
|
||||
function q(t, ...e) {
|
||||
if (!e.length) return t;
|
||||
F.set(t, rt(t, this));
|
||||
for (let [r, n] of Object.entries(Object.assign({}, ...e)))
|
||||
nt.call(this, t, r, n);
|
||||
return F.delete(t), t;
|
||||
}
|
||||
function nt(t, e, r) {
|
||||
let { setRemoveAttr: n, s: o } = rt(t, this), c = this;
|
||||
r = o.processReactiveAttribute(
|
||||
t,
|
||||
e,
|
||||
r,
|
||||
(i, p) => nt.call(c, t, i, p)
|
||||
);
|
||||
let [u] = e;
|
||||
if (u === "=") return n(e.slice(1), r);
|
||||
if (u === ".") return et(t, e.slice(1), r);
|
||||
if (/(aria|data)([A-Z])/.test(e))
|
||||
return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), n(e, r);
|
||||
switch (e === "className" && (e = "class"), e) {
|
||||
case "xlink:href":
|
||||
return n(e, r, "http://www.w3.org/1999/xlink");
|
||||
case "textContent":
|
||||
return tt(t, e, r);
|
||||
case "style":
|
||||
if (typeof r != "object") break;
|
||||
/* falls through */
|
||||
case "dataset":
|
||||
return B(o, e, t, r, et.bind(null, t[e]));
|
||||
case "ariaset":
|
||||
return B(o, e, t, r, (i, p) => n("aria-" + i, p));
|
||||
case "classList":
|
||||
return ht.call(c, t, r);
|
||||
}
|
||||
return gt(t, e) ? tt(t, e, r) : n(e, r);
|
||||
}
|
||||
function rt(t, e) {
|
||||
if (F.has(t)) return F.get(t);
|
||||
let n = (t instanceof d.S ? mt : vt).bind(null, t, "Attribute"), o = j(e);
|
||||
return { setRemoveAttr: n, s: o };
|
||||
}
|
||||
function ht(t, e) {
|
||||
let r = j(this);
|
||||
return B(
|
||||
r,
|
||||
"classList",
|
||||
t,
|
||||
e,
|
||||
(n, o) => t.classList.toggle(n, o === -1 ? void 0 : !!o)
|
||||
), t;
|
||||
}
|
||||
function bt(t, e, r, n) {
|
||||
return t instanceof d.H ? t[e + "Attribute"](r, n) : t[e + "AttributeNS"](null, r, n);
|
||||
}
|
||||
function gt(t, e) {
|
||||
if (!(e in t)) return !1;
|
||||
let r = ot(t, e);
|
||||
return !S(r.set);
|
||||
}
|
||||
function ot(t, e) {
|
||||
if (t = Object.getPrototypeOf(t), !t) return {};
|
||||
let r = Object.getOwnPropertyDescriptor(t, e);
|
||||
return r || ot(t, e);
|
||||
}
|
||||
function B(t, e, r, n, o) {
|
||||
let c = String;
|
||||
if (!(typeof n != "object" || n === null))
|
||||
return Object.entries(n).forEach(function([i, p]) {
|
||||
i && (i = new c(i), i.target = e, p = t.processReactiveAttribute(r, i, p, o), o(i, p));
|
||||
});
|
||||
}
|
||||
function vt(t, e, r, n) {
|
||||
return t[(S(n) ? "remove" : "set") + e](r, n);
|
||||
}
|
||||
function mt(t, e, r, n, o = null) {
|
||||
return t[(S(n) ? "remove" : "set") + e + "NS"](o, r, n);
|
||||
}
|
||||
function et(t, e, r) {
|
||||
if (Reflect.set(t, e, r), !!S(r))
|
||||
return Reflect.deleteProperty(t, e);
|
||||
}
|
||||
|
||||
// src/events-observer.js
|
||||
var C = d.M ? Et() : new Proxy({}, {
|
||||
get() {
|
||||
return () => {
|
||||
};
|
||||
}
|
||||
});
|
||||
function Et() {
|
||||
let t = /* @__PURE__ */ new Map(), e = !1, r = (s) => function(f) {
|
||||
for (let a of f)
|
||||
if (a.type === "childList") {
|
||||
if (h(a.addedNodes, !0)) {
|
||||
s();
|
||||
continue;
|
||||
}
|
||||
E(a.removedNodes, !0) && s();
|
||||
}
|
||||
}, n = new d.M(r(i));
|
||||
return {
|
||||
observe(s) {
|
||||
let f = new d.M(r(() => {
|
||||
}));
|
||||
return f.observe(s, { childList: !0, subtree: !0 }), () => f.disconnect();
|
||||
},
|
||||
onConnected(s, f) {
|
||||
u();
|
||||
let a = c(s);
|
||||
a.connected.has(f) || (a.connected.add(f), a.length_c += 1);
|
||||
},
|
||||
offConnected(s, f) {
|
||||
if (!t.has(s)) return;
|
||||
let a = t.get(s);
|
||||
a.connected.has(f) && (a.connected.delete(f), a.length_c -= 1, o(s, a));
|
||||
},
|
||||
onDisconnected(s, f) {
|
||||
u();
|
||||
let a = c(s);
|
||||
a.disconnected.has(f) || (a.disconnected.add(f), a.length_d += 1);
|
||||
},
|
||||
offDisconnected(s, f) {
|
||||
if (!t.has(s)) return;
|
||||
let a = t.get(s);
|
||||
a.disconnected.has(f) && (a.disconnected.delete(f), a.length_d -= 1, o(s, a));
|
||||
}
|
||||
};
|
||||
function o(s, f) {
|
||||
f.length_c || f.length_d || (t.delete(s), i());
|
||||
}
|
||||
function c(s) {
|
||||
if (t.has(s)) return t.get(s);
|
||||
let f = {
|
||||
connected: /* @__PURE__ */ new WeakSet(),
|
||||
length_c: 0,
|
||||
disconnected: /* @__PURE__ */ new WeakSet(),
|
||||
length_d: 0
|
||||
};
|
||||
return t.set(s, f), f;
|
||||
}
|
||||
function u() {
|
||||
e || (e = !0, n.observe(d.D.body, { childList: !0, subtree: !0 }));
|
||||
}
|
||||
function i() {
|
||||
!e || t.size || (e = !1, n.disconnect());
|
||||
}
|
||||
function p() {
|
||||
return new Promise(function(s) {
|
||||
(requestIdleCallback || requestAnimationFrame)(s);
|
||||
});
|
||||
}
|
||||
async function v(s) {
|
||||
t.size > 30 && await p();
|
||||
let f = [];
|
||||
if (!(s instanceof Node)) return f;
|
||||
for (let a of t.keys())
|
||||
a === s || !(a instanceof Node) || s.contains(a) && f.push(a);
|
||||
return f;
|
||||
}
|
||||
function h(s, f) {
|
||||
let a = !1;
|
||||
for (let b of s) {
|
||||
if (f && v(b).then(h), !t.has(b)) continue;
|
||||
let N = t.get(b);
|
||||
N.length_c && (b.dispatchEvent(new Event(w)), N.connected = /* @__PURE__ */ new WeakSet(), N.length_c = 0, N.length_d || t.delete(b), a = !0);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
function E(s, f) {
|
||||
let a = !1;
|
||||
for (let b of s)
|
||||
f && v(b).then(E), !(!t.has(b) || !t.get(b).length_d) && ((globalThis.queueMicrotask || setTimeout)(L(b)), a = !0);
|
||||
return a;
|
||||
}
|
||||
function L(s) {
|
||||
return () => {
|
||||
s.isConnected || (s.dispatchEvent(new Event(y)), t.delete(s));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// src/customElement.js
|
||||
function It(t, e, r = _t) {
|
||||
let n = t.host || t;
|
||||
x.push({
|
||||
scope: n,
|
||||
host: (...u) => u.length ? u.forEach((i) => i(n)) : n
|
||||
}), typeof r == "function" && (r = r.call(n, n));
|
||||
let o = n[O];
|
||||
o || xt(n);
|
||||
let c = e.call(n, r);
|
||||
return o || n.dispatchEvent(new Event(w)), t.nodeType === 11 && typeof t.mode == "string" && n.addEventListener(y, C.observe(t), { once: !0 }), x.pop(), t.append(c);
|
||||
}
|
||||
function xt(t) {
|
||||
return J(t.prototype, "connectedCallback", function(e, r, n) {
|
||||
e.apply(r, n), r.dispatchEvent(new Event(w));
|
||||
}), J(t.prototype, "disconnectedCallback", function(e, r, n) {
|
||||
e.apply(r, n), (globalThis.queueMicrotask || setTimeout)(
|
||||
() => !r.isConnected && r.dispatchEvent(new Event(y))
|
||||
);
|
||||
}), J(t.prototype, "attributeChangedCallback", function(e, r, n) {
|
||||
let [o, , c] = n;
|
||||
r.dispatchEvent(new CustomEvent(k, {
|
||||
detail: [o, c]
|
||||
})), e.apply(r, n);
|
||||
}), t.prototype[O] = !0, t;
|
||||
}
|
||||
function J(t, e, r) {
|
||||
t[e] = new Proxy(t[e] || (() => {
|
||||
}), { apply: r });
|
||||
}
|
||||
function _t(t) {
|
||||
return W(t, (e, r) => e.getAttribute(r));
|
||||
}
|
||||
|
||||
// src/events.js
|
||||
function Gt(t, e, r) {
|
||||
return e || (e = {}), function(o, ...c) {
|
||||
r && (c.unshift(o), o = typeof r == "function" ? r() : r);
|
||||
let u = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
|
||||
return o.dispatchEvent(u);
|
||||
};
|
||||
}
|
||||
function _(t, e, r) {
|
||||
return function(o) {
|
||||
return o.addEventListener(t, e, r), o;
|
||||
};
|
||||
}
|
||||
var ct = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
|
||||
_.connected = function(t, e) {
|
||||
return e = ct(e), function(n) {
|
||||
return n.addEventListener(w, t, e), n[O] ? n : n.isConnected ? (n.dispatchEvent(new Event(w)), n) : (P(e.signal, () => C.offConnected(n, t)) && C.onConnected(n, t), n);
|
||||
};
|
||||
};
|
||||
_.disconnected = function(t, e) {
|
||||
return e = ct(e), function(n) {
|
||||
return n.addEventListener(y, t, e), n[O] || P(e.signal, () => C.offDisconnected(n, t)) && C.onDisconnected(n, t), n;
|
||||
};
|
||||
};
|
||||
var Z = /* @__PURE__ */ new WeakMap();
|
||||
_.disconnectedAsAbort = function(t) {
|
||||
if (Z.has(t)) return Z.get(t);
|
||||
let e = new AbortController();
|
||||
return Z.set(t, e), t(_.disconnected(() => e.abort())), e;
|
||||
};
|
||||
var wt = /* @__PURE__ */ new WeakSet();
|
||||
_.attributeChanged = function(t, e) {
|
||||
return typeof e != "object" && (e = {}), function(n) {
|
||||
if (n.addEventListener(k, t, e), n[O] || wt.has(n) || !d.M) return n;
|
||||
let o = new d.M(function(u) {
|
||||
for (let { attributeName: i, target: p } of u)
|
||||
p.dispatchEvent(
|
||||
new CustomEvent(k, { detail: [i, p.getAttribute(i)] })
|
||||
);
|
||||
});
|
||||
return P(e.signal, () => o.disconnect()) && o.observe(n, { attributes: !0 }), n;
|
||||
};
|
||||
};
|
||||
|
||||
// src/signals-lib.js
|
||||
var l = "__dde_signal";
|
||||
function U(t) {
|
||||
try {
|
||||
return I(t, l);
|
||||
} catch {
|
||||
return !1;
|
||||
}
|
||||
}
|
||||
var z = [], g = /* @__PURE__ */ new WeakMap();
|
||||
function m(t, e) {
|
||||
if (typeof t != "function")
|
||||
return it(!1, t, e);
|
||||
if (U(t)) return t;
|
||||
let r = it(!0), n = function() {
|
||||
let [o, ...c] = g.get(n);
|
||||
if (g.set(n, /* @__PURE__ */ new Set([o])), z.push(n), at(r, t()), z.pop(), !c.length) return;
|
||||
let u = g.get(n);
|
||||
for (let i of c)
|
||||
u.has(i) || R(i, n);
|
||||
};
|
||||
return g.set(r[l], n), g.set(n, /* @__PURE__ */ new Set([r])), n(), r;
|
||||
}
|
||||
m.action = function(t, e, ...r) {
|
||||
let n = t[l], { actions: o } = n;
|
||||
if (!o || !(e in o))
|
||||
throw new Error(`'${t}' has no action with name '${e}'!`);
|
||||
if (o[e].apply(n, r), n.skip) return delete n.skip;
|
||||
n.listeners.forEach((c) => c(n.value));
|
||||
};
|
||||
m.on = function t(e, r, n = {}) {
|
||||
let { signal: o } = n;
|
||||
if (!(o && o.aborted)) {
|
||||
if (Array.isArray(e)) return e.forEach((c) => t(c, r, n));
|
||||
Q(e, r), o && o.addEventListener("abort", () => R(e, r));
|
||||
}
|
||||
};
|
||||
m.symbols = {
|
||||
//signal: mark,
|
||||
onclear: Symbol.for("Signal.onclear")
|
||||
};
|
||||
m.clear = function(...t) {
|
||||
for (let r of t) {
|
||||
let n = r[l];
|
||||
n && (delete r.toJSON, n.onclear.forEach((o) => o.call(n)), e(r, n), delete r[l]);
|
||||
}
|
||||
function e(r, n) {
|
||||
n.listeners.forEach((o) => {
|
||||
if (n.listeners.delete(o), !g.has(o)) return;
|
||||
let c = g.get(o);
|
||||
c.delete(r), !(c.size > 1) && (r.clear(...c), g.delete(o));
|
||||
});
|
||||
}
|
||||
};
|
||||
var D = "__dde_reactive";
|
||||
m.el = function(t, e) {
|
||||
let r = M.mark({ type: "reactive" }, !0), n = r.end, o = d.D.createDocumentFragment();
|
||||
o.append(r, n);
|
||||
let { current: c } = x, u = {}, i = (p) => {
|
||||
if (!r.parentNode || !n.parentNode)
|
||||
return R(t, i);
|
||||
let v = u;
|
||||
u = {}, x.push(c);
|
||||
let h = e(p, function(f, a) {
|
||||
let b;
|
||||
return I(v, f) ? (b = v[f], delete v[f]) : b = a(), u[f] = b, b;
|
||||
});
|
||||
x.pop(), Array.isArray(h) || (h = [h]);
|
||||
let E = document.createComment("");
|
||||
h.push(E), r.after(...h);
|
||||
let L;
|
||||
for (; (L = E.nextSibling) && L !== n; )
|
||||
L.remove();
|
||||
E.remove(), r.isConnected && At(c.host());
|
||||
};
|
||||
return Q(t, i), ut(t, i, r, e), i(t()), o;
|
||||
};
|
||||
function At(t) {
|
||||
!t || !t[D] || (requestIdleCallback || setTimeout)(function() {
|
||||
t[D] = t[D].filter(([e, r]) => r.isConnected ? !0 : (R(...e), !1));
|
||||
});
|
||||
}
|
||||
var St = {
|
||||
_set(t) {
|
||||
this.value = t;
|
||||
}
|
||||
};
|
||||
function Ot(t) {
|
||||
return function(e, r) {
|
||||
let n = (...c) => c.length ? e.setAttribute(r, ...c) : K(n), o = ft(n, e.getAttribute(r), St);
|
||||
return t[r] = o, o;
|
||||
};
|
||||
}
|
||||
var G = "__dde_attributes";
|
||||
m.observedAttributes = function(t) {
|
||||
let e = t[G] = {}, r = W(t, Ot(e));
|
||||
return _.attributeChanged(function({ detail: o }) {
|
||||
/*! This maps attributes to signals (`S.observedAttributes`).
|
||||
* Investigate `__dde_attributes` key of the element.*/
|
||||
let [c, u] = o, i = this[G][c];
|
||||
if (i) return m.action(i, "_set", u);
|
||||
})(t), _.disconnected(function() {
|
||||
/*! This removes all signals mapped to attributes (`S.observedAttributes`).
|
||||
* Investigate `__dde_attributes` key of the element.*/
|
||||
m.clear(...Object.values(this[G]));
|
||||
})(t), r;
|
||||
};
|
||||
var st = {
|
||||
isSignal: U,
|
||||
processReactiveAttribute(t, e, r, n) {
|
||||
if (!U(r)) return r;
|
||||
let o = (c) => {
|
||||
if (!t.isConnected)
|
||||
return R(r, o);
|
||||
n(e, c);
|
||||
};
|
||||
return Q(r, o), ut(r, o, t, e), r();
|
||||
}
|
||||
};
|
||||
function ut(t, e, ...r) {
|
||||
let { current: n } = x;
|
||||
n.host(function(o) {
|
||||
if (o[D])
|
||||
return o[D].push([[t, e], ...r]);
|
||||
o[D] = [], !n.prevent && _.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[D].forEach(([[c, u]]) => R(c, u, c[l] && c[l].host && c[l].host() === o))
|
||||
)
|
||||
)(o);
|
||||
});
|
||||
}
|
||||
function it(t, e, r) {
|
||||
let n = t ? () => K(n) : (...o) => o.length ? at(n, ...o) : K(n);
|
||||
return ft(n, e, r, t);
|
||||
}
|
||||
var yt = Object.assign(/* @__PURE__ */ Object.create(null), {
|
||||
stopPropagation() {
|
||||
this.skip = !0;
|
||||
}
|
||||
}), V = class extends Error {
|
||||
constructor() {
|
||||
super();
|
||||
let [e, ...r] = this.stack.split(`
|
||||
`), n = e.slice(e.indexOf("@"), e.indexOf(".js:") + 4);
|
||||
this.stack = r.find((o) => !o.includes(n));
|
||||
}
|
||||
};
|
||||
function ft(t, e, r, n = !1) {
|
||||
let o = [];
|
||||
X(r) !== "[object Object]" && (r = {});
|
||||
let { onclear: c } = m.symbols;
|
||||
r[c] && (o.push(r[c]), delete r[c]);
|
||||
let { host: u } = x;
|
||||
return Reflect.defineProperty(t, l, {
|
||||
value: {
|
||||
value: e,
|
||||
actions: r,
|
||||
onclear: o,
|
||||
host: u,
|
||||
listeners: /* @__PURE__ */ new Set(),
|
||||
defined: new V().stack,
|
||||
readonly: n
|
||||
},
|
||||
enumerable: !1,
|
||||
writable: !1,
|
||||
configurable: !0
|
||||
}), t.toJSON = () => t(), t.valueOf = () => t[l] && t[l].value, Object.setPrototypeOf(t[l], yt), t;
|
||||
}
|
||||
function Ct() {
|
||||
return z[z.length - 1];
|
||||
}
|
||||
function K(t) {
|
||||
if (!t[l]) return;
|
||||
let { value: e, listeners: r } = t[l], n = Ct();
|
||||
return n && r.add(n), g.has(n) && g.get(n).add(t), e;
|
||||
}
|
||||
function at(t, e, r) {
|
||||
if (!t[l]) return;
|
||||
let n = t[l];
|
||||
if (!(!r && n.value === e))
|
||||
return n.value = e, n.listeners.forEach((o) => o(e)), e;
|
||||
}
|
||||
function Q(t, e) {
|
||||
if (t[l])
|
||||
return t[l].listeners.add(e);
|
||||
}
|
||||
function R(t, e, r) {
|
||||
let n = t[l];
|
||||
if (!n) return;
|
||||
let o = n.listeners.delete(e);
|
||||
if (r && !n.listeners.size) {
|
||||
if (m.clear(t), !g.has(n)) return o;
|
||||
let c = g.get(n);
|
||||
if (!g.has(c)) return o;
|
||||
g.get(c).forEach((u) => R(u, c, !0));
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
// signals.js
|
||||
H(st);
|
||||
|
||||
globalThis.dde= {
|
||||
S: m,
|
||||
assign: q,
|
||||
assignAttribute: nt,
|
||||
chainableAppend: lt,
|
||||
classListDeclarative: ht,
|
||||
createElement: M,
|
||||
createElementNS: jt,
|
||||
customElementRender: It,
|
||||
customElementWithDDE: xt,
|
||||
dispatchEvent: Gt,
|
||||
el: M,
|
||||
elNS: jt,
|
||||
elementAttribute: bt,
|
||||
isSignal: U,
|
||||
lifecyclesToEvents: xt,
|
||||
observedAttributes: _t,
|
||||
on: _,
|
||||
queue: Mt,
|
||||
registerReactivity: H,
|
||||
scope: x,
|
||||
signal: m,
|
||||
simulateSlots: Pt
|
||||
};
|
||||
|
||||
})();
|
456
dist/dde.js
vendored
456
dist/dde.js
vendored
@ -1,456 +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, r, n) {
|
||||
return r;
|
||||
}
|
||||
};
|
||||
function Z(t, e = !0) {
|
||||
return e ? Object.assign(C, t) : (Object.setPrototypeOf(t, C), t);
|
||||
}
|
||||
function S(t) {
|
||||
return C.isPrototypeOf(t) && t !== C ? t : C;
|
||||
}
|
||||
|
||||
// src/helpers.js
|
||||
function m(t) {
|
||||
return typeof t > "u";
|
||||
}
|
||||
function L(t, e) {
|
||||
if (!t || !(t instanceof AbortSignal))
|
||||
return !0;
|
||||
if (!t.aborted)
|
||||
return t.addEventListener("abort", e), function() {
|
||||
t.removeEventListener("abort", e);
|
||||
};
|
||||
}
|
||||
function q(t, e) {
|
||||
let { observedAttributes: r = [] } = t.constructor;
|
||||
return r.reduce(function(n, o) {
|
||||
return n[G(o)] = e(t, o), n;
|
||||
}, {});
|
||||
}
|
||||
function G(t) {
|
||||
return t.replace(/-./g, (e) => e[1].toUpperCase());
|
||||
}
|
||||
|
||||
// src/dom-common.js
|
||||
var a = {
|
||||
setDeleteAttr: V,
|
||||
ssr: "",
|
||||
D: globalThis.document,
|
||||
F: globalThis.DocumentFragment,
|
||||
H: globalThis.HTMLElement,
|
||||
S: globalThis.SVGElement,
|
||||
M: globalThis.MutationObserver,
|
||||
q: (t) => t || Promise.resolve()
|
||||
};
|
||||
function V(t, e, r) {
|
||||
if (Reflect.set(t, e, r), !!m(r)) {
|
||||
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", v = "dde:connected", w = "dde:disconnected", y = "dde:attributeChanged";
|
||||
|
||||
// src/dom.js
|
||||
function dt(t) {
|
||||
return a.q(t);
|
||||
}
|
||||
var g = [{
|
||||
get scope() {
|
||||
return a.D.body;
|
||||
},
|
||||
host: (t) => t ? t(a.D.body) : a.D.body,
|
||||
prevent: !0
|
||||
}], O = {
|
||||
get current() {
|
||||
return g[g.length - 1];
|
||||
},
|
||||
get host() {
|
||||
return this.current.host;
|
||||
},
|
||||
preventDefault() {
|
||||
let { current: t } = this;
|
||||
return t.prevent = !0, t;
|
||||
},
|
||||
get state() {
|
||||
return [...g];
|
||||
},
|
||||
push(t = {}) {
|
||||
return g.push(Object.assign({}, this.current, { prevent: !1 }, t));
|
||||
},
|
||||
pushRoot() {
|
||||
return g.push(g[0]);
|
||||
},
|
||||
pop() {
|
||||
if (g.length !== 1)
|
||||
return g.pop();
|
||||
}
|
||||
};
|
||||
function k(...t) {
|
||||
return this.appendOriginal(...t), this;
|
||||
}
|
||||
function J(t) {
|
||||
return t.append === k || (t.appendOriginal = t.append, t.append = k), t;
|
||||
}
|
||||
var T;
|
||||
function P(t, e, ...r) {
|
||||
let n = S(this), o = 0, c, d;
|
||||
switch ((Object(e) !== e || n.isSignal(e)) && (e = { textContent: e }), !0) {
|
||||
case typeof t == "function": {
|
||||
o = 1;
|
||||
let f = (...l) => l.length ? (o === 1 ? r.unshift(...l) : l.forEach((E) => E(d)), void 0) : d;
|
||||
O.push({ scope: t, host: f }), c = t(e || void 0);
|
||||
let p = c instanceof a.F;
|
||||
if (c.nodeName === "#comment") break;
|
||||
let b = P.mark({
|
||||
type: "component",
|
||||
name: t.name,
|
||||
host: p ? "this" : "parentElement"
|
||||
});
|
||||
c.prepend(b), p && (d = b);
|
||||
break;
|
||||
}
|
||||
case t === "#text":
|
||||
c = R.call(this, a.D.createTextNode(""), e);
|
||||
break;
|
||||
case (t === "<>" || !t):
|
||||
c = R.call(this, a.D.createDocumentFragment(), e);
|
||||
break;
|
||||
case !!T:
|
||||
c = R.call(this, a.D.createElementNS(T, t), e);
|
||||
break;
|
||||
case !c:
|
||||
c = R.call(this, a.D.createElement(t), e);
|
||||
}
|
||||
return J(c), d || (d = c), r.forEach((f) => f(d)), o && O.pop(), o = 2, c;
|
||||
}
|
||||
P.mark = function(t, e = !1) {
|
||||
t = Object.entries(t).map(([o, c]) => o + `="${c}"`).join(" ");
|
||||
let r = e ? "" : "/", n = a.D.createComment(`<dde:mark ${t}${a.ssr}${r}>`);
|
||||
return e && (n.end = a.D.createComment("</dde:mark>")), n;
|
||||
};
|
||||
function pt(t) {
|
||||
let e = this;
|
||||
return function(...n) {
|
||||
T = t;
|
||||
let o = P.call(e, ...n);
|
||||
return T = void 0, o;
|
||||
};
|
||||
}
|
||||
function lt(t, e = t) {
|
||||
let r = "\xB9\u2070", n = "\u2713", o = Object.fromEntries(
|
||||
Array.from(e.querySelectorAll("slot")).filter((c) => !c.name.endsWith(r)).map((c) => [c.name += r, c])
|
||||
);
|
||||
if (t.append = new Proxy(t.append, {
|
||||
apply(c, d, f) {
|
||||
if (f[0] === e) return c.apply(t, f);
|
||||
for (let p of f) {
|
||||
let b = (p.slot || "") + r;
|
||||
try {
|
||||
Q(p, "remove", "slot");
|
||||
} catch {
|
||||
}
|
||||
let l = o[b];
|
||||
if (!l) return;
|
||||
l.name.startsWith(n) || (l.childNodes.forEach((E) => E.remove()), l.name = n + b), l.append(p);
|
||||
}
|
||||
return t.append = c, t;
|
||||
}
|
||||
}), t !== e) {
|
||||
let c = Array.from(t.childNodes);
|
||||
t.append(...c);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
var N = /* @__PURE__ */ new WeakMap(), { setDeleteAttr: $ } = a;
|
||||
function R(t, ...e) {
|
||||
if (!e.length) return t;
|
||||
N.set(t, H(t, this));
|
||||
for (let [r, n] of Object.entries(Object.assign({}, ...e)))
|
||||
U.call(this, t, r, n);
|
||||
return N.delete(t), t;
|
||||
}
|
||||
function U(t, e, r) {
|
||||
let { setRemoveAttr: n, s: o } = H(t, this), c = this;
|
||||
r = o.processReactiveAttribute(
|
||||
t,
|
||||
e,
|
||||
r,
|
||||
(f, p) => U.call(c, t, f, p)
|
||||
);
|
||||
let [d] = e;
|
||||
if (d === "=") return n(e.slice(1), r);
|
||||
if (d === ".") return F(t, e.slice(1), r);
|
||||
if (/(aria|data)([A-Z])/.test(e))
|
||||
return e = e.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase(), n(e, r);
|
||||
switch (e === "className" && (e = "class"), e) {
|
||||
case "xlink:href":
|
||||
return n(e, r, "http://www.w3.org/1999/xlink");
|
||||
case "textContent":
|
||||
return $(t, e, r);
|
||||
case "style":
|
||||
if (typeof r != "object") break;
|
||||
/* falls through */
|
||||
case "dataset":
|
||||
return M(o, e, t, r, F.bind(null, t[e]));
|
||||
case "ariaset":
|
||||
return M(o, e, t, r, (f, p) => n("aria-" + f, p));
|
||||
case "classList":
|
||||
return K.call(c, t, r);
|
||||
}
|
||||
return X(t, e) ? $(t, e, r) : n(e, r);
|
||||
}
|
||||
function H(t, e) {
|
||||
if (N.has(t)) return N.get(t);
|
||||
let n = (t instanceof a.S ? tt : Y).bind(null, t, "Attribute"), o = S(e);
|
||||
return { setRemoveAttr: n, s: o };
|
||||
}
|
||||
function K(t, e) {
|
||||
let r = S(this);
|
||||
return M(
|
||||
r,
|
||||
"classList",
|
||||
t,
|
||||
e,
|
||||
(n, o) => t.classList.toggle(n, o === -1 ? void 0 : !!o)
|
||||
), t;
|
||||
}
|
||||
function Q(t, e, r, n) {
|
||||
return t instanceof a.H ? t[e + "Attribute"](r, n) : t[e + "AttributeNS"](null, r, n);
|
||||
}
|
||||
function X(t, e) {
|
||||
if (!(e in t)) return !1;
|
||||
let r = z(t, e);
|
||||
return !m(r.set);
|
||||
}
|
||||
function z(t, e) {
|
||||
if (t = Object.getPrototypeOf(t), !t) return {};
|
||||
let r = Object.getOwnPropertyDescriptor(t, e);
|
||||
return r || z(t, e);
|
||||
}
|
||||
function M(t, e, r, n, o) {
|
||||
let c = String;
|
||||
if (!(typeof n != "object" || n === null))
|
||||
return Object.entries(n).forEach(function([f, p]) {
|
||||
f && (f = new c(f), f.target = e, p = t.processReactiveAttribute(r, f, p, o), o(f, p));
|
||||
});
|
||||
}
|
||||
function Y(t, e, r, n) {
|
||||
return t[(m(n) ? "remove" : "set") + e](r, n);
|
||||
}
|
||||
function tt(t, e, r, n, o = null) {
|
||||
return t[(m(n) ? "remove" : "set") + e + "NS"](o, r, n);
|
||||
}
|
||||
function F(t, e, r) {
|
||||
if (Reflect.set(t, e, r), !!m(r))
|
||||
return Reflect.deleteProperty(t, e);
|
||||
}
|
||||
|
||||
// src/events-observer.js
|
||||
var _ = a.M ? et() : new Proxy({}, {
|
||||
get() {
|
||||
return () => {
|
||||
};
|
||||
}
|
||||
});
|
||||
function et() {
|
||||
let t = /* @__PURE__ */ new Map(), e = !1, r = (s) => function(u) {
|
||||
for (let i of u)
|
||||
if (i.type === "childList") {
|
||||
if (l(i.addedNodes, !0)) {
|
||||
s();
|
||||
continue;
|
||||
}
|
||||
E(i.removedNodes, !0) && s();
|
||||
}
|
||||
}, n = new a.M(r(f));
|
||||
return {
|
||||
observe(s) {
|
||||
let u = new a.M(r(() => {
|
||||
}));
|
||||
return u.observe(s, { childList: !0, subtree: !0 }), () => u.disconnect();
|
||||
},
|
||||
onConnected(s, u) {
|
||||
d();
|
||||
let i = c(s);
|
||||
i.connected.has(u) || (i.connected.add(u), i.length_c += 1);
|
||||
},
|
||||
offConnected(s, u) {
|
||||
if (!t.has(s)) return;
|
||||
let i = t.get(s);
|
||||
i.connected.has(u) && (i.connected.delete(u), i.length_c -= 1, o(s, i));
|
||||
},
|
||||
onDisconnected(s, u) {
|
||||
d();
|
||||
let i = c(s);
|
||||
i.disconnected.has(u) || (i.disconnected.add(u), i.length_d += 1);
|
||||
},
|
||||
offDisconnected(s, u) {
|
||||
if (!t.has(s)) return;
|
||||
let i = t.get(s);
|
||||
i.disconnected.has(u) && (i.disconnected.delete(u), i.length_d -= 1, o(s, i));
|
||||
}
|
||||
};
|
||||
function o(s, u) {
|
||||
u.length_c || u.length_d || (t.delete(s), f());
|
||||
}
|
||||
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 d() {
|
||||
e || (e = !0, n.observe(a.D.body, { childList: !0, subtree: !0 }));
|
||||
}
|
||||
function f() {
|
||||
!e || t.size || (e = !1, n.disconnect());
|
||||
}
|
||||
function p() {
|
||||
return new Promise(function(s) {
|
||||
(requestIdleCallback || requestAnimationFrame)(s);
|
||||
});
|
||||
}
|
||||
async function b(s) {
|
||||
t.size > 30 && await p();
|
||||
let u = [];
|
||||
if (!(s instanceof Node)) return u;
|
||||
for (let i of t.keys())
|
||||
i === s || !(i instanceof Node) || s.contains(i) && u.push(i);
|
||||
return u;
|
||||
}
|
||||
function l(s, u) {
|
||||
let i = !1;
|
||||
for (let h of s) {
|
||||
if (u && b(h).then(l), !t.has(h)) continue;
|
||||
let A = t.get(h);
|
||||
A.length_c && (h.dispatchEvent(new Event(v)), A.connected = /* @__PURE__ */ new WeakSet(), A.length_c = 0, A.length_d || t.delete(h), i = !0);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
function E(s, u) {
|
||||
let i = !1;
|
||||
for (let h of s)
|
||||
u && b(h).then(E), !(!t.has(h) || !t.get(h).length_d) && ((globalThis.queueMicrotask || setTimeout)(I(h)), i = !0);
|
||||
return i;
|
||||
}
|
||||
function I(s) {
|
||||
return () => {
|
||||
s.isConnected || (s.dispatchEvent(new Event(w)), t.delete(s));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// src/customElement.js
|
||||
function wt(t, e, r = rt) {
|
||||
let n = t.host || t;
|
||||
O.push({
|
||||
scope: n,
|
||||
host: (...d) => d.length ? d.forEach((f) => f(n)) : n
|
||||
}), typeof r == "function" && (r = r.call(n, n));
|
||||
let o = n[x];
|
||||
o || nt(n);
|
||||
let c = e.call(n, r);
|
||||
return o || n.dispatchEvent(new Event(v)), t.nodeType === 11 && typeof t.mode == "string" && n.addEventListener(w, _.observe(t), { once: !0 }), O.pop(), t.append(c);
|
||||
}
|
||||
function nt(t) {
|
||||
return j(t.prototype, "connectedCallback", function(e, r, n) {
|
||||
e.apply(r, n), r.dispatchEvent(new Event(v));
|
||||
}), j(t.prototype, "disconnectedCallback", function(e, r, n) {
|
||||
e.apply(r, n), (globalThis.queueMicrotask || setTimeout)(
|
||||
() => !r.isConnected && r.dispatchEvent(new Event(w))
|
||||
);
|
||||
}), j(t.prototype, "attributeChangedCallback", function(e, r, n) {
|
||||
let [o, , c] = n;
|
||||
r.dispatchEvent(new CustomEvent(y, {
|
||||
detail: [o, c]
|
||||
})), e.apply(r, n);
|
||||
}), t.prototype[x] = !0, t;
|
||||
}
|
||||
function j(t, e, r) {
|
||||
t[e] = new Proxy(t[e] || (() => {
|
||||
}), { apply: r });
|
||||
}
|
||||
function rt(t) {
|
||||
return q(t, (e, r) => e.getAttribute(r));
|
||||
}
|
||||
|
||||
// src/events.js
|
||||
function yt(t, e, r) {
|
||||
return e || (e = {}), function(o, ...c) {
|
||||
r && (c.unshift(o), o = typeof r == "function" ? r() : r);
|
||||
let d = c.length ? new CustomEvent(t, Object.assign({ detail: c[0] }, e)) : new Event(t, e);
|
||||
return o.dispatchEvent(d);
|
||||
};
|
||||
}
|
||||
function D(t, e, r) {
|
||||
return function(o) {
|
||||
return o.addEventListener(t, e, r), o;
|
||||
};
|
||||
}
|
||||
var B = (t) => Object.assign({}, typeof t == "object" ? t : null, { once: !0 });
|
||||
D.connected = function(t, e) {
|
||||
return e = B(e), function(n) {
|
||||
return n.addEventListener(v, t, e), n[x] ? n : n.isConnected ? (n.dispatchEvent(new Event(v)), n) : (L(e.signal, () => _.offConnected(n, t)) && _.onConnected(n, t), n);
|
||||
};
|
||||
};
|
||||
D.disconnected = function(t, e) {
|
||||
return e = B(e), function(n) {
|
||||
return n.addEventListener(w, t, e), n[x] || L(e.signal, () => _.offDisconnected(n, t)) && _.onDisconnected(n, t), n;
|
||||
};
|
||||
};
|
||||
var W = /* @__PURE__ */ new WeakMap();
|
||||
D.disconnectedAsAbort = function(t) {
|
||||
if (W.has(t)) return W.get(t);
|
||||
let e = new AbortController();
|
||||
return W.set(t, e), t(D.disconnected(() => e.abort())), e;
|
||||
};
|
||||
var ot = /* @__PURE__ */ new WeakSet();
|
||||
D.attributeChanged = function(t, e) {
|
||||
return typeof e != "object" && (e = {}), function(n) {
|
||||
if (n.addEventListener(y, t, e), n[x] || ot.has(n) || !a.M) return n;
|
||||
let o = new a.M(function(d) {
|
||||
for (let { attributeName: f, target: p } of d)
|
||||
p.dispatchEvent(
|
||||
new CustomEvent(y, { detail: [f, p.getAttribute(f)] })
|
||||
);
|
||||
});
|
||||
return L(e.signal, () => o.disconnect()) && o.observe(n, { attributes: !0 }), n;
|
||||
};
|
||||
};
|
||||
|
||||
globalThis.dde= {
|
||||
assign: R,
|
||||
assignAttribute: U,
|
||||
chainableAppend: J,
|
||||
classListDeclarative: K,
|
||||
createElement: P,
|
||||
createElementNS: pt,
|
||||
customElementRender: wt,
|
||||
customElementWithDDE: nt,
|
||||
dispatchEvent: yt,
|
||||
el: P,
|
||||
elNS: pt,
|
||||
elementAttribute: Q,
|
||||
lifecyclesToEvents: nt,
|
||||
observedAttributes: rt,
|
||||
on: D,
|
||||
queue: dt,
|
||||
registerReactivity: Z,
|
||||
scope: O,
|
||||
simulateSlots: lt
|
||||
};
|
||||
|
||||
})();
|
1049
dist/esm-with-signals.d.ts
vendored
1049
dist/esm-with-signals.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1478
dist/esm-with-signals.js
vendored
1478
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
860
dist/esm-with-signals.min.d.ts
vendored
Normal 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
3
dist/esm-with-signals.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
984
dist/esm.d.ts
vendored
984
dist/esm.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1024
dist/esm.js
vendored
1024
dist/esm.js
vendored
File diff suppressed because it is too large
Load Diff
859
dist/esm.min.d.ts
vendored
Normal file
859
dist/esm.min.d.ts
vendored
Normal 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
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
862
dist/iife-with-signals.d.ts
vendored
Normal file
@ -0,0 +1,862 @@
|
||||
// Generated by dts-bundle-generator v9.5.1
|
||||
|
||||
export interface Signal<V, A> {
|
||||
/** The current value of the signal */
|
||||
get(): V;
|
||||
/** Set new value of the signal */
|
||||
set(value: V, force?: boolean): V;
|
||||
toJSON(): V;
|
||||
valueOf(): V;
|
||||
}
|
||||
export type Action<V> = (this: {
|
||||
value: V;
|
||||
stopPropagation(): void;
|
||||
}, ...a: any[]) => typeof signal._ | void;
|
||||
//type SymbolSignal= Symbol;
|
||||
export type SymbolOnclear = symbol;
|
||||
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
|
||||
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
|
||||
first_time?: boolean;
|
||||
};
|
||||
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
|
||||
export interface signal {
|
||||
_: Symbol;
|
||||
/**
|
||||
* Computations signal. This creates a signal which is computed from other signals.
|
||||
* */
|
||||
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
|
||||
/**
|
||||
* Simple example:
|
||||
* ```js
|
||||
* const hello= S("Hello Signal");
|
||||
* ```
|
||||
* …simple todo signal:
|
||||
* ```js
|
||||
* const todos= S([], {
|
||||
* add(v){ this.value.push(S(v)); },
|
||||
* remove(i){ this.value.splice(i, 1); },
|
||||
* [S.symbols.onclear](){ S.clear(...this.value); },
|
||||
* });
|
||||
* ```
|
||||
* …computed signal:
|
||||
* ```js
|
||||
* const name= S("Jan");
|
||||
* const surname= S("Andrle");
|
||||
* const fullname= S(()=> name.get()+" "+surname.get());
|
||||
* ```
|
||||
* @param value Initial signal value. Or function computing value from other signals.
|
||||
* @param actions Use to define actions on the signal. Such as add item to the array.
|
||||
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
|
||||
* by `S.clear`.
|
||||
* */
|
||||
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
|
||||
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
|
||||
clear(...signals: Signal<any, any>[]): void;
|
||||
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
|
||||
symbols: {
|
||||
//signal: SymbolSignal;
|
||||
onclear: SymbolOnclear;
|
||||
};
|
||||
/**
|
||||
* Reactive element, which is rendered based on the given signal.
|
||||
* ```js
|
||||
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
|
||||
* S.el(listS, list=> list.map(li=> el("li", li)));
|
||||
* ```
|
||||
* */
|
||||
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
|
||||
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
|
||||
}
|
||||
export const signal: signal;
|
||||
export const S: signal;
|
||||
declare global {
|
||||
type ddeSignal<T, A = {}> = Signal<T, A>;
|
||||
type ddeAction<V> = Action<V>;
|
||||
type ddeActions<V> = Actions<V>;
|
||||
}
|
||||
export type CustomElementTagNameMap = {
|
||||
"#text": Text;
|
||||
"#comment": Comment;
|
||||
};
|
||||
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
|
||||
declare global {
|
||||
type ddeComponentAttributes = Record<any, any> | undefined;
|
||||
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
|
||||
type ddeString = string | Signal<string, {}>;
|
||||
type ddeStringable = ddeString | number | Signal<number, {}>;
|
||||
}
|
||||
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
|
||||
export type PascalCase = `${Capitalize<string>}${string}`;
|
||||
export type AttrsModified = {
|
||||
/**
|
||||
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
|
||||
*/
|
||||
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
|
||||
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
|
||||
}>;
|
||||
/**
|
||||
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
|
||||
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
|
||||
* for others.
|
||||
*/
|
||||
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
|
||||
/**
|
||||
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
|
||||
* Values are converted to string (see {@link DOMStringMap}).
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
|
||||
* */
|
||||
dataset: Record<string, ddeStringable>;
|
||||
/**
|
||||
* Sets `aria-*` simiraly to `dataset`
|
||||
* */
|
||||
ariaset: Record<string, ddeString>;
|
||||
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
|
||||
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
|
||||
export type IsReadonly<T, K extends keyof T> = T extends {
|
||||
readonly [P in K]: T[K];
|
||||
} ? true : false;
|
||||
/**
|
||||
* Just element attributtes
|
||||
*
|
||||
* In most cases, you can use native propertie such as
|
||||
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
|
||||
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
|
||||
*
|
||||
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
|
||||
* @private
|
||||
*/
|
||||
export type ElementAttributes<T extends SupportedElement> = Partial<{
|
||||
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
|
||||
} & AttrsModified> & Record<string, any>;
|
||||
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
|
||||
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
|
||||
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
|
||||
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
|
||||
export namespace el {
|
||||
/**
|
||||
* Creates a marker comment for elements
|
||||
*
|
||||
* @param attrs - Marker attributes
|
||||
* @param [is_open=false] - Whether the marker is open-ended
|
||||
* @returns Comment node marker
|
||||
*/
|
||||
export function mark(attrs: {
|
||||
type: "component" | "reactive" | "later";
|
||||
name?: string;
|
||||
host?: "this" | "parentElement";
|
||||
}, is_open?: boolean): Comment;
|
||||
}
|
||||
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
|
||||
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
|
||||
export function el<A extends {
|
||||
textContent: ddeStringable;
|
||||
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
|
||||
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
|
||||
export function el(tag_name?: "<>"): ddeDocumentFragment;
|
||||
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
|
||||
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
|
||||
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
|
||||
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
|
||||
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
|
||||
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
|
||||
/** Simulate slots for ddeComponents */
|
||||
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
|
||||
/**
|
||||
* Simulate slots in Custom Elements without using `shadowRoot`.
|
||||
* @param el Custom Element root element
|
||||
* @param body Body of the custom element
|
||||
* */
|
||||
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
|
||||
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
|
||||
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
|
||||
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
|
||||
export interface On {
|
||||
/** Listens to the DOM event. See {@link Document.addEventListener} */
|
||||
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
|
||||
target: EL;
|
||||
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
|
||||
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
|
||||
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
|
||||
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
|
||||
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
|
||||
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
|
||||
/**
|
||||
* Fires after the next tick of the Javascript event loop.
|
||||
* This is handy for example to apply some property depending on the element content:
|
||||
* ```js
|
||||
* const selected= "Z";
|
||||
* //...
|
||||
* return el("form").append(
|
||||
* el("select", null, on.defer(e=> e.value=selected)).append(
|
||||
* el("option", { value: "A", textContent: "A" }),
|
||||
* //...
|
||||
* el("option", { value: "Z", textContent: "Z" }),
|
||||
* ),
|
||||
* );
|
||||
* ```
|
||||
* */
|
||||
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
|
||||
}
|
||||
export const on: On;
|
||||
export type Scope = {
|
||||
scope: Node | Function | Object;
|
||||
host: Host<SupportedElement>;
|
||||
custom_element: false | HTMLElement;
|
||||
prevent: boolean;
|
||||
};
|
||||
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
|
||||
export const scope: {
|
||||
current: Scope;
|
||||
/** Stops all automatizations. E. g. signals used as attributes in current scope
|
||||
* registers removing these listeners (and clean signal if no other listeners are detected)
|
||||
* on `disconnected` event. */
|
||||
preventDefault<T extends boolean>(prevent: T): T;
|
||||
/**
|
||||
* This represents reference to the current host element — `scope.host()`.
|
||||
* It can be also used to register Addon(s) (functions to be called when component is initized)
|
||||
* — `scope.host(on.connected(console.log))`.
|
||||
* */
|
||||
host: Host<SupportedElement>;
|
||||
/**
|
||||
* Creates/gets an AbortController that triggers when the element disconnects
|
||||
* */
|
||||
signal: AbortSignal;
|
||||
state: Scope[];
|
||||
/** Adds new child scope. All attributes are inherited by default. */
|
||||
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
|
||||
/** Adds root scope as a child of the current scope. */
|
||||
pushRoot(): ReturnType<Array<Scope>["push"]>;
|
||||
/** Removes last/current child scope. */
|
||||
pop(): ReturnType<Array<Scope>["pop"]>;
|
||||
};
|
||||
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
|
||||
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
|
||||
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
|
||||
/**
|
||||
* This is used primarly for server side rendering. To be sure that all async operations
|
||||
* are finished before the page is sent to the client.
|
||||
* ```
|
||||
* // on component
|
||||
* function component(){
|
||||
* …
|
||||
* queue(fetch(...).then(...));
|
||||
* }
|
||||
*
|
||||
* // building the page
|
||||
* async function build(){
|
||||
* const { component }= await import("./component.js");
|
||||
* document.body.append(el(component));
|
||||
* await queue();
|
||||
* retutn document.body.innerHTML;
|
||||
* }
|
||||
* ```
|
||||
* */
|
||||
export function queue(promise?: Promise<unknown>): Promise<unknown>;
|
||||
/**
|
||||
* Memoization utility for caching DOM elements to improve performance.
|
||||
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
|
||||
*
|
||||
* @param key - Unique identifier for the element (usually an ID or unique value)
|
||||
* @param generator - Function that creates the element
|
||||
* @returns The cached element if the key exists, otherwise the result of the generator function
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Within S.el for list rendering
|
||||
* S.el(itemsSignal, (items, memo) =>
|
||||
* el("ul").append(
|
||||
* ...items.map(item =>
|
||||
* memo(item.id, () => el(ItemComponent, item))
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
|
||||
/**
|
||||
* Memo namespace containing utility functions for memoization.
|
||||
*/
|
||||
export namespace memo {
|
||||
/**
|
||||
* Checks if an object is a memo scope.
|
||||
* @param obj - The object to check
|
||||
* @returns True if the object is a memo scope
|
||||
*/
|
||||
export function isScope(obj: any): boolean;
|
||||
/**
|
||||
* Creates a memoized function with optional cleanup support.
|
||||
*
|
||||
* @param fun - The function to memoize
|
||||
* @param options - Configuration options
|
||||
* @param options.signal - AbortSignal for cleanup
|
||||
* @param options.onlyLast - When true, only keeps the cache from the most recent call
|
||||
* @returns A memoized version of the function with a .clear() method
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const renderItems = memo.scope(function(items) {
|
||||
* return items.map(item =>
|
||||
* memo(item.id, () => el("div", item.name))
|
||||
* );
|
||||
* }, {
|
||||
* signal: controller.signal,
|
||||
* onlyLast: true
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function scope<F extends Function>(fun: F, options?: {
|
||||
signal?: AbortSignal;
|
||||
onlyLast?: boolean;
|
||||
}): F & {
|
||||
clear: () => void;
|
||||
};
|
||||
}
|
||||
/* TypeScript MEH */
|
||||
declare global {
|
||||
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
|
||||
interface ddeDocumentFragment extends DocumentFragment {
|
||||
append: ddeAppend<ddeDocumentFragment>;
|
||||
}
|
||||
interface ddeHTMLElement extends HTMLElement {
|
||||
append: ddeAppend<ddeHTMLElement>;
|
||||
}
|
||||
interface ddeSVGElement extends SVGElement {
|
||||
append: ddeAppend<ddeSVGElement>;
|
||||
}
|
||||
interface ddeMathMLElement extends MathMLElement {
|
||||
append: ddeAppend<ddeMathMLElement>;
|
||||
}
|
||||
interface ddeHTMLElementTagNameMap {
|
||||
"a": ddeHTMLAnchorElement;
|
||||
"area": ddeHTMLAreaElement;
|
||||
"audio": ddeHTMLAudioElement;
|
||||
"base": ddeHTMLBaseElement;
|
||||
"blockquote": ddeHTMLQuoteElement;
|
||||
"body": ddeHTMLBodyElement;
|
||||
"br": ddeHTMLBRElement;
|
||||
"button": ddeHTMLButtonElement;
|
||||
"canvas": ddeHTMLCanvasElement;
|
||||
"caption": ddeHTMLTableCaptionElement;
|
||||
"col": ddeHTMLTableColElement;
|
||||
"colgroup": ddeHTMLTableColElement;
|
||||
"data": ddeHTMLDataElement;
|
||||
"datalist": ddeHTMLDataListElement;
|
||||
"del": ddeHTMLModElement;
|
||||
"details": ddeHTMLDetailsElement;
|
||||
"dialog": ddeHTMLDialogElement;
|
||||
"div": ddeHTMLDivElement;
|
||||
"dl": ddeHTMLDListElement;
|
||||
"embed": ddeHTMLEmbedElement;
|
||||
"fieldset": ddeHTMLFieldSetElement;
|
||||
"form": ddeHTMLFormElement;
|
||||
"h1": ddeHTMLHeadingElement;
|
||||
"h2": ddeHTMLHeadingElement;
|
||||
"h3": ddeHTMLHeadingElement;
|
||||
"h4": ddeHTMLHeadingElement;
|
||||
"h5": ddeHTMLHeadingElement;
|
||||
"h6": ddeHTMLHeadingElement;
|
||||
"head": ddeHTMLHeadElement;
|
||||
"hr": ddeHTMLHRElement;
|
||||
"html": ddeHTMLHtmlElement;
|
||||
"iframe": ddeHTMLIFrameElement;
|
||||
"img": ddeHTMLImageElement;
|
||||
"input": ddeHTMLInputElement;
|
||||
"ins": ddeHTMLModElement;
|
||||
"label": ddeHTMLLabelElement;
|
||||
"legend": ddeHTMLLegendElement;
|
||||
"li": ddeHTMLLIElement;
|
||||
"link": ddeHTMLLinkElement;
|
||||
"map": ddeHTMLMapElement;
|
||||
"menu": ddeHTMLMenuElement;
|
||||
"meta": ddeHTMLMetaElement;
|
||||
"meter": ddeHTMLMeterElement;
|
||||
"object": ddeHTMLObjectElement;
|
||||
"ol": ddeHTMLOListElement;
|
||||
"optgroup": ddeHTMLOptGroupElement;
|
||||
"option": ddeHTMLOptionElement;
|
||||
"output": ddeHTMLOutputElement;
|
||||
"p": ddeHTMLParagraphElement;
|
||||
"picture": ddeHTMLPictureElement;
|
||||
"pre": ddeHTMLPreElement;
|
||||
"progress": ddeHTMLProgressElement;
|
||||
"q": ddeHTMLQuoteElement;
|
||||
"script": ddeHTMLScriptElement;
|
||||
"select": ddeHTMLSelectElement;
|
||||
"slot": ddeHTMLSlotElement;
|
||||
"source": ddeHTMLSourceElement;
|
||||
"span": ddeHTMLSpanElement;
|
||||
"style": ddeHTMLStyleElement;
|
||||
"table": ddeHTMLTableElement;
|
||||
"tbody": ddeHTMLTableSectionElement;
|
||||
"td": ddeHTMLTableCellElement;
|
||||
"template": ddeHTMLTemplateElement;
|
||||
"textarea": ddeHTMLTextAreaElement;
|
||||
"tfoot": ddeHTMLTableSectionElement;
|
||||
"th": ddeHTMLTableCellElement;
|
||||
"thead": ddeHTMLTableSectionElement;
|
||||
"time": ddeHTMLTimeElement;
|
||||
"title": ddeHTMLTitleElement;
|
||||
"tr": ddeHTMLTableRowElement;
|
||||
"track": ddeHTMLTrackElement;
|
||||
"ul": ddeHTMLUListElement;
|
||||
"video": ddeHTMLVideoElement;
|
||||
}
|
||||
interface ddeSVGElementTagNameMap {
|
||||
"a": ddeSVGAElement;
|
||||
"animate": ddeSVGAnimateElement;
|
||||
"animateMotion": ddeSVGAnimateMotionElement;
|
||||
"animateTransform": ddeSVGAnimateTransformElement;
|
||||
"circle": ddeSVGCircleElement;
|
||||
"clipPath": ddeSVGClipPathElement;
|
||||
"defs": ddeSVGDefsElement;
|
||||
"desc": ddeSVGDescElement;
|
||||
"ellipse": ddeSVGEllipseElement;
|
||||
"feBlend": ddeSVGFEBlendElement;
|
||||
"feColorMatrix": ddeSVGFEColorMatrixElement;
|
||||
"feComponentTransfer": ddeSVGFEComponentTransferElement;
|
||||
"feComposite": ddeSVGFECompositeElement;
|
||||
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
|
||||
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
|
||||
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
|
||||
"feDistantLight": ddeSVGFEDistantLightElement;
|
||||
"feDropShadow": ddeSVGFEDropShadowElement;
|
||||
"feFlood": ddeSVGFEFloodElement;
|
||||
"feFuncA": ddeSVGFEFuncAElement;
|
||||
"feFuncB": ddeSVGFEFuncBElement;
|
||||
"feFuncG": ddeSVGFEFuncGElement;
|
||||
"feFuncR": ddeSVGFEFuncRElement;
|
||||
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
|
||||
"feImage": ddeSVGFEImageElement;
|
||||
"feMerge": ddeSVGFEMergeElement;
|
||||
"feMergeNode": ddeSVGFEMergeNodeElement;
|
||||
"feMorphology": ddeSVGFEMorphologyElement;
|
||||
"feOffset": ddeSVGFEOffsetElement;
|
||||
"fePointLight": ddeSVGFEPointLightElement;
|
||||
"feSpecularLighting": ddeSVGFESpecularLightingElement;
|
||||
"feSpotLight": ddeSVGFESpotLightElement;
|
||||
"feTile": ddeSVGFETileElement;
|
||||
"feTurbulence": ddeSVGFETurbulenceElement;
|
||||
"filter": ddeSVGFilterElement;
|
||||
"foreignObject": ddeSVGForeignObjectElement;
|
||||
"g": ddeSVGGElement;
|
||||
"image": ddeSVGImageElement;
|
||||
"line": ddeSVGLineElement;
|
||||
"linearGradient": ddeSVGLinearGradientElement;
|
||||
"marker": ddeSVGMarkerElement;
|
||||
"mask": ddeSVGMaskElement;
|
||||
"metadata": ddeSVGMetadataElement;
|
||||
"mpath": ddeSVGMPathElement;
|
||||
"path": ddeSVGPathElement;
|
||||
"pattern": ddeSVGPatternElement;
|
||||
"polygon": ddeSVGPolygonElement;
|
||||
"polyline": ddeSVGPolylineElement;
|
||||
"radialGradient": ddeSVGRadialGradientElement;
|
||||
"rect": ddeSVGRectElement;
|
||||
"script": ddeSVGScriptElement;
|
||||
"set": ddeSVGSetElement;
|
||||
"stop": ddeSVGStopElement;
|
||||
"style": ddeSVGStyleElement;
|
||||
"svg": ddeSVGSVGElement;
|
||||
"switch": ddeSVGSwitchElement;
|
||||
"symbol": ddeSVGSymbolElement;
|
||||
"text": ddeSVGTextElement;
|
||||
"textPath": ddeSVGTextPathElement;
|
||||
"title": ddeSVGTitleElement;
|
||||
"tspan": ddeSVGTSpanElement;
|
||||
"use": ddeSVGUseElement;
|
||||
"view": ddeSVGViewElement;
|
||||
}
|
||||
}
|
||||
// editorconfig-checker-disable
|
||||
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
|
||||
append: ddeAppend<ddeHTMLAnchorElement>;
|
||||
}
|
||||
export interface ddeHTMLAreaElement extends HTMLAreaElement {
|
||||
append: ddeAppend<ddeHTMLAreaElement>;
|
||||
}
|
||||
export interface ddeHTMLAudioElement extends HTMLAudioElement {
|
||||
append: ddeAppend<ddeHTMLAudioElement>;
|
||||
}
|
||||
export interface ddeHTMLBaseElement extends HTMLBaseElement {
|
||||
append: ddeAppend<ddeHTMLBaseElement>;
|
||||
}
|
||||
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
|
||||
append: ddeAppend<ddeHTMLQuoteElement>;
|
||||
}
|
||||
export interface ddeHTMLBodyElement extends HTMLBodyElement {
|
||||
append: ddeAppend<ddeHTMLBodyElement>;
|
||||
}
|
||||
export interface ddeHTMLBRElement extends HTMLBRElement {
|
||||
append: ddeAppend<ddeHTMLBRElement>;
|
||||
}
|
||||
export interface ddeHTMLButtonElement extends HTMLButtonElement {
|
||||
append: ddeAppend<ddeHTMLButtonElement>;
|
||||
}
|
||||
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
|
||||
append: ddeAppend<ddeHTMLCanvasElement>;
|
||||
}
|
||||
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
|
||||
append: ddeAppend<ddeHTMLTableCaptionElement>;
|
||||
}
|
||||
export interface ddeHTMLTableColElement extends HTMLTableColElement {
|
||||
append: ddeAppend<ddeHTMLTableColElement>;
|
||||
}
|
||||
export interface ddeHTMLTableColElement extends HTMLTableColElement {
|
||||
append: ddeAppend<ddeHTMLTableColElement>;
|
||||
}
|
||||
export interface ddeHTMLDataElement extends HTMLDataElement {
|
||||
append: ddeAppend<ddeHTMLDataElement>;
|
||||
}
|
||||
export interface ddeHTMLDataListElement extends HTMLDataListElement {
|
||||
append: ddeAppend<ddeHTMLDataListElement>;
|
||||
}
|
||||
export interface ddeHTMLModElement extends HTMLModElement {
|
||||
append: ddeAppend<ddeHTMLModElement>;
|
||||
}
|
||||
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
|
||||
append: ddeAppend<ddeHTMLDetailsElement>;
|
||||
}
|
||||
export interface ddeHTMLDialogElement extends HTMLDialogElement {
|
||||
append: ddeAppend<ddeHTMLDialogElement>;
|
||||
}
|
||||
export interface ddeHTMLDivElement extends HTMLDivElement {
|
||||
append: ddeAppend<ddeHTMLDivElement>;
|
||||
}
|
||||
export interface ddeHTMLDListElement extends HTMLDListElement {
|
||||
append: ddeAppend<ddeHTMLDListElement>;
|
||||
}
|
||||
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
|
||||
append: ddeAppend<ddeHTMLEmbedElement>;
|
||||
}
|
||||
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
|
||||
append: ddeAppend<ddeHTMLFieldSetElement>;
|
||||
}
|
||||
export interface ddeHTMLFormElement extends HTMLFormElement {
|
||||
append: ddeAppend<ddeHTMLFormElement>;
|
||||
}
|
||||
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
|
||||
append: ddeAppend<ddeHTMLHeadingElement>;
|
||||
}
|
||||
export interface ddeHTMLHeadElement extends HTMLHeadElement {
|
||||
append: ddeAppend<ddeHTMLHeadElement>;
|
||||
}
|
||||
export interface ddeHTMLHRElement extends HTMLHRElement {
|
||||
append: ddeAppend<ddeHTMLHRElement>;
|
||||
}
|
||||
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
|
||||
append: ddeAppend<ddeHTMLHtmlElement>;
|
||||
}
|
||||
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
|
||||
append: ddeAppend<ddeHTMLIFrameElement>;
|
||||
}
|
||||
export interface ddeHTMLImageElement extends HTMLImageElement {
|
||||
append: ddeAppend<ddeHTMLImageElement>;
|
||||
}
|
||||
export interface ddeHTMLInputElement extends HTMLInputElement {
|
||||
append: ddeAppend<ddeHTMLInputElement>;
|
||||
}
|
||||
export interface ddeHTMLLabelElement extends HTMLLabelElement {
|
||||
append: ddeAppend<ddeHTMLLabelElement>;
|
||||
}
|
||||
export interface ddeHTMLLegendElement extends HTMLLegendElement {
|
||||
append: ddeAppend<ddeHTMLLegendElement>;
|
||||
}
|
||||
export interface ddeHTMLLIElement extends HTMLLIElement {
|
||||
append: ddeAppend<ddeHTMLLIElement>;
|
||||
}
|
||||
export interface ddeHTMLLinkElement extends HTMLLinkElement {
|
||||
append: ddeAppend<ddeHTMLLinkElement>;
|
||||
}
|
||||
export interface ddeHTMLMapElement extends HTMLMapElement {
|
||||
append: ddeAppend<ddeHTMLMapElement>;
|
||||
}
|
||||
export interface ddeHTMLMenuElement extends HTMLMenuElement {
|
||||
append: ddeAppend<ddeHTMLMenuElement>;
|
||||
}
|
||||
export interface ddeHTMLMetaElement extends HTMLMetaElement {
|
||||
append: ddeAppend<ddeHTMLMetaElement>;
|
||||
}
|
||||
export interface ddeHTMLMeterElement extends HTMLMeterElement {
|
||||
append: ddeAppend<ddeHTMLMeterElement>;
|
||||
}
|
||||
export interface ddeHTMLObjectElement extends HTMLObjectElement {
|
||||
append: ddeAppend<ddeHTMLObjectElement>;
|
||||
}
|
||||
export interface ddeHTMLOListElement extends HTMLOListElement {
|
||||
append: ddeAppend<ddeHTMLOListElement>;
|
||||
}
|
||||
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
|
||||
append: ddeAppend<ddeHTMLOptGroupElement>;
|
||||
}
|
||||
export interface ddeHTMLOptionElement extends HTMLOptionElement {
|
||||
append: ddeAppend<ddeHTMLOptionElement>;
|
||||
}
|
||||
export interface ddeHTMLOutputElement extends HTMLOutputElement {
|
||||
append: ddeAppend<ddeHTMLOutputElement>;
|
||||
}
|
||||
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
|
||||
append: ddeAppend<ddeHTMLParagraphElement>;
|
||||
}
|
||||
export interface ddeHTMLPictureElement extends HTMLPictureElement {
|
||||
append: ddeAppend<ddeHTMLPictureElement>;
|
||||
}
|
||||
export interface ddeHTMLPreElement extends HTMLPreElement {
|
||||
append: ddeAppend<ddeHTMLPreElement>;
|
||||
}
|
||||
export interface ddeHTMLProgressElement extends HTMLProgressElement {
|
||||
append: ddeAppend<ddeHTMLProgressElement>;
|
||||
}
|
||||
export interface ddeHTMLScriptElement extends HTMLScriptElement {
|
||||
append: ddeAppend<ddeHTMLScriptElement>;
|
||||
}
|
||||
export interface ddeHTMLSelectElement extends HTMLSelectElement {
|
||||
append: ddeAppend<ddeHTMLSelectElement>;
|
||||
}
|
||||
export interface ddeHTMLSlotElement extends HTMLSlotElement {
|
||||
append: ddeAppend<ddeHTMLSlotElement>;
|
||||
}
|
||||
export interface ddeHTMLSourceElement extends HTMLSourceElement {
|
||||
append: ddeAppend<ddeHTMLSourceElement>;
|
||||
}
|
||||
export interface ddeHTMLSpanElement extends HTMLSpanElement {
|
||||
append: ddeAppend<ddeHTMLSpanElement>;
|
||||
}
|
||||
export interface ddeHTMLStyleElement extends HTMLStyleElement {
|
||||
append: ddeAppend<ddeHTMLStyleElement>;
|
||||
}
|
||||
export interface ddeHTMLTableElement extends HTMLTableElement {
|
||||
append: ddeAppend<ddeHTMLTableElement>;
|
||||
}
|
||||
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
|
||||
append: ddeAppend<ddeHTMLTableSectionElement>;
|
||||
}
|
||||
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
|
||||
append: ddeAppend<ddeHTMLTableCellElement>;
|
||||
}
|
||||
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
|
||||
append: ddeAppend<ddeHTMLTemplateElement>;
|
||||
}
|
||||
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
|
||||
append: ddeAppend<ddeHTMLTextAreaElement>;
|
||||
}
|
||||
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
|
||||
append: ddeAppend<ddeHTMLTableCellElement>;
|
||||
}
|
||||
export interface ddeHTMLTimeElement extends HTMLTimeElement {
|
||||
append: ddeAppend<ddeHTMLTimeElement>;
|
||||
}
|
||||
export interface ddeHTMLTitleElement extends HTMLTitleElement {
|
||||
append: ddeAppend<ddeHTMLTitleElement>;
|
||||
}
|
||||
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
|
||||
append: ddeAppend<ddeHTMLTableRowElement>;
|
||||
}
|
||||
export interface ddeHTMLTrackElement extends HTMLTrackElement {
|
||||
append: ddeAppend<ddeHTMLTrackElement>;
|
||||
}
|
||||
export interface ddeHTMLUListElement extends HTMLUListElement {
|
||||
append: ddeAppend<ddeHTMLUListElement>;
|
||||
}
|
||||
export interface ddeHTMLVideoElement extends HTMLVideoElement {
|
||||
append: ddeAppend<ddeHTMLVideoElement>;
|
||||
}
|
||||
export interface ddeSVGAElement extends SVGAElement {
|
||||
append: ddeAppend<ddeSVGAElement>;
|
||||
}
|
||||
export interface ddeSVGAnimateElement extends SVGAnimateElement {
|
||||
append: ddeAppend<ddeSVGAnimateElement>;
|
||||
}
|
||||
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
|
||||
append: ddeAppend<ddeSVGAnimateMotionElement>;
|
||||
}
|
||||
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
|
||||
append: ddeAppend<ddeSVGAnimateTransformElement>;
|
||||
}
|
||||
export interface ddeSVGCircleElement extends SVGCircleElement {
|
||||
append: ddeAppend<ddeSVGCircleElement>;
|
||||
}
|
||||
export interface ddeSVGClipPathElement extends SVGClipPathElement {
|
||||
append: ddeAppend<ddeSVGClipPathElement>;
|
||||
}
|
||||
export interface ddeSVGDefsElement extends SVGDefsElement {
|
||||
append: ddeAppend<ddeSVGDefsElement>;
|
||||
}
|
||||
export interface ddeSVGDescElement extends SVGDescElement {
|
||||
append: ddeAppend<ddeSVGDescElement>;
|
||||
}
|
||||
export interface ddeSVGEllipseElement extends SVGEllipseElement {
|
||||
append: ddeAppend<ddeSVGEllipseElement>;
|
||||
}
|
||||
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
|
||||
append: ddeAppend<ddeSVGFEBlendElement>;
|
||||
}
|
||||
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
|
||||
append: ddeAppend<ddeSVGFEColorMatrixElement>;
|
||||
}
|
||||
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
|
||||
append: ddeAppend<ddeSVGFEComponentTransferElement>;
|
||||
}
|
||||
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
|
||||
append: ddeAppend<ddeSVGFECompositeElement>;
|
||||
}
|
||||
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
|
||||
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
|
||||
}
|
||||
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
|
||||
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
|
||||
}
|
||||
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
|
||||
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
|
||||
}
|
||||
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
|
||||
append: ddeAppend<ddeSVGFEDistantLightElement>;
|
||||
}
|
||||
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
|
||||
append: ddeAppend<ddeSVGFEDropShadowElement>;
|
||||
}
|
||||
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
|
||||
append: ddeAppend<ddeSVGFEFloodElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
|
||||
append: ddeAppend<ddeSVGFEFuncAElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
|
||||
append: ddeAppend<ddeSVGFEFuncBElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
|
||||
append: ddeAppend<ddeSVGFEFuncGElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
|
||||
append: ddeAppend<ddeSVGFEFuncRElement>;
|
||||
}
|
||||
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
|
||||
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
|
||||
}
|
||||
export interface ddeSVGFEImageElement extends SVGFEImageElement {
|
||||
append: ddeAppend<ddeSVGFEImageElement>;
|
||||
}
|
||||
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
|
||||
append: ddeAppend<ddeSVGFEMergeElement>;
|
||||
}
|
||||
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
|
||||
append: ddeAppend<ddeSVGFEMergeNodeElement>;
|
||||
}
|
||||
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
|
||||
append: ddeAppend<ddeSVGFEMorphologyElement>;
|
||||
}
|
||||
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
|
||||
append: ddeAppend<ddeSVGFEOffsetElement>;
|
||||
}
|
||||
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
|
||||
append: ddeAppend<ddeSVGFEPointLightElement>;
|
||||
}
|
||||
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
|
||||
append: ddeAppend<ddeSVGFESpecularLightingElement>;
|
||||
}
|
||||
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
|
||||
append: ddeAppend<ddeSVGFESpotLightElement>;
|
||||
}
|
||||
export interface ddeSVGFETileElement extends SVGFETileElement {
|
||||
append: ddeAppend<ddeSVGFETileElement>;
|
||||
}
|
||||
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
|
||||
append: ddeAppend<ddeSVGFETurbulenceElement>;
|
||||
}
|
||||
export interface ddeSVGFilterElement extends SVGFilterElement {
|
||||
append: ddeAppend<ddeSVGFilterElement>;
|
||||
}
|
||||
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
|
||||
append: ddeAppend<ddeSVGForeignObjectElement>;
|
||||
}
|
||||
export interface ddeSVGGElement extends SVGGElement {
|
||||
append: ddeAppend<ddeSVGGElement>;
|
||||
}
|
||||
export interface ddeSVGImageElement extends SVGImageElement {
|
||||
append: ddeAppend<ddeSVGImageElement>;
|
||||
}
|
||||
export interface ddeSVGLineElement extends SVGLineElement {
|
||||
append: ddeAppend<ddeSVGLineElement>;
|
||||
}
|
||||
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
|
||||
append: ddeAppend<ddeSVGLinearGradientElement>;
|
||||
}
|
||||
export interface ddeSVGMarkerElement extends SVGMarkerElement {
|
||||
append: ddeAppend<ddeSVGMarkerElement>;
|
||||
}
|
||||
export interface ddeSVGMaskElement extends SVGMaskElement {
|
||||
append: ddeAppend<ddeSVGMaskElement>;
|
||||
}
|
||||
export interface ddeSVGMetadataElement extends SVGMetadataElement {
|
||||
append: ddeAppend<ddeSVGMetadataElement>;
|
||||
}
|
||||
export interface ddeSVGMPathElement extends SVGMPathElement {
|
||||
append: ddeAppend<ddeSVGMPathElement>;
|
||||
}
|
||||
export interface ddeSVGPathElement extends SVGPathElement {
|
||||
append: ddeAppend<ddeSVGPathElement>;
|
||||
}
|
||||
export interface ddeSVGPatternElement extends SVGPatternElement {
|
||||
append: ddeAppend<ddeSVGPatternElement>;
|
||||
}
|
||||
export interface ddeSVGPolygonElement extends SVGPolygonElement {
|
||||
append: ddeAppend<ddeSVGPolygonElement>;
|
||||
}
|
||||
export interface ddeSVGPolylineElement extends SVGPolylineElement {
|
||||
append: ddeAppend<ddeSVGPolylineElement>;
|
||||
}
|
||||
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
|
||||
append: ddeAppend<ddeSVGRadialGradientElement>;
|
||||
}
|
||||
export interface ddeSVGRectElement extends SVGRectElement {
|
||||
append: ddeAppend<ddeSVGRectElement>;
|
||||
}
|
||||
export interface ddeSVGScriptElement extends SVGScriptElement {
|
||||
append: ddeAppend<ddeSVGScriptElement>;
|
||||
}
|
||||
export interface ddeSVGSetElement extends SVGSetElement {
|
||||
append: ddeAppend<ddeSVGSetElement>;
|
||||
}
|
||||
export interface ddeSVGStopElement extends SVGStopElement {
|
||||
append: ddeAppend<ddeSVGStopElement>;
|
||||
}
|
||||
export interface ddeSVGStyleElement extends SVGStyleElement {
|
||||
append: ddeAppend<ddeSVGStyleElement>;
|
||||
}
|
||||
export interface ddeSVGSVGElement extends SVGSVGElement {
|
||||
append: ddeAppend<ddeSVGSVGElement>;
|
||||
}
|
||||
export interface ddeSVGSwitchElement extends SVGSwitchElement {
|
||||
append: ddeAppend<ddeSVGSwitchElement>;
|
||||
}
|
||||
export interface ddeSVGSymbolElement extends SVGSymbolElement {
|
||||
append: ddeAppend<ddeSVGSymbolElement>;
|
||||
}
|
||||
export interface ddeSVGTextElement extends SVGTextElement {
|
||||
append: ddeAppend<ddeSVGTextElement>;
|
||||
}
|
||||
export interface ddeSVGTextPathElement extends SVGTextPathElement {
|
||||
append: ddeAppend<ddeSVGTextPathElement>;
|
||||
}
|
||||
export interface ddeSVGTitleElement extends SVGTitleElement {
|
||||
append: ddeAppend<ddeSVGTitleElement>;
|
||||
}
|
||||
export interface ddeSVGTSpanElement extends SVGTSpanElement {
|
||||
append: ddeAppend<ddeSVGTSpanElement>;
|
||||
}
|
||||
export interface ddeSVGUseElement extends SVGUseElement {
|
||||
append: ddeAppend<ddeSVGUseElement>;
|
||||
}
|
||||
export interface ddeSVGViewElement extends SVGViewElement {
|
||||
append: ddeAppend<ddeSVGViewElement>;
|
||||
}
|
||||
|
||||
export {
|
||||
dispatchEvent$1 as dispatchEvent,
|
||||
el as createElement,
|
||||
elNS as createElementNS,
|
||||
};
|
||||
|
||||
export as namespace iife;
|
||||
|
||||
export {};
|
1029
dist/iife-with-signals.js
vendored
Normal file
1029
dist/iife-with-signals.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
862
dist/iife-with-signals.min.d.ts
vendored
Normal file
862
dist/iife-with-signals.min.d.ts
vendored
Normal 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
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
861
dist/iife.d.ts
vendored
Normal file
@ -0,0 +1,861 @@
|
||||
// Generated by dts-bundle-generator v9.5.1
|
||||
|
||||
export interface Signal<V, A> {
|
||||
/** The current value of the signal */
|
||||
get(): V;
|
||||
/** Set new value of the signal */
|
||||
set(value: V, force?: boolean): V;
|
||||
toJSON(): V;
|
||||
valueOf(): V;
|
||||
}
|
||||
export type Action<V> = (this: {
|
||||
value: V;
|
||||
stopPropagation(): void;
|
||||
}, ...a: any[]) => typeof signal._ | void;
|
||||
//type SymbolSignal= Symbol;
|
||||
export type SymbolOnclear = symbol;
|
||||
export type Actions<V> = Record<string | SymbolOnclear, Action<V>>;
|
||||
export type OnListenerOptions = Pick<AddEventListenerOptions, "signal"> & {
|
||||
first_time?: boolean;
|
||||
};
|
||||
export type SElement = Node | Element | DocumentFragment | ddeHTMLElement | ddeSVGElement | ddeDocumentFragment;
|
||||
export interface signal {
|
||||
_: Symbol;
|
||||
/**
|
||||
* Computations signal. This creates a signal which is computed from other signals.
|
||||
* */
|
||||
<V extends () => any>(computation: V): Signal<ReturnType<V>, {}>;
|
||||
/**
|
||||
* Simple example:
|
||||
* ```js
|
||||
* const hello= S("Hello Signal");
|
||||
* ```
|
||||
* …simple todo signal:
|
||||
* ```js
|
||||
* const todos= S([], {
|
||||
* add(v){ this.value.push(S(v)); },
|
||||
* remove(i){ this.value.splice(i, 1); },
|
||||
* [S.symbols.onclear](){ S.clear(...this.value); },
|
||||
* });
|
||||
* ```
|
||||
* …computed signal:
|
||||
* ```js
|
||||
* const name= S("Jan");
|
||||
* const surname= S("Andrle");
|
||||
* const fullname= S(()=> name.get()+" "+surname.get());
|
||||
* ```
|
||||
* @param value Initial signal value. Or function computing value from other signals.
|
||||
* @param actions Use to define actions on the signal. Such as add item to the array.
|
||||
* There is also a reserved function `S.symbol.onclear` which is called when the signal is cleared
|
||||
* by `S.clear`.
|
||||
* */
|
||||
<V, A extends Actions<V>>(value: V, actions?: A): Signal<V, A>;
|
||||
action<S extends Signal<any, Actions<any>>, A extends (S extends Signal<any, infer A> ? A : never), N extends keyof A>(signal: S, name: N, ...params: A[N] extends (...args: infer P) => any ? P : never): void;
|
||||
clear(...signals: Signal<any, any>[]): void;
|
||||
on<T>(signal: Signal<T, any>, onchange: (a: T) => void, options?: OnListenerOptions): void;
|
||||
symbols: {
|
||||
//signal: SymbolSignal;
|
||||
onclear: SymbolOnclear;
|
||||
};
|
||||
/**
|
||||
* Reactive element, which is rendered based on the given signal.
|
||||
* ```js
|
||||
* S.el(signal, value=> value ? el("b", "True") : el("i", "False"));
|
||||
* S.el(listS, list=> list.map(li=> el("li", li)));
|
||||
* ```
|
||||
* */
|
||||
el<S extends any>(signal: Signal<S, any>, el: (v: S) => SElement | SElement[]): DocumentFragment;
|
||||
observedAttributes(custom_element: HTMLElement): Record<string, Signal<string, {}>>;
|
||||
}
|
||||
declare const signal: signal;
|
||||
declare global {
|
||||
type ddeSignal<T, A = {}> = Signal<T, A>;
|
||||
type ddeAction<V> = Action<V>;
|
||||
type ddeActions<V> = Actions<V>;
|
||||
}
|
||||
export type CustomElementTagNameMap = {
|
||||
"#text": Text;
|
||||
"#comment": Comment;
|
||||
};
|
||||
export type SupportedElement = HTMLElementTagNameMap[keyof HTMLElementTagNameMap] | SVGElementTagNameMap[keyof SVGElementTagNameMap] | MathMLElementTagNameMap[keyof MathMLElementTagNameMap] | CustomElementTagNameMap[keyof CustomElementTagNameMap];
|
||||
declare global {
|
||||
type ddeComponentAttributes = Record<any, any> | undefined;
|
||||
type ddeElementAddon<El extends SupportedElement | DocumentFragment | Node> = (element: El, ...rest: any) => any;
|
||||
type ddeString = string | Signal<string, {}>;
|
||||
type ddeStringable = ddeString | number | Signal<number, {}>;
|
||||
}
|
||||
export type Host<EL extends SupportedElement> = (...addons: ddeElementAddon<EL>[]) => EL;
|
||||
export type PascalCase = `${Capitalize<string>}${string}`;
|
||||
export type AttrsModified = {
|
||||
/**
|
||||
* Use string like in HTML (internally uses `*.setAttribute("style", *)`), or object representation (like DOM API).
|
||||
*/
|
||||
style: Partial<CSSStyleDeclaration> | ddeString | Partial<{
|
||||
[K in keyof CSSStyleDeclaration]: Signal<CSSStyleDeclaration[K], {}>;
|
||||
}>;
|
||||
/**
|
||||
* Provide option to add/remove/toggle CSS clasess (index of object) using 1/0/-1.
|
||||
* In fact `el.classList.toggle(class_name)` for `-1` and `el.classList.toggle(class_name, Boolean(...))`
|
||||
* for others.
|
||||
*/
|
||||
classList: Record<string, -1 | 0 | 1 | boolean | Signal<-1 | 0 | 1 | boolean, {}>>;
|
||||
/**
|
||||
* Used by the dataset HTML attribute to represent data for custom attributes added to elements.
|
||||
* Values are converted to string (see {@link DOMStringMap}).
|
||||
*
|
||||
* [MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMStringMap)
|
||||
* */
|
||||
dataset: Record<string, ddeStringable>;
|
||||
/**
|
||||
* Sets `aria-*` simiraly to `dataset`
|
||||
* */
|
||||
ariaset: Record<string, ddeString>;
|
||||
} & Record<`=${string}` | `data${PascalCase}` | `aria${PascalCase}`, ddeString> & Record<`.${string}`, any>;
|
||||
export type _fromElsInterfaces<EL extends SupportedElement> = Omit<EL, keyof AttrsModified>;
|
||||
export type IsReadonly<T, K extends keyof T> = T extends {
|
||||
readonly [P in K]: T[K];
|
||||
} ? true : false;
|
||||
/**
|
||||
* Just element attributtes
|
||||
*
|
||||
* In most cases, you can use native propertie such as
|
||||
* [MDN WEB/API/Element](https://developer.mozilla.org/en-US/docs/Web/API/Element) and so on
|
||||
* (e.g. [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text)).
|
||||
*
|
||||
* There is added support for `data[A-Z].*`/`aria[A-Z].*` to be converted to the kebab-case alternatives.
|
||||
* @private
|
||||
*/
|
||||
export type ElementAttributes<T extends SupportedElement> = Partial<{
|
||||
[K in keyof _fromElsInterfaces<T>]: _fromElsInterfaces<T>[K] extends ((...p: any[]) => any) ? _fromElsInterfaces<T>[K] | ((...p: Parameters<_fromElsInterfaces<T>[K]>) => Signal<ReturnType<_fromElsInterfaces<T>[K]>, {}>) : (IsReadonly<_fromElsInterfaces<T>, K> extends false ? _fromElsInterfaces<T>[K] | Signal<_fromElsInterfaces<T>[K], {}> : ddeStringable);
|
||||
} & AttrsModified> & Record<string, any>;
|
||||
export function classListDeclarative<El extends SupportedElement>(element: El, classList: AttrsModified["classList"]): El;
|
||||
export function assign<El extends SupportedElement>(element: El, ...attrs_array: ElementAttributes<El>[]): El;
|
||||
export function assignAttribute<El extends SupportedElement, ATT extends keyof ElementAttributes<El>>(element: El, attr: ATT, value: ElementAttributes<El>[ATT]): ElementAttributes<El>[ATT];
|
||||
export type ExtendedHTMLElementTagNameMap = HTMLElementTagNameMap & CustomElementTagNameMap;
|
||||
export namespace el {
|
||||
/**
|
||||
* Creates a marker comment for elements
|
||||
*
|
||||
* @param attrs - Marker attributes
|
||||
* @param [is_open=false] - Whether the marker is open-ended
|
||||
* @returns Comment node marker
|
||||
*/
|
||||
export function mark(attrs: {
|
||||
type: "component" | "reactive" | "later";
|
||||
name?: string;
|
||||
host?: "this" | "parentElement";
|
||||
}, is_open?: boolean): Comment;
|
||||
}
|
||||
export function chainableAppend<EL extends SupportedElement>(el: EL): EL | ddeHTMLElement;
|
||||
export function el<A extends ddeComponentAttributes, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>, ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
|
||||
export function el<A extends {
|
||||
textContent: ddeStringable;
|
||||
}, EL extends SupportedElement | ddeDocumentFragment>(component: (attr: A, ...rest: any[]) => EL, attrs?: NoInfer<A>["textContent"], ...addons: ddeElementAddon<EL>[]): EL extends ddeHTMLElementTagNameMap[keyof ddeHTMLElementTagNameMap] ? EL : (EL extends ddeDocumentFragment ? EL : ddeHTMLElement);
|
||||
export function el<TAG extends keyof ExtendedHTMLElementTagNameMap>(tag_name: TAG, attrs?: ElementAttributes<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]> | ddeStringable, ...addons: ddeElementAddon<ExtendedHTMLElementTagNameMap[NoInfer<TAG>]>[]): TAG extends keyof ddeHTMLElementTagNameMap ? ddeHTMLElementTagNameMap[TAG] : ddeHTMLElement;
|
||||
export function el(tag_name?: "<>"): ddeDocumentFragment;
|
||||
export function el(tag_name: string, attrs?: ElementAttributes<HTMLElement> | ddeStringable, ...addons: ddeElementAddon<HTMLElement>[]): ddeHTMLElement;
|
||||
export function elNS(namespace: "http://www.w3.org/2000/svg"): <TAG extends keyof SVGElementTagNameMap & string, EL extends (TAG extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[TAG] : SVGElement)>(tag_name: TAG, attrs?: ElementAttributes<NoInfer<EL>> | ddeStringable, ...addons: ddeElementAddon<NoInfer<EL>>[]) => TAG extends keyof ddeSVGElementTagNameMap ? ddeSVGElementTagNameMap[TAG] : ddeSVGElement;
|
||||
export function elNS(namespace: "http://www.w3.org/1998/Math/MathML"): <TAG extends keyof MathMLElementTagNameMap & string, EL extends (TAG extends keyof MathMLElementTagNameMap ? MathMLElementTagNameMap[TAG] : MathMLElement)>(tag_name: TAG, attrs?: ddeStringable | Partial<{
|
||||
[key in keyof EL]: EL[key] | Signal<EL[key], {}> | string | number | boolean;
|
||||
}>, ...addons: ddeElementAddon<NoInfer<EL>>[]) => ddeMathMLElement;
|
||||
export function elNS(namespace: string): (tag_name: string, attrs?: string | ddeStringable | Record<string, any>, ...addons: ddeElementAddon<SupportedElement>[]) => SupportedElement;
|
||||
/** Simulate slots for ddeComponents */
|
||||
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(root: EL): EL;
|
||||
/**
|
||||
* Simulate slots in Custom Elements without using `shadowRoot`.
|
||||
* @param el Custom Element root element
|
||||
* @param body Body of the custom element
|
||||
* */
|
||||
export function simulateSlots<EL extends SupportedElement | DocumentFragment>(el: HTMLElement, body: EL): EL;
|
||||
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, host: Host<SupportedElement>): (data?: any) => void;
|
||||
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options?: EventInit): (element: SupportedElement, data?: any) => void;
|
||||
declare function dispatchEvent$1(name: keyof DocumentEventMap | string, options: EventInit | null, host: Host<SupportedElement>): (data?: any) => void;
|
||||
export interface On {
|
||||
/** Listens to the DOM event. See {@link Document.addEventListener} */
|
||||
<Event extends keyof DocumentEventMap, EL extends SupportedElement>(type: Event, listener: (this: EL, ev: DocumentEventMap[Event] & {
|
||||
target: EL;
|
||||
}) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
|
||||
<EE extends ddeElementAddon<SupportedElement> = ddeElementAddon<HTMLElement>>(type: string, listener: (this: EE extends ddeElementAddon<infer El> ? El : never, ev: Event | CustomEvent) => any, options?: AddEventListenerOptions): EE;
|
||||
/** Listens to the element is connected to the live DOM. In case of custom elements uses [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
|
||||
connected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<NoInfer<EL>>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
|
||||
/** Listens to the element is disconnected from the live DOM. In case of custom elements uses [`disconnectedCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements#custom_element_lifecycle_callbacks), or {@link MutationObserver} else where */ // editorconfig-checker-disable-line
|
||||
disconnected<EL extends SupportedElement>(listener: (this: EL, event: CustomEvent<void>) => any, options?: AddEventListenerOptions): ddeElementAddon<EL>;
|
||||
/**
|
||||
* Fires after the next tick of the Javascript event loop.
|
||||
* This is handy for example to apply some property depending on the element content:
|
||||
* ```js
|
||||
* const selected= "Z";
|
||||
* //...
|
||||
* return el("form").append(
|
||||
* el("select", null, on.defer(e=> e.value=selected)).append(
|
||||
* el("option", { value: "A", textContent: "A" }),
|
||||
* //...
|
||||
* el("option", { value: "Z", textContent: "Z" }),
|
||||
* ),
|
||||
* );
|
||||
* ```
|
||||
* */
|
||||
defer<EL extends SupportedElement>(listener: (element: EL) => any): ddeElementAddon<EL>;
|
||||
}
|
||||
export const on: On;
|
||||
export type Scope = {
|
||||
scope: Node | Function | Object;
|
||||
host: Host<SupportedElement>;
|
||||
custom_element: false | HTMLElement;
|
||||
prevent: boolean;
|
||||
};
|
||||
/** Current scope created last time the `el(Function)` was invoke. (Or {@link scope.push}) */
|
||||
export const scope: {
|
||||
current: Scope;
|
||||
/** Stops all automatizations. E. g. signals used as attributes in current scope
|
||||
* registers removing these listeners (and clean signal if no other listeners are detected)
|
||||
* on `disconnected` event. */
|
||||
preventDefault<T extends boolean>(prevent: T): T;
|
||||
/**
|
||||
* This represents reference to the current host element — `scope.host()`.
|
||||
* It can be also used to register Addon(s) (functions to be called when component is initized)
|
||||
* — `scope.host(on.connected(console.log))`.
|
||||
* */
|
||||
host: Host<SupportedElement>;
|
||||
/**
|
||||
* Creates/gets an AbortController that triggers when the element disconnects
|
||||
* */
|
||||
signal: AbortSignal;
|
||||
state: Scope[];
|
||||
/** Adds new child scope. All attributes are inherited by default. */
|
||||
push(scope?: Partial<Scope>): ReturnType<Array<Scope>["push"]>;
|
||||
/** Adds root scope as a child of the current scope. */
|
||||
pushRoot(): ReturnType<Array<Scope>["push"]>;
|
||||
/** Removes last/current child scope. */
|
||||
pop(): ReturnType<Array<Scope>["pop"]>;
|
||||
};
|
||||
export function customElementRender<EL extends HTMLElement, P extends any = Record<string, string | Signal<string, {}>>>(target: ShadowRoot | EL, render: (props: P) => SupportedElement | DocumentFragment, props?: P | ((el: EL) => P)): EL;
|
||||
export function customElementWithDDE<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
|
||||
export function lifecyclesToEvents<EL extends (new () => HTMLElement)>(custom_element: EL): EL;
|
||||
/**
|
||||
* This is used primarly for server side rendering. To be sure that all async operations
|
||||
* are finished before the page is sent to the client.
|
||||
* ```
|
||||
* // on component
|
||||
* function component(){
|
||||
* …
|
||||
* queue(fetch(...).then(...));
|
||||
* }
|
||||
*
|
||||
* // building the page
|
||||
* async function build(){
|
||||
* const { component }= await import("./component.js");
|
||||
* document.body.append(el(component));
|
||||
* await queue();
|
||||
* retutn document.body.innerHTML;
|
||||
* }
|
||||
* ```
|
||||
* */
|
||||
export function queue(promise?: Promise<unknown>): Promise<unknown>;
|
||||
/**
|
||||
* Memoization utility for caching DOM elements to improve performance.
|
||||
* Used to prevent unnecessary recreation of elements when rendering lists or complex components.
|
||||
*
|
||||
* @param key - Unique identifier for the element (usually an ID or unique value)
|
||||
* @param generator - Function that creates the element
|
||||
* @returns The cached element if the key exists, otherwise the result of the generator function
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Within S.el for list rendering
|
||||
* S.el(itemsSignal, (items, memo) =>
|
||||
* el("ul").append(
|
||||
* ...items.map(item =>
|
||||
* memo(item.id, () => el(ItemComponent, item))
|
||||
* )
|
||||
* )
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
export function memo<T>(key: string | number | object, generator: (key: any) => T): T;
|
||||
/**
|
||||
* Memo namespace containing utility functions for memoization.
|
||||
*/
|
||||
export namespace memo {
|
||||
/**
|
||||
* Checks if an object is a memo scope.
|
||||
* @param obj - The object to check
|
||||
* @returns True if the object is a memo scope
|
||||
*/
|
||||
export function isScope(obj: any): boolean;
|
||||
/**
|
||||
* Creates a memoized function with optional cleanup support.
|
||||
*
|
||||
* @param fun - The function to memoize
|
||||
* @param options - Configuration options
|
||||
* @param options.signal - AbortSignal for cleanup
|
||||
* @param options.onlyLast - When true, only keeps the cache from the most recent call
|
||||
* @returns A memoized version of the function with a .clear() method
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const renderItems = memo.scope(function(items) {
|
||||
* return items.map(item =>
|
||||
* memo(item.id, () => el("div", item.name))
|
||||
* );
|
||||
* }, {
|
||||
* signal: controller.signal,
|
||||
* onlyLast: true
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export function scope<F extends Function>(fun: F, options?: {
|
||||
signal?: AbortSignal;
|
||||
onlyLast?: boolean;
|
||||
}): F & {
|
||||
clear: () => void;
|
||||
};
|
||||
}
|
||||
/* TypeScript MEH */
|
||||
declare global {
|
||||
type ddeAppend<el> = (...nodes: (Node | string)[]) => el;
|
||||
interface ddeDocumentFragment extends DocumentFragment {
|
||||
append: ddeAppend<ddeDocumentFragment>;
|
||||
}
|
||||
interface ddeHTMLElement extends HTMLElement {
|
||||
append: ddeAppend<ddeHTMLElement>;
|
||||
}
|
||||
interface ddeSVGElement extends SVGElement {
|
||||
append: ddeAppend<ddeSVGElement>;
|
||||
}
|
||||
interface ddeMathMLElement extends MathMLElement {
|
||||
append: ddeAppend<ddeMathMLElement>;
|
||||
}
|
||||
interface ddeHTMLElementTagNameMap {
|
||||
"a": ddeHTMLAnchorElement;
|
||||
"area": ddeHTMLAreaElement;
|
||||
"audio": ddeHTMLAudioElement;
|
||||
"base": ddeHTMLBaseElement;
|
||||
"blockquote": ddeHTMLQuoteElement;
|
||||
"body": ddeHTMLBodyElement;
|
||||
"br": ddeHTMLBRElement;
|
||||
"button": ddeHTMLButtonElement;
|
||||
"canvas": ddeHTMLCanvasElement;
|
||||
"caption": ddeHTMLTableCaptionElement;
|
||||
"col": ddeHTMLTableColElement;
|
||||
"colgroup": ddeHTMLTableColElement;
|
||||
"data": ddeHTMLDataElement;
|
||||
"datalist": ddeHTMLDataListElement;
|
||||
"del": ddeHTMLModElement;
|
||||
"details": ddeHTMLDetailsElement;
|
||||
"dialog": ddeHTMLDialogElement;
|
||||
"div": ddeHTMLDivElement;
|
||||
"dl": ddeHTMLDListElement;
|
||||
"embed": ddeHTMLEmbedElement;
|
||||
"fieldset": ddeHTMLFieldSetElement;
|
||||
"form": ddeHTMLFormElement;
|
||||
"h1": ddeHTMLHeadingElement;
|
||||
"h2": ddeHTMLHeadingElement;
|
||||
"h3": ddeHTMLHeadingElement;
|
||||
"h4": ddeHTMLHeadingElement;
|
||||
"h5": ddeHTMLHeadingElement;
|
||||
"h6": ddeHTMLHeadingElement;
|
||||
"head": ddeHTMLHeadElement;
|
||||
"hr": ddeHTMLHRElement;
|
||||
"html": ddeHTMLHtmlElement;
|
||||
"iframe": ddeHTMLIFrameElement;
|
||||
"img": ddeHTMLImageElement;
|
||||
"input": ddeHTMLInputElement;
|
||||
"ins": ddeHTMLModElement;
|
||||
"label": ddeHTMLLabelElement;
|
||||
"legend": ddeHTMLLegendElement;
|
||||
"li": ddeHTMLLIElement;
|
||||
"link": ddeHTMLLinkElement;
|
||||
"map": ddeHTMLMapElement;
|
||||
"menu": ddeHTMLMenuElement;
|
||||
"meta": ddeHTMLMetaElement;
|
||||
"meter": ddeHTMLMeterElement;
|
||||
"object": ddeHTMLObjectElement;
|
||||
"ol": ddeHTMLOListElement;
|
||||
"optgroup": ddeHTMLOptGroupElement;
|
||||
"option": ddeHTMLOptionElement;
|
||||
"output": ddeHTMLOutputElement;
|
||||
"p": ddeHTMLParagraphElement;
|
||||
"picture": ddeHTMLPictureElement;
|
||||
"pre": ddeHTMLPreElement;
|
||||
"progress": ddeHTMLProgressElement;
|
||||
"q": ddeHTMLQuoteElement;
|
||||
"script": ddeHTMLScriptElement;
|
||||
"select": ddeHTMLSelectElement;
|
||||
"slot": ddeHTMLSlotElement;
|
||||
"source": ddeHTMLSourceElement;
|
||||
"span": ddeHTMLSpanElement;
|
||||
"style": ddeHTMLStyleElement;
|
||||
"table": ddeHTMLTableElement;
|
||||
"tbody": ddeHTMLTableSectionElement;
|
||||
"td": ddeHTMLTableCellElement;
|
||||
"template": ddeHTMLTemplateElement;
|
||||
"textarea": ddeHTMLTextAreaElement;
|
||||
"tfoot": ddeHTMLTableSectionElement;
|
||||
"th": ddeHTMLTableCellElement;
|
||||
"thead": ddeHTMLTableSectionElement;
|
||||
"time": ddeHTMLTimeElement;
|
||||
"title": ddeHTMLTitleElement;
|
||||
"tr": ddeHTMLTableRowElement;
|
||||
"track": ddeHTMLTrackElement;
|
||||
"ul": ddeHTMLUListElement;
|
||||
"video": ddeHTMLVideoElement;
|
||||
}
|
||||
interface ddeSVGElementTagNameMap {
|
||||
"a": ddeSVGAElement;
|
||||
"animate": ddeSVGAnimateElement;
|
||||
"animateMotion": ddeSVGAnimateMotionElement;
|
||||
"animateTransform": ddeSVGAnimateTransformElement;
|
||||
"circle": ddeSVGCircleElement;
|
||||
"clipPath": ddeSVGClipPathElement;
|
||||
"defs": ddeSVGDefsElement;
|
||||
"desc": ddeSVGDescElement;
|
||||
"ellipse": ddeSVGEllipseElement;
|
||||
"feBlend": ddeSVGFEBlendElement;
|
||||
"feColorMatrix": ddeSVGFEColorMatrixElement;
|
||||
"feComponentTransfer": ddeSVGFEComponentTransferElement;
|
||||
"feComposite": ddeSVGFECompositeElement;
|
||||
"feConvolveMatrix": ddeSVGFEConvolveMatrixElement;
|
||||
"feDiffuseLighting": ddeSVGFEDiffuseLightingElement;
|
||||
"feDisplacementMap": ddeSVGFEDisplacementMapElement;
|
||||
"feDistantLight": ddeSVGFEDistantLightElement;
|
||||
"feDropShadow": ddeSVGFEDropShadowElement;
|
||||
"feFlood": ddeSVGFEFloodElement;
|
||||
"feFuncA": ddeSVGFEFuncAElement;
|
||||
"feFuncB": ddeSVGFEFuncBElement;
|
||||
"feFuncG": ddeSVGFEFuncGElement;
|
||||
"feFuncR": ddeSVGFEFuncRElement;
|
||||
"feGaussianBlur": ddeSVGFEGaussianBlurElement;
|
||||
"feImage": ddeSVGFEImageElement;
|
||||
"feMerge": ddeSVGFEMergeElement;
|
||||
"feMergeNode": ddeSVGFEMergeNodeElement;
|
||||
"feMorphology": ddeSVGFEMorphologyElement;
|
||||
"feOffset": ddeSVGFEOffsetElement;
|
||||
"fePointLight": ddeSVGFEPointLightElement;
|
||||
"feSpecularLighting": ddeSVGFESpecularLightingElement;
|
||||
"feSpotLight": ddeSVGFESpotLightElement;
|
||||
"feTile": ddeSVGFETileElement;
|
||||
"feTurbulence": ddeSVGFETurbulenceElement;
|
||||
"filter": ddeSVGFilterElement;
|
||||
"foreignObject": ddeSVGForeignObjectElement;
|
||||
"g": ddeSVGGElement;
|
||||
"image": ddeSVGImageElement;
|
||||
"line": ddeSVGLineElement;
|
||||
"linearGradient": ddeSVGLinearGradientElement;
|
||||
"marker": ddeSVGMarkerElement;
|
||||
"mask": ddeSVGMaskElement;
|
||||
"metadata": ddeSVGMetadataElement;
|
||||
"mpath": ddeSVGMPathElement;
|
||||
"path": ddeSVGPathElement;
|
||||
"pattern": ddeSVGPatternElement;
|
||||
"polygon": ddeSVGPolygonElement;
|
||||
"polyline": ddeSVGPolylineElement;
|
||||
"radialGradient": ddeSVGRadialGradientElement;
|
||||
"rect": ddeSVGRectElement;
|
||||
"script": ddeSVGScriptElement;
|
||||
"set": ddeSVGSetElement;
|
||||
"stop": ddeSVGStopElement;
|
||||
"style": ddeSVGStyleElement;
|
||||
"svg": ddeSVGSVGElement;
|
||||
"switch": ddeSVGSwitchElement;
|
||||
"symbol": ddeSVGSymbolElement;
|
||||
"text": ddeSVGTextElement;
|
||||
"textPath": ddeSVGTextPathElement;
|
||||
"title": ddeSVGTitleElement;
|
||||
"tspan": ddeSVGTSpanElement;
|
||||
"use": ddeSVGUseElement;
|
||||
"view": ddeSVGViewElement;
|
||||
}
|
||||
}
|
||||
// editorconfig-checker-disable
|
||||
export interface ddeHTMLAnchorElement extends HTMLAnchorElement {
|
||||
append: ddeAppend<ddeHTMLAnchorElement>;
|
||||
}
|
||||
export interface ddeHTMLAreaElement extends HTMLAreaElement {
|
||||
append: ddeAppend<ddeHTMLAreaElement>;
|
||||
}
|
||||
export interface ddeHTMLAudioElement extends HTMLAudioElement {
|
||||
append: ddeAppend<ddeHTMLAudioElement>;
|
||||
}
|
||||
export interface ddeHTMLBaseElement extends HTMLBaseElement {
|
||||
append: ddeAppend<ddeHTMLBaseElement>;
|
||||
}
|
||||
export interface ddeHTMLQuoteElement extends HTMLQuoteElement {
|
||||
append: ddeAppend<ddeHTMLQuoteElement>;
|
||||
}
|
||||
export interface ddeHTMLBodyElement extends HTMLBodyElement {
|
||||
append: ddeAppend<ddeHTMLBodyElement>;
|
||||
}
|
||||
export interface ddeHTMLBRElement extends HTMLBRElement {
|
||||
append: ddeAppend<ddeHTMLBRElement>;
|
||||
}
|
||||
export interface ddeHTMLButtonElement extends HTMLButtonElement {
|
||||
append: ddeAppend<ddeHTMLButtonElement>;
|
||||
}
|
||||
export interface ddeHTMLCanvasElement extends HTMLCanvasElement {
|
||||
append: ddeAppend<ddeHTMLCanvasElement>;
|
||||
}
|
||||
export interface ddeHTMLTableCaptionElement extends HTMLTableCaptionElement {
|
||||
append: ddeAppend<ddeHTMLTableCaptionElement>;
|
||||
}
|
||||
export interface ddeHTMLTableColElement extends HTMLTableColElement {
|
||||
append: ddeAppend<ddeHTMLTableColElement>;
|
||||
}
|
||||
export interface ddeHTMLTableColElement extends HTMLTableColElement {
|
||||
append: ddeAppend<ddeHTMLTableColElement>;
|
||||
}
|
||||
export interface ddeHTMLDataElement extends HTMLDataElement {
|
||||
append: ddeAppend<ddeHTMLDataElement>;
|
||||
}
|
||||
export interface ddeHTMLDataListElement extends HTMLDataListElement {
|
||||
append: ddeAppend<ddeHTMLDataListElement>;
|
||||
}
|
||||
export interface ddeHTMLModElement extends HTMLModElement {
|
||||
append: ddeAppend<ddeHTMLModElement>;
|
||||
}
|
||||
export interface ddeHTMLDetailsElement extends HTMLDetailsElement {
|
||||
append: ddeAppend<ddeHTMLDetailsElement>;
|
||||
}
|
||||
export interface ddeHTMLDialogElement extends HTMLDialogElement {
|
||||
append: ddeAppend<ddeHTMLDialogElement>;
|
||||
}
|
||||
export interface ddeHTMLDivElement extends HTMLDivElement {
|
||||
append: ddeAppend<ddeHTMLDivElement>;
|
||||
}
|
||||
export interface ddeHTMLDListElement extends HTMLDListElement {
|
||||
append: ddeAppend<ddeHTMLDListElement>;
|
||||
}
|
||||
export interface ddeHTMLEmbedElement extends HTMLEmbedElement {
|
||||
append: ddeAppend<ddeHTMLEmbedElement>;
|
||||
}
|
||||
export interface ddeHTMLFieldSetElement extends HTMLFieldSetElement {
|
||||
append: ddeAppend<ddeHTMLFieldSetElement>;
|
||||
}
|
||||
export interface ddeHTMLFormElement extends HTMLFormElement {
|
||||
append: ddeAppend<ddeHTMLFormElement>;
|
||||
}
|
||||
export interface ddeHTMLHeadingElement extends HTMLHeadingElement {
|
||||
append: ddeAppend<ddeHTMLHeadingElement>;
|
||||
}
|
||||
export interface ddeHTMLHeadElement extends HTMLHeadElement {
|
||||
append: ddeAppend<ddeHTMLHeadElement>;
|
||||
}
|
||||
export interface ddeHTMLHRElement extends HTMLHRElement {
|
||||
append: ddeAppend<ddeHTMLHRElement>;
|
||||
}
|
||||
export interface ddeHTMLHtmlElement extends HTMLHtmlElement {
|
||||
append: ddeAppend<ddeHTMLHtmlElement>;
|
||||
}
|
||||
export interface ddeHTMLIFrameElement extends HTMLIFrameElement {
|
||||
append: ddeAppend<ddeHTMLIFrameElement>;
|
||||
}
|
||||
export interface ddeHTMLImageElement extends HTMLImageElement {
|
||||
append: ddeAppend<ddeHTMLImageElement>;
|
||||
}
|
||||
export interface ddeHTMLInputElement extends HTMLInputElement {
|
||||
append: ddeAppend<ddeHTMLInputElement>;
|
||||
}
|
||||
export interface ddeHTMLLabelElement extends HTMLLabelElement {
|
||||
append: ddeAppend<ddeHTMLLabelElement>;
|
||||
}
|
||||
export interface ddeHTMLLegendElement extends HTMLLegendElement {
|
||||
append: ddeAppend<ddeHTMLLegendElement>;
|
||||
}
|
||||
export interface ddeHTMLLIElement extends HTMLLIElement {
|
||||
append: ddeAppend<ddeHTMLLIElement>;
|
||||
}
|
||||
export interface ddeHTMLLinkElement extends HTMLLinkElement {
|
||||
append: ddeAppend<ddeHTMLLinkElement>;
|
||||
}
|
||||
export interface ddeHTMLMapElement extends HTMLMapElement {
|
||||
append: ddeAppend<ddeHTMLMapElement>;
|
||||
}
|
||||
export interface ddeHTMLMenuElement extends HTMLMenuElement {
|
||||
append: ddeAppend<ddeHTMLMenuElement>;
|
||||
}
|
||||
export interface ddeHTMLMetaElement extends HTMLMetaElement {
|
||||
append: ddeAppend<ddeHTMLMetaElement>;
|
||||
}
|
||||
export interface ddeHTMLMeterElement extends HTMLMeterElement {
|
||||
append: ddeAppend<ddeHTMLMeterElement>;
|
||||
}
|
||||
export interface ddeHTMLObjectElement extends HTMLObjectElement {
|
||||
append: ddeAppend<ddeHTMLObjectElement>;
|
||||
}
|
||||
export interface ddeHTMLOListElement extends HTMLOListElement {
|
||||
append: ddeAppend<ddeHTMLOListElement>;
|
||||
}
|
||||
export interface ddeHTMLOptGroupElement extends HTMLOptGroupElement {
|
||||
append: ddeAppend<ddeHTMLOptGroupElement>;
|
||||
}
|
||||
export interface ddeHTMLOptionElement extends HTMLOptionElement {
|
||||
append: ddeAppend<ddeHTMLOptionElement>;
|
||||
}
|
||||
export interface ddeHTMLOutputElement extends HTMLOutputElement {
|
||||
append: ddeAppend<ddeHTMLOutputElement>;
|
||||
}
|
||||
export interface ddeHTMLParagraphElement extends HTMLParagraphElement {
|
||||
append: ddeAppend<ddeHTMLParagraphElement>;
|
||||
}
|
||||
export interface ddeHTMLPictureElement extends HTMLPictureElement {
|
||||
append: ddeAppend<ddeHTMLPictureElement>;
|
||||
}
|
||||
export interface ddeHTMLPreElement extends HTMLPreElement {
|
||||
append: ddeAppend<ddeHTMLPreElement>;
|
||||
}
|
||||
export interface ddeHTMLProgressElement extends HTMLProgressElement {
|
||||
append: ddeAppend<ddeHTMLProgressElement>;
|
||||
}
|
||||
export interface ddeHTMLScriptElement extends HTMLScriptElement {
|
||||
append: ddeAppend<ddeHTMLScriptElement>;
|
||||
}
|
||||
export interface ddeHTMLSelectElement extends HTMLSelectElement {
|
||||
append: ddeAppend<ddeHTMLSelectElement>;
|
||||
}
|
||||
export interface ddeHTMLSlotElement extends HTMLSlotElement {
|
||||
append: ddeAppend<ddeHTMLSlotElement>;
|
||||
}
|
||||
export interface ddeHTMLSourceElement extends HTMLSourceElement {
|
||||
append: ddeAppend<ddeHTMLSourceElement>;
|
||||
}
|
||||
export interface ddeHTMLSpanElement extends HTMLSpanElement {
|
||||
append: ddeAppend<ddeHTMLSpanElement>;
|
||||
}
|
||||
export interface ddeHTMLStyleElement extends HTMLStyleElement {
|
||||
append: ddeAppend<ddeHTMLStyleElement>;
|
||||
}
|
||||
export interface ddeHTMLTableElement extends HTMLTableElement {
|
||||
append: ddeAppend<ddeHTMLTableElement>;
|
||||
}
|
||||
export interface ddeHTMLTableSectionElement extends HTMLTableSectionElement {
|
||||
append: ddeAppend<ddeHTMLTableSectionElement>;
|
||||
}
|
||||
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
|
||||
append: ddeAppend<ddeHTMLTableCellElement>;
|
||||
}
|
||||
export interface ddeHTMLTemplateElement extends HTMLTemplateElement {
|
||||
append: ddeAppend<ddeHTMLTemplateElement>;
|
||||
}
|
||||
export interface ddeHTMLTextAreaElement extends HTMLTextAreaElement {
|
||||
append: ddeAppend<ddeHTMLTextAreaElement>;
|
||||
}
|
||||
export interface ddeHTMLTableCellElement extends HTMLTableCellElement {
|
||||
append: ddeAppend<ddeHTMLTableCellElement>;
|
||||
}
|
||||
export interface ddeHTMLTimeElement extends HTMLTimeElement {
|
||||
append: ddeAppend<ddeHTMLTimeElement>;
|
||||
}
|
||||
export interface ddeHTMLTitleElement extends HTMLTitleElement {
|
||||
append: ddeAppend<ddeHTMLTitleElement>;
|
||||
}
|
||||
export interface ddeHTMLTableRowElement extends HTMLTableRowElement {
|
||||
append: ddeAppend<ddeHTMLTableRowElement>;
|
||||
}
|
||||
export interface ddeHTMLTrackElement extends HTMLTrackElement {
|
||||
append: ddeAppend<ddeHTMLTrackElement>;
|
||||
}
|
||||
export interface ddeHTMLUListElement extends HTMLUListElement {
|
||||
append: ddeAppend<ddeHTMLUListElement>;
|
||||
}
|
||||
export interface ddeHTMLVideoElement extends HTMLVideoElement {
|
||||
append: ddeAppend<ddeHTMLVideoElement>;
|
||||
}
|
||||
export interface ddeSVGAElement extends SVGAElement {
|
||||
append: ddeAppend<ddeSVGAElement>;
|
||||
}
|
||||
export interface ddeSVGAnimateElement extends SVGAnimateElement {
|
||||
append: ddeAppend<ddeSVGAnimateElement>;
|
||||
}
|
||||
export interface ddeSVGAnimateMotionElement extends SVGAnimateMotionElement {
|
||||
append: ddeAppend<ddeSVGAnimateMotionElement>;
|
||||
}
|
||||
export interface ddeSVGAnimateTransformElement extends SVGAnimateTransformElement {
|
||||
append: ddeAppend<ddeSVGAnimateTransformElement>;
|
||||
}
|
||||
export interface ddeSVGCircleElement extends SVGCircleElement {
|
||||
append: ddeAppend<ddeSVGCircleElement>;
|
||||
}
|
||||
export interface ddeSVGClipPathElement extends SVGClipPathElement {
|
||||
append: ddeAppend<ddeSVGClipPathElement>;
|
||||
}
|
||||
export interface ddeSVGDefsElement extends SVGDefsElement {
|
||||
append: ddeAppend<ddeSVGDefsElement>;
|
||||
}
|
||||
export interface ddeSVGDescElement extends SVGDescElement {
|
||||
append: ddeAppend<ddeSVGDescElement>;
|
||||
}
|
||||
export interface ddeSVGEllipseElement extends SVGEllipseElement {
|
||||
append: ddeAppend<ddeSVGEllipseElement>;
|
||||
}
|
||||
export interface ddeSVGFEBlendElement extends SVGFEBlendElement {
|
||||
append: ddeAppend<ddeSVGFEBlendElement>;
|
||||
}
|
||||
export interface ddeSVGFEColorMatrixElement extends SVGFEColorMatrixElement {
|
||||
append: ddeAppend<ddeSVGFEColorMatrixElement>;
|
||||
}
|
||||
export interface ddeSVGFEComponentTransferElement extends SVGFEComponentTransferElement {
|
||||
append: ddeAppend<ddeSVGFEComponentTransferElement>;
|
||||
}
|
||||
export interface ddeSVGFECompositeElement extends SVGFECompositeElement {
|
||||
append: ddeAppend<ddeSVGFECompositeElement>;
|
||||
}
|
||||
export interface ddeSVGFEConvolveMatrixElement extends SVGFEConvolveMatrixElement {
|
||||
append: ddeAppend<ddeSVGFEConvolveMatrixElement>;
|
||||
}
|
||||
export interface ddeSVGFEDiffuseLightingElement extends SVGFEDiffuseLightingElement {
|
||||
append: ddeAppend<ddeSVGFEDiffuseLightingElement>;
|
||||
}
|
||||
export interface ddeSVGFEDisplacementMapElement extends SVGFEDisplacementMapElement {
|
||||
append: ddeAppend<ddeSVGFEDisplacementMapElement>;
|
||||
}
|
||||
export interface ddeSVGFEDistantLightElement extends SVGFEDistantLightElement {
|
||||
append: ddeAppend<ddeSVGFEDistantLightElement>;
|
||||
}
|
||||
export interface ddeSVGFEDropShadowElement extends SVGFEDropShadowElement {
|
||||
append: ddeAppend<ddeSVGFEDropShadowElement>;
|
||||
}
|
||||
export interface ddeSVGFEFloodElement extends SVGFEFloodElement {
|
||||
append: ddeAppend<ddeSVGFEFloodElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncAElement extends SVGFEFuncAElement {
|
||||
append: ddeAppend<ddeSVGFEFuncAElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncBElement extends SVGFEFuncBElement {
|
||||
append: ddeAppend<ddeSVGFEFuncBElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncGElement extends SVGFEFuncGElement {
|
||||
append: ddeAppend<ddeSVGFEFuncGElement>;
|
||||
}
|
||||
export interface ddeSVGFEFuncRElement extends SVGFEFuncRElement {
|
||||
append: ddeAppend<ddeSVGFEFuncRElement>;
|
||||
}
|
||||
export interface ddeSVGFEGaussianBlurElement extends SVGFEGaussianBlurElement {
|
||||
append: ddeAppend<ddeSVGFEGaussianBlurElement>;
|
||||
}
|
||||
export interface ddeSVGFEImageElement extends SVGFEImageElement {
|
||||
append: ddeAppend<ddeSVGFEImageElement>;
|
||||
}
|
||||
export interface ddeSVGFEMergeElement extends SVGFEMergeElement {
|
||||
append: ddeAppend<ddeSVGFEMergeElement>;
|
||||
}
|
||||
export interface ddeSVGFEMergeNodeElement extends SVGFEMergeNodeElement {
|
||||
append: ddeAppend<ddeSVGFEMergeNodeElement>;
|
||||
}
|
||||
export interface ddeSVGFEMorphologyElement extends SVGFEMorphologyElement {
|
||||
append: ddeAppend<ddeSVGFEMorphologyElement>;
|
||||
}
|
||||
export interface ddeSVGFEOffsetElement extends SVGFEOffsetElement {
|
||||
append: ddeAppend<ddeSVGFEOffsetElement>;
|
||||
}
|
||||
export interface ddeSVGFEPointLightElement extends SVGFEPointLightElement {
|
||||
append: ddeAppend<ddeSVGFEPointLightElement>;
|
||||
}
|
||||
export interface ddeSVGFESpecularLightingElement extends SVGFESpecularLightingElement {
|
||||
append: ddeAppend<ddeSVGFESpecularLightingElement>;
|
||||
}
|
||||
export interface ddeSVGFESpotLightElement extends SVGFESpotLightElement {
|
||||
append: ddeAppend<ddeSVGFESpotLightElement>;
|
||||
}
|
||||
export interface ddeSVGFETileElement extends SVGFETileElement {
|
||||
append: ddeAppend<ddeSVGFETileElement>;
|
||||
}
|
||||
export interface ddeSVGFETurbulenceElement extends SVGFETurbulenceElement {
|
||||
append: ddeAppend<ddeSVGFETurbulenceElement>;
|
||||
}
|
||||
export interface ddeSVGFilterElement extends SVGFilterElement {
|
||||
append: ddeAppend<ddeSVGFilterElement>;
|
||||
}
|
||||
export interface ddeSVGForeignObjectElement extends SVGForeignObjectElement {
|
||||
append: ddeAppend<ddeSVGForeignObjectElement>;
|
||||
}
|
||||
export interface ddeSVGGElement extends SVGGElement {
|
||||
append: ddeAppend<ddeSVGGElement>;
|
||||
}
|
||||
export interface ddeSVGImageElement extends SVGImageElement {
|
||||
append: ddeAppend<ddeSVGImageElement>;
|
||||
}
|
||||
export interface ddeSVGLineElement extends SVGLineElement {
|
||||
append: ddeAppend<ddeSVGLineElement>;
|
||||
}
|
||||
export interface ddeSVGLinearGradientElement extends SVGLinearGradientElement {
|
||||
append: ddeAppend<ddeSVGLinearGradientElement>;
|
||||
}
|
||||
export interface ddeSVGMarkerElement extends SVGMarkerElement {
|
||||
append: ddeAppend<ddeSVGMarkerElement>;
|
||||
}
|
||||
export interface ddeSVGMaskElement extends SVGMaskElement {
|
||||
append: ddeAppend<ddeSVGMaskElement>;
|
||||
}
|
||||
export interface ddeSVGMetadataElement extends SVGMetadataElement {
|
||||
append: ddeAppend<ddeSVGMetadataElement>;
|
||||
}
|
||||
export interface ddeSVGMPathElement extends SVGMPathElement {
|
||||
append: ddeAppend<ddeSVGMPathElement>;
|
||||
}
|
||||
export interface ddeSVGPathElement extends SVGPathElement {
|
||||
append: ddeAppend<ddeSVGPathElement>;
|
||||
}
|
||||
export interface ddeSVGPatternElement extends SVGPatternElement {
|
||||
append: ddeAppend<ddeSVGPatternElement>;
|
||||
}
|
||||
export interface ddeSVGPolygonElement extends SVGPolygonElement {
|
||||
append: ddeAppend<ddeSVGPolygonElement>;
|
||||
}
|
||||
export interface ddeSVGPolylineElement extends SVGPolylineElement {
|
||||
append: ddeAppend<ddeSVGPolylineElement>;
|
||||
}
|
||||
export interface ddeSVGRadialGradientElement extends SVGRadialGradientElement {
|
||||
append: ddeAppend<ddeSVGRadialGradientElement>;
|
||||
}
|
||||
export interface ddeSVGRectElement extends SVGRectElement {
|
||||
append: ddeAppend<ddeSVGRectElement>;
|
||||
}
|
||||
export interface ddeSVGScriptElement extends SVGScriptElement {
|
||||
append: ddeAppend<ddeSVGScriptElement>;
|
||||
}
|
||||
export interface ddeSVGSetElement extends SVGSetElement {
|
||||
append: ddeAppend<ddeSVGSetElement>;
|
||||
}
|
||||
export interface ddeSVGStopElement extends SVGStopElement {
|
||||
append: ddeAppend<ddeSVGStopElement>;
|
||||
}
|
||||
export interface ddeSVGStyleElement extends SVGStyleElement {
|
||||
append: ddeAppend<ddeSVGStyleElement>;
|
||||
}
|
||||
export interface ddeSVGSVGElement extends SVGSVGElement {
|
||||
append: ddeAppend<ddeSVGSVGElement>;
|
||||
}
|
||||
export interface ddeSVGSwitchElement extends SVGSwitchElement {
|
||||
append: ddeAppend<ddeSVGSwitchElement>;
|
||||
}
|
||||
export interface ddeSVGSymbolElement extends SVGSymbolElement {
|
||||
append: ddeAppend<ddeSVGSymbolElement>;
|
||||
}
|
||||
export interface ddeSVGTextElement extends SVGTextElement {
|
||||
append: ddeAppend<ddeSVGTextElement>;
|
||||
}
|
||||
export interface ddeSVGTextPathElement extends SVGTextPathElement {
|
||||
append: ddeAppend<ddeSVGTextPathElement>;
|
||||
}
|
||||
export interface ddeSVGTitleElement extends SVGTitleElement {
|
||||
append: ddeAppend<ddeSVGTitleElement>;
|
||||
}
|
||||
export interface ddeSVGTSpanElement extends SVGTSpanElement {
|
||||
append: ddeAppend<ddeSVGTSpanElement>;
|
||||
}
|
||||
export interface ddeSVGUseElement extends SVGUseElement {
|
||||
append: ddeAppend<ddeSVGUseElement>;
|
||||
}
|
||||
export interface ddeSVGViewElement extends SVGViewElement {
|
||||
append: ddeAppend<ddeSVGViewElement>;
|
||||
}
|
||||
|
||||
export {
|
||||
dispatchEvent$1 as dispatchEvent,
|
||||
el as createElement,
|
||||
elNS as createElementNS,
|
||||
};
|
||||
|
||||
export as namespace iife;
|
||||
|
||||
export {};
|
703
dist/iife.js
vendored
Normal file
703
dist/iife.js
vendored
Normal file
@ -0,0 +1,703 @@
|
||||
var DDE = (() => {
|
||||
var __defProp = Object.defineProperty;
|
||||
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||
var __export = (target, all) => {
|
||||
for (var name in all)
|
||||
__defProp(target, name, { get: all[name], enumerable: true });
|
||||
};
|
||||
var __copyProps = (to, from, except, desc) => {
|
||||
if (from && typeof from === "object" || typeof from === "function") {
|
||||
for (let key of __getOwnPropNames(from))
|
||||
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||
}
|
||||
return to;
|
||||
};
|
||||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||
|
||||
// index.js
|
||||
var index_exports = {};
|
||||
__export(index_exports, {
|
||||
assign: () => assign,
|
||||
assignAttribute: () => assignAttribute,
|
||||
chainableAppend: () => chainableAppend,
|
||||
classListDeclarative: () => classListDeclarative,
|
||||
createElement: () => createElement,
|
||||
createElementNS: () => createElementNS,
|
||||
customElementRender: () => customElementRender,
|
||||
customElementWithDDE: () => lifecyclesToEvents,
|
||||
dispatchEvent: () => dispatchEvent,
|
||||
el: () => createElement,
|
||||
elNS: () => createElementNS,
|
||||
lifecyclesToEvents: () => lifecyclesToEvents,
|
||||
memo: () => memo,
|
||||
on: () => on,
|
||||
queue: () => queue,
|
||||
registerReactivity: () => registerReactivity,
|
||||
scope: () => scope,
|
||||
simulateSlots: () => simulateSlots
|
||||
});
|
||||
|
||||
// src/helpers.js
|
||||
var hasOwn = (...a) => Object.prototype.hasOwnProperty.call(...a);
|
||||
function isUndef(value) {
|
||||
return typeof value === "undefined";
|
||||
}
|
||||
function isInstance(obj, cls) {
|
||||
return obj instanceof cls;
|
||||
}
|
||||
function isProtoFrom(obj, cls) {
|
||||
return Object.prototype.isPrototypeOf.call(cls, obj);
|
||||
}
|
||||
function oCreate(proto = null, p = {}) {
|
||||
return Object.create(proto, p);
|
||||
}
|
||||
function oAssign(...o) {
|
||||
return Object.assign(...o);
|
||||
}
|
||||
function onAbort(signal, listener) {
|
||||
if (!signal || !isInstance(signal, AbortSignal))
|
||||
return true;
|
||||
if (signal.aborted)
|
||||
return;
|
||||
signal.addEventListener("abort", listener);
|
||||
return function cleanUp() {
|
||||
signal.removeEventListener("abort", listener);
|
||||
};
|
||||
}
|
||||
function requestIdle() {
|
||||
return new Promise(function(resolve) {
|
||||
(globalThis.requestIdleCallback || requestAnimationFrame)(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
// src/dom-lib/common.js
|
||||
var enviroment = {
|
||||
setDeleteAttr,
|
||||
ssr: "",
|
||||
D: globalThis.document,
|
||||
N: globalThis.Node,
|
||||
F: globalThis.DocumentFragment,
|
||||
H: globalThis.HTMLElement,
|
||||
S: globalThis.SVGElement,
|
||||
M: globalThis.MutationObserver,
|
||||
q: (p) => p || Promise.resolve()
|
||||
};
|
||||
function setDeleteAttr(obj, prop, val) {
|
||||
Reflect.set(obj, prop, val);
|
||||
if (!isUndef(val)) return;
|
||||
Reflect.deleteProperty(obj, prop);
|
||||
if (isInstance(obj, enviroment.H) && obj.getAttribute(prop) === "undefined")
|
||||
return obj.removeAttribute(prop);
|
||||
if (Reflect.get(obj, prop) === "undefined")
|
||||
return Reflect.set(obj, prop, "");
|
||||
}
|
||||
var keyLTE = "__dde_lifecyclesToEvents";
|
||||
var evc = "dde:connected";
|
||||
var evd = "dde:disconnected";
|
||||
var eva = "dde:attributeChanged";
|
||||
|
||||
// src/dom-lib/events-observer.js
|
||||
var c_ch_o = enviroment.M ? connectionsChangesObserverConstructor() : new Proxy({}, {
|
||||
get() {
|
||||
return () => {
|
||||
};
|
||||
}
|
||||
});
|
||||
function connectionsChangesObserverConstructor() {
|
||||
const store = /* @__PURE__ */ new Map();
|
||||
let is_observing = false;
|
||||
const observerListener = (stop2) => function(mutations) {
|
||||
for (const mutation of mutations) {
|
||||
if (mutation.type !== "childList") continue;
|
||||
if (observerAdded(mutation.addedNodes, true)) {
|
||||
stop2();
|
||||
continue;
|
||||
}
|
||||
if (observerRemoved(mutation.removedNodes, true))
|
||||
stop2();
|
||||
}
|
||||
};
|
||||
const observer = new enviroment.M(observerListener(stop));
|
||||
return {
|
||||
/**
|
||||
* Creates an observer for a specific element
|
||||
* @param {Element} element - Element to observe
|
||||
* @returns {Function} Cleanup function
|
||||
*/
|
||||
observe(element) {
|
||||
const o = new enviroment.M(observerListener(() => {
|
||||
}));
|
||||
o.observe(element, { childList: true, subtree: true });
|
||||
return () => o.disconnect();
|
||||
},
|
||||
/**
|
||||
* Register a connection listener for an element
|
||||
* @param {Element} element - Element to watch
|
||||
* @param {Function} listener - Callback for connection event
|
||||
*/
|
||||
onConnected(element, listener) {
|
||||
start();
|
||||
const listeners = getElementStore(element);
|
||||
if (listeners.connected.has(listener)) return;
|
||||
listeners.connected.add(listener);
|
||||
listeners.length_c += 1;
|
||||
},
|
||||
/**
|
||||
* Unregister a connection listener
|
||||
* @param {Element} element - Element being watched
|
||||
* @param {Function} listener - Callback to remove
|
||||
*/
|
||||
offConnected(element, listener) {
|
||||
if (!store.has(element)) return;
|
||||
const ls = store.get(element);
|
||||
if (!ls.connected.has(listener)) return;
|
||||
ls.connected.delete(listener);
|
||||
ls.length_c -= 1;
|
||||
cleanWhenOff(element, ls);
|
||||
},
|
||||
/**
|
||||
* Register a disconnection listener for an element
|
||||
* @param {Element} element - Element to watch
|
||||
* @param {Function} listener - Callback for disconnection event
|
||||
*/
|
||||
onDisconnected(element, listener) {
|
||||
start();
|
||||
const listeners = getElementStore(element);
|
||||
if (listeners.disconnected.has(listener)) return;
|
||||
listeners.disconnected.add(listener);
|
||||
listeners.length_d += 1;
|
||||
},
|
||||
/**
|
||||
* Unregister a disconnection listener
|
||||
* @param {Element} element - Element being watched
|
||||
* @param {Function} listener - Callback to remove
|
||||
*/
|
||||
offDisconnected(element, listener) {
|
||||
if (!store.has(element)) return;
|
||||
const ls = store.get(element);
|
||||
ls.disconnected.delete(listener);
|
||||
ls.length_d -= 1;
|
||||
cleanWhenOff(element, ls);
|
||||
}
|
||||
};
|
||||
function cleanWhenOff(element, ls) {
|
||||
if (ls.length_c || ls.length_d)
|
||||
return;
|
||||
store.delete(element);
|
||||
stop();
|
||||
}
|
||||
function getElementStore(element) {
|
||||
if (store.has(element)) return store.get(element);
|
||||
const out = {
|
||||
connected: /* @__PURE__ */ new WeakSet(),
|
||||
length_c: 0,
|
||||
disconnected: /* @__PURE__ */ new WeakSet(),
|
||||
length_d: 0
|
||||
};
|
||||
store.set(element, out);
|
||||
return out;
|
||||
}
|
||||
function start() {
|
||||
if (is_observing) return;
|
||||
is_observing = true;
|
||||
observer.observe(enviroment.D.body, { childList: true, subtree: true });
|
||||
}
|
||||
function stop() {
|
||||
if (!is_observing || store.size) return;
|
||||
is_observing = false;
|
||||
observer.disconnect();
|
||||
}
|
||||
async function collectChildren(element) {
|
||||
if (store.size > 30)
|
||||
await requestIdle();
|
||||
const out = [];
|
||||
if (!isInstance(element, enviroment.N)) return out;
|
||||
for (const el of store.keys()) {
|
||||
if (el === element || !isInstance(el, enviroment.N)) continue;
|
||||
if (element.contains(el))
|
||||
out.push(el);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function observerAdded(addedNodes, is_root) {
|
||||
let out = false;
|
||||
for (const element of addedNodes) {
|
||||
if (is_root) collectChildren(element).then(observerAdded);
|
||||
if (!store.has(element)) continue;
|
||||
const ls = store.get(element);
|
||||
if (!ls.length_c) continue;
|
||||
element.dispatchEvent(new Event(evc));
|
||||
ls.connected = /* @__PURE__ */ new WeakSet();
|
||||
ls.length_c = 0;
|
||||
if (!ls.length_d) store.delete(element);
|
||||
out = true;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function observerRemoved(removedNodes, is_root) {
|
||||
let out = false;
|
||||
for (const element of removedNodes) {
|
||||
if (is_root) collectChildren(element).then(observerRemoved);
|
||||
if (!store.has(element)) continue;
|
||||
const ls = store.get(element);
|
||||
if (!ls.length_d) continue;
|
||||
(globalThis.queueMicrotask || setTimeout)(dispatchRemove(element));
|
||||
out = true;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function dispatchRemove(element) {
|
||||
return () => {
|
||||
if (element.isConnected) return;
|
||||
element.dispatchEvent(new Event(evd));
|
||||
store.delete(element);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// src/dom-lib/events.js
|
||||
function dispatchEvent(name, options, host) {
|
||||
if (typeof options === "function") {
|
||||
host = options;
|
||||
options = null;
|
||||
}
|
||||
if (!options) options = {};
|
||||
return function dispatch(element, ...d) {
|
||||
if (host) {
|
||||
d.unshift(element);
|
||||
element = typeof host === "function" ? host() : host;
|
||||
}
|
||||
const event = d.length ? new CustomEvent(name, oAssign({ detail: d[0] }, options)) : new Event(name, options);
|
||||
return element.dispatchEvent(event);
|
||||
};
|
||||
}
|
||||
function on(event, listener, options) {
|
||||
return function registerElement(element) {
|
||||
element.addEventListener(event, listener, options);
|
||||
return element;
|
||||
};
|
||||
}
|
||||
on.defer = (fn) => setTimeout.bind(null, fn, 0);
|
||||
var lifeOptions = (obj) => oAssign({}, typeof obj === "object" ? obj : null, { once: true });
|
||||
on.connected = function(listener, options) {
|
||||
options = lifeOptions(options);
|
||||
return function registerElement(element) {
|
||||
element.addEventListener(evc, listener, options);
|
||||
if (element[keyLTE]) return element;
|
||||
if (element.isConnected) return element.dispatchEvent(new Event(evc)), element;
|
||||
const c = onAbort(options.signal, () => c_ch_o.offConnected(element, listener));
|
||||
if (c) c_ch_o.onConnected(element, listener);
|
||||
return element;
|
||||
};
|
||||
};
|
||||
on.disconnected = function(listener, options) {
|
||||
options = lifeOptions(options);
|
||||
return function registerElement(element) {
|
||||
element.addEventListener(evd, listener, options);
|
||||
if (element[keyLTE]) return element;
|
||||
const c = onAbort(options.signal, () => c_ch_o.offDisconnected(element, listener));
|
||||
if (c) c_ch_o.onDisconnected(element, listener);
|
||||
return element;
|
||||
};
|
||||
};
|
||||
|
||||
// src/dom-lib/scopes.js
|
||||
var scopes = [{
|
||||
get scope() {
|
||||
return enviroment.D.body;
|
||||
},
|
||||
host: (c) => c ? c(enviroment.D.body) : enviroment.D.body,
|
||||
prevent: true
|
||||
}];
|
||||
var store_abort = /* @__PURE__ */ new WeakMap();
|
||||
var scope = {
|
||||
/**
|
||||
* Gets the current scope
|
||||
* @returns {typeof scopes[number]} Current scope context
|
||||
*/
|
||||
get current() {
|
||||
return scopes[scopes.length - 1];
|
||||
},
|
||||
/**
|
||||
* Gets the host element of the current scope
|
||||
* @returns {Function} Host accessor function
|
||||
*/
|
||||
get host() {
|
||||
return this.current.host;
|
||||
},
|
||||
/**
|
||||
* Creates/gets an AbortController that triggers when the element disconnects
|
||||
* */
|
||||
get signal() {
|
||||
const { host } = this;
|
||||
if (store_abort.has(host)) return store_abort.get(host);
|
||||
const a = new AbortController();
|
||||
store_abort.set(host, a);
|
||||
host(on.disconnected(() => a.abort()));
|
||||
return a.signal;
|
||||
},
|
||||
/**
|
||||
* Prevents default behavior in the current scope
|
||||
* @returns {Object} Current scope context
|
||||
*/
|
||||
preventDefault() {
|
||||
const { current } = this;
|
||||
current.prevent = true;
|
||||
return current;
|
||||
},
|
||||
/**
|
||||
* Gets a copy of the current scope stack
|
||||
* @returns {Array} Copy of scope stack
|
||||
*/
|
||||
get state() {
|
||||
return [...scopes];
|
||||
},
|
||||
/**
|
||||
* Pushes a new scope to the stack
|
||||
* @param {Object} [s={}] - Scope object to push
|
||||
* @returns {number} New length of the scope stack
|
||||
*/
|
||||
push(s = {}) {
|
||||
return scopes.push(oAssign({}, this.current, { prevent: false }, s));
|
||||
},
|
||||
/**
|
||||
* Pushes the root scope to the stack
|
||||
* @returns {number} New length of the scope stack
|
||||
*/
|
||||
pushRoot() {
|
||||
return scopes.push(scopes[0]);
|
||||
},
|
||||
/**
|
||||
* Pops the current scope from the stack
|
||||
* @returns {Object|undefined} Popped scope or undefined if only one scope remains
|
||||
*/
|
||||
pop() {
|
||||
if (scopes.length === 1) return;
|
||||
return scopes.pop();
|
||||
}
|
||||
};
|
||||
|
||||
// src/signals-lib/common.js
|
||||
var signals_global = {
|
||||
/**
|
||||
* Checks if a value is a signal
|
||||
* @param {any} attributes - Value to check
|
||||
* @returns {boolean} Whether the value is a signal
|
||||
*/
|
||||
isSignal(attributes) {
|
||||
return false;
|
||||
},
|
||||
/**
|
||||
* Processes an attribute that might be reactive
|
||||
* @param {Element} obj - Element that owns the attribute
|
||||
* @param {string} key - Attribute name
|
||||
* @param {any} attr - Attribute value
|
||||
* @param {Function} set - Function to set the attribute
|
||||
* @returns {any} Processed attribute value
|
||||
*/
|
||||
processReactiveAttribute(obj, key, attr, set) {
|
||||
return attr;
|
||||
}
|
||||
};
|
||||
function registerReactivity(def, global = true) {
|
||||
if (global) return oAssign(signals_global, def);
|
||||
Object.setPrototypeOf(def, signals_global);
|
||||
return def;
|
||||
}
|
||||
function signals(_this) {
|
||||
return isProtoFrom(_this, signals_global) && _this !== signals_global ? _this : signals_global;
|
||||
}
|
||||
|
||||
// src/dom-lib/helpers.js
|
||||
function setRemove(obj, prop, key, val) {
|
||||
return obj[(isUndef(val) ? "remove" : "set") + prop](key, val);
|
||||
}
|
||||
function setRemoveNS(obj, prop, key, val, ns = null) {
|
||||
return obj[(isUndef(val) ? "remove" : "set") + prop + "NS"](ns, key, val);
|
||||
}
|
||||
function setDelete(obj, key, val) {
|
||||
Reflect.set(obj, key, val);
|
||||
if (!isUndef(val)) return;
|
||||
return Reflect.deleteProperty(obj, key);
|
||||
}
|
||||
function elementAttribute(element, op, key, value) {
|
||||
if (isInstance(element, enviroment.H))
|
||||
return element[op + "Attribute"](key, value);
|
||||
return element[op + "AttributeNS"](null, key, value);
|
||||
}
|
||||
|
||||
// src/dom-lib/el.js
|
||||
function queue(promise) {
|
||||
return enviroment.q(promise);
|
||||
}
|
||||
function append(...els) {
|
||||
this.appendOriginal(...els);
|
||||
return this;
|
||||
}
|
||||
function chainableAppend(el) {
|
||||
if (el.append === append) return el;
|
||||
el.appendOriginal = el.append;
|
||||
el.append = append;
|
||||
return el;
|
||||
}
|
||||
var namespace;
|
||||
function createElement(tag, attributes, ...addons) {
|
||||
const s = signals(this);
|
||||
let scoped = 0;
|
||||
let el, el_host;
|
||||
const att_type = typeof attributes;
|
||||
if (att_type === "string" || att_type === "number" || s.isSignal(attributes))
|
||||
attributes = { textContent: attributes };
|
||||
switch (true) {
|
||||
case typeof tag === "function": {
|
||||
scoped = 1;
|
||||
const host = (...c) => !c.length ? el_host : (scoped === 1 ? addons.unshift(...c) : c.forEach((c2) => c2(el_host)), void 0);
|
||||
scope.push({ scope: tag, host });
|
||||
el = /** @type {Element} */
|
||||
tag(attributes || void 0);
|
||||
if (el.nodeName === "#comment") break;
|
||||
const is_fragment = isInstance(el, enviroment.F);
|
||||
const el_mark = createElement.mark({
|
||||
type: "component",
|
||||
name: tag.name,
|
||||
host: is_fragment ? "this" : "parentElement"
|
||||
});
|
||||
el.prepend(el_mark);
|
||||
if (is_fragment) el_host = el_mark;
|
||||
break;
|
||||
}
|
||||
case tag === "#text":
|
||||
el = assign.call(this, enviroment.D.createTextNode(""), attributes);
|
||||
break;
|
||||
case (tag === "<>" || !tag):
|
||||
el = assign.call(this, enviroment.D.createDocumentFragment(), attributes);
|
||||
break;
|
||||
case Boolean(namespace):
|
||||
el = assign.call(this, enviroment.D.createElementNS(namespace, tag), attributes);
|
||||
break;
|
||||
case !el:
|
||||
el = assign.call(this, enviroment.D.createElement(tag), attributes);
|
||||
}
|
||||
chainableAppend(el);
|
||||
if (!el_host) el_host = el;
|
||||
addons.forEach((c) => c(el_host));
|
||||
if (scoped) scope.pop();
|
||||
scoped = 2;
|
||||
return el;
|
||||
}
|
||||
createElement.mark = function(attrs, is_open = false) {
|
||||
attrs = Object.entries(attrs).map(([n, v]) => n + `="${v}"`).join(" ");
|
||||
const end = is_open ? "" : "/";
|
||||
const out = enviroment.D.createComment(`<dde:mark ${attrs}${enviroment.ssr}${end}>`);
|
||||
if (is_open) out.end = enviroment.D.createComment("</dde:mark>");
|
||||
return out;
|
||||
};
|
||||
function createElementNS(ns) {
|
||||
const _this = this;
|
||||
return function createElementNSCurried(...rest) {
|
||||
namespace = ns;
|
||||
const el = createElement.call(_this, ...rest);
|
||||
namespace = void 0;
|
||||
return el;
|
||||
};
|
||||
}
|
||||
var assign_context = /* @__PURE__ */ new WeakMap();
|
||||
var { setDeleteAttr: setDeleteAttr2 } = enviroment;
|
||||
function assign(element, ...attributes) {
|
||||
if (!attributes.length) return element;
|
||||
assign_context.set(element, assignContext(element, this));
|
||||
for (const [key, value] of Object.entries(oAssign({}, ...attributes)))
|
||||
assignAttribute.call(this, element, key, value);
|
||||
assign_context.delete(element);
|
||||
return element;
|
||||
}
|
||||
function assignAttribute(element, key, value) {
|
||||
const { setRemoveAttr, s } = assignContext(element, this);
|
||||
const _this = this;
|
||||
value = s.processReactiveAttribute(
|
||||
element,
|
||||
key,
|
||||
value,
|
||||
(key2, value2) => assignAttribute.call(_this, element, key2, value2)
|
||||
);
|
||||
const [k] = key;
|
||||
if ("=" === k) return setRemoveAttr(key.slice(1), value);
|
||||
if ("." === k) return setDelete(element, key.slice(1), value);
|
||||
if (/(aria|data)([A-Z])/.test(key)) {
|
||||
key = key.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
||||
return setRemoveAttr(key, value);
|
||||
}
|
||||
if ("className" === key) key = "class";
|
||||
switch (key) {
|
||||
case "xlink:href":
|
||||
return setRemoveAttr(key, value, "http://www.w3.org/1999/xlink");
|
||||
case "textContent":
|
||||
return setDeleteAttr2(element, key, value);
|
||||
case "style":
|
||||
if (typeof value !== "object") break;
|
||||
/* falls through */
|
||||
case "dataset":
|
||||
return forEachEntries(s, key, element, value, setDelete.bind(null, element[key]));
|
||||
case "ariaset":
|
||||
return forEachEntries(s, key, element, value, (key2, val) => setRemoveAttr("aria-" + key2, val));
|
||||
case "classList":
|
||||
return classListDeclarative.call(_this, element, value);
|
||||
}
|
||||
return isPropSetter(element, key) ? setDeleteAttr2(element, key, value) : setRemoveAttr(key, value);
|
||||
}
|
||||
function assignContext(element, _this) {
|
||||
if (assign_context.has(element)) return assign_context.get(element);
|
||||
const is_svg = isInstance(element, enviroment.S);
|
||||
const setRemoveAttr = (is_svg ? setRemoveNS : setRemove).bind(null, element, "Attribute");
|
||||
const s = signals(_this);
|
||||
return { setRemoveAttr, s };
|
||||
}
|
||||
function classListDeclarative(element, toggle) {
|
||||
const s = signals(this);
|
||||
forEachEntries(
|
||||
s,
|
||||
"classList",
|
||||
element,
|
||||
toggle,
|
||||
(class_name, val) => element.classList.toggle(class_name, val === -1 ? void 0 : Boolean(val))
|
||||
);
|
||||
return element;
|
||||
}
|
||||
function isPropSetter(el, key) {
|
||||
if (!(key in el)) return false;
|
||||
const des = getPropDescriptor(el, key);
|
||||
return !isUndef(des.set);
|
||||
}
|
||||
function getPropDescriptor(p, key) {
|
||||
p = Object.getPrototypeOf(p);
|
||||
if (!p) return {};
|
||||
const des = Object.getOwnPropertyDescriptor(p, key);
|
||||
if (!des) return getPropDescriptor(p, key);
|
||||
return des;
|
||||
}
|
||||
function forEachEntries(s, target, element, obj, cb) {
|
||||
const S = String;
|
||||
if (typeof obj !== "object" || obj === null) return;
|
||||
return Object.entries(obj).forEach(function process([key, val]) {
|
||||
if (!key) return;
|
||||
key = new S(key);
|
||||
key.target = target;
|
||||
val = s.processReactiveAttribute(element, key, val, cb);
|
||||
cb(key, val);
|
||||
});
|
||||
}
|
||||
|
||||
// src/dom-lib/customElement.js
|
||||
function simulateSlots(element, root = element) {
|
||||
const mark_e = "\xB9\u2070", mark_s = "\u2713";
|
||||
const slots = Object.fromEntries(
|
||||
Array.from(root.querySelectorAll("slot")).filter((s) => !s.name.endsWith(mark_e)).map((s) => [s.name += mark_e, s])
|
||||
);
|
||||
element.append = new Proxy(element.append, {
|
||||
apply(orig, _, els) {
|
||||
if (els[0] === root) return orig.apply(element, els);
|
||||
for (const el of els) {
|
||||
const name = (el.slot || "") + mark_e;
|
||||
try {
|
||||
elementAttribute(el, "remove", "slot");
|
||||
} catch (_error) {
|
||||
}
|
||||
const slot = slots[name];
|
||||
if (!slot) return;
|
||||
if (!slot.name.startsWith(mark_s)) {
|
||||
slot.childNodes.forEach((c) => c.remove());
|
||||
slot.name = mark_s + name;
|
||||
}
|
||||
slot.append(el);
|
||||
}
|
||||
element.append = orig;
|
||||
return element;
|
||||
}
|
||||
});
|
||||
if (element !== root) {
|
||||
const els = Array.from(element.childNodes);
|
||||
element.append(...els);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
function customElementRender(target, render, props = {}) {
|
||||
const custom_element = target.host || target;
|
||||
scope.push({
|
||||
scope: custom_element,
|
||||
host: (...c) => c.length ? c.forEach((c2) => c2(custom_element)) : custom_element
|
||||
});
|
||||
if (typeof props === "function") props = props.call(custom_element, custom_element);
|
||||
const is_lte = custom_element[keyLTE];
|
||||
if (!is_lte) lifecyclesToEvents(custom_element);
|
||||
const out = render.call(custom_element, props);
|
||||
if (!is_lte) custom_element.dispatchEvent(new Event(evc));
|
||||
if (target.nodeType === 11 && typeof target.mode === "string")
|
||||
custom_element.addEventListener(evd, c_ch_o.observe(target), { once: true });
|
||||
scope.pop();
|
||||
return target.append(out);
|
||||
}
|
||||
function lifecyclesToEvents(class_declaration) {
|
||||
wrapMethod(class_declaration.prototype, "connectedCallback", function(target, thisArg, detail) {
|
||||
target.apply(thisArg, detail);
|
||||
thisArg.dispatchEvent(new Event(evc));
|
||||
});
|
||||
wrapMethod(class_declaration.prototype, "disconnectedCallback", function(target, thisArg, detail) {
|
||||
target.apply(thisArg, detail);
|
||||
(globalThis.queueMicrotask || setTimeout)(
|
||||
() => !thisArg.isConnected && thisArg.dispatchEvent(new Event(evd))
|
||||
);
|
||||
});
|
||||
wrapMethod(class_declaration.prototype, "attributeChangedCallback", function(target, thisArg, detail) {
|
||||
const [attribute, , value] = detail;
|
||||
thisArg.dispatchEvent(new CustomEvent(eva, {
|
||||
detail: [attribute, value]
|
||||
}));
|
||||
target.apply(thisArg, detail);
|
||||
});
|
||||
class_declaration.prototype[keyLTE] = true;
|
||||
return class_declaration;
|
||||
}
|
||||
function wrapMethod(obj, method, apply) {
|
||||
obj[method] = new Proxy(obj[method] || (() => {
|
||||
}), { apply });
|
||||
}
|
||||
|
||||
// src/memo.js
|
||||
var memoMark = "__dde_memo";
|
||||
var memo_scope = [];
|
||||
function memo(key, generator) {
|
||||
if (!memo_scope.length) return generator(key);
|
||||
const k = typeof key === "object" ? JSON.stringify(key) : key;
|
||||
const [{ cache, after }] = memo_scope;
|
||||
return after(k, hasOwn(cache, k) ? cache[k] : generator(key));
|
||||
}
|
||||
memo.isScope = function(obj) {
|
||||
return obj[memoMark];
|
||||
};
|
||||
memo.scope = function memoScopeCreate(fun, { signal, onlyLast } = {}) {
|
||||
let cache = oCreate();
|
||||
function memoScope(...args) {
|
||||
if (signal && signal.aborted)
|
||||
return fun.apply(this, args);
|
||||
let cache_local = onlyLast ? cache : oCreate();
|
||||
memo_scope.unshift({
|
||||
cache,
|
||||
after(key, val) {
|
||||
return cache_local[key] = val;
|
||||
}
|
||||
});
|
||||
const out = fun.apply(this, args);
|
||||
memo_scope.shift();
|
||||
cache = cache_local;
|
||||
return out;
|
||||
}
|
||||
memoScope[memoMark] = true;
|
||||
memoScope.clear = () => cache = oCreate();
|
||||
if (signal) signal.addEventListener("abort", memoScope.clear);
|
||||
return memoScope;
|
||||
};
|
||||
return __toCommonJS(index_exports);
|
||||
})();
|
861
dist/iife.min.d.ts
vendored
Normal file
861
dist/iife.min.d.ts
vendored
Normal 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
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
BIN
docs/assets/devtools.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 145 KiB |
60
docs/assets/favicon.svg
Normal file
60
docs/assets/favicon.svg
Normal 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
92
docs/assets/logo.svg
Normal 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 |
@ -1,31 +1,185 @@
|
||||
import { registerClientFile, styles } from "../ssr.js";
|
||||
import { page_id, 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";
|
||||
/**
|
||||
* @typedef {"js"|"ts"|"html"|"css"|"shell"|"-"} Language
|
||||
* */
|
||||
/**
|
||||
* Prints code to the page and registers flems to make it interactive.
|
||||
* @param {object} attrs
|
||||
@ -33,31 +187,49 @@ import { el } from "deka-dom-el";
|
||||
* @param {string} [attrs.className]
|
||||
* @param {URL} [attrs.src] Example code file path
|
||||
* @param {string} [attrs.content] Example code
|
||||
* @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
|
||||
* @param {string} [attrs.page_id] ID of the page, if setted it registers shiki
|
||||
* @param {Language} [attrs.language="-s"] Language of the code
|
||||
* */
|
||||
export function code({ id, src, content, language= "js", className= host.slice(1), page_id }){
|
||||
if(src) content= s.cat(src);
|
||||
export function code({ id, src, content, language= "-", className= host.slice(1) }){
|
||||
if(src){
|
||||
content= s.cat(src);
|
||||
if(language=== "-") language= /** @type {Language} */(src.pathname.split(".").pop());
|
||||
}
|
||||
content= normalizeIndentation(content);
|
||||
let dataJS;
|
||||
if(page_id){
|
||||
registerClientPart(page_id);
|
||||
if(language!== "-"){
|
||||
registerClientPart();
|
||||
dataJS= "todo";
|
||||
}
|
||||
return el("div", { id, className, dataJS }).append(
|
||||
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){
|
||||
function registerClientPart(){
|
||||
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");
|
||||
}
|
||||
|
@ -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";
|
||||
});
|
||||
}
|
||||
|
175
docs/components/converter.html.js
Normal file
175
docs/components/converter.html.js
Normal file
@ -0,0 +1,175 @@
|
||||
import { page_id, styles } from "../ssr.js";
|
||||
|
||||
styles.css`
|
||||
#html-to-dde-converter {
|
||||
grid-column: full-main;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--bg-sidebar);
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
#html-to-dde-converter h3 {
|
||||
margin-top: 0;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
#html-to-dde-converter .description {
|
||||
color: var(--text-light);
|
||||
font-size: 0.95rem;
|
||||
margin-top: -1rem;
|
||||
}
|
||||
|
||||
#html-to-dde-converter .converter-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#html-to-dde-converter .input-group,
|
||||
#html-to-dde-converter .output-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
#html-to-dde-converter [type="number"]{
|
||||
width: 3em;
|
||||
font-variant-numeric: tabular-nums;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
#html-to-dde-converter label {
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#html-to-dde-converter .options {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
#html-to-dde-converter .option-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
#html-to-dde-converter textarea {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.9rem;
|
||||
padding: 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
border: 1px solid var(--border);
|
||||
background-color: var(--bg);
|
||||
color: var(--text);
|
||||
min-height: 200px;
|
||||
height: 25em;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
#html-to-dde-converter textarea:focus {
|
||||
outline: 2px solid var(--primary-light);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
#html-to-dde-converter .button-group {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#html-to-dde-converter button {
|
||||
padding: 0.5rem 1rem;
|
||||
border-radius: var(--border-radius);
|
||||
border: none;
|
||||
background-color: var(--primary);
|
||||
color: var(--button-text);
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
#html-to-dde-converter button:hover {
|
||||
background-color: var(--primary-dark);
|
||||
}
|
||||
|
||||
#html-to-dde-converter button.secondary {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
#html-to-dde-converter button.secondary:hover {
|
||||
background-color: var(--bg);
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
#html-to-dde-converter .copy-button {
|
||||
background-color: var(--secondary);
|
||||
}
|
||||
|
||||
#html-to-dde-converter .copy-button:hover {
|
||||
background-color: var(--secondary-dark);
|
||||
}
|
||||
|
||||
#html-to-dde-converter .status {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
#html-to-dde-converter .error {
|
||||
color: hsl(0, 100%, 60%);
|
||||
font-size: 0.9rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
/* Sample HTML examples list */
|
||||
#html-to-dde-converter .examples-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
#html-to-dde-converter .example-button {
|
||||
font-size: 0.85rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
`;
|
||||
|
||||
import { ireland } from "./ireland.html.js";
|
||||
import { el } from "deka-dom-el";
|
||||
const fileURL= url=> new URL(url, import.meta.url);
|
||||
|
||||
export function converter(){
|
||||
registerClientPart(page_id);
|
||||
return el(ireland, {
|
||||
src: fileURL("./converter.js.js"),
|
||||
exportName: "converter",
|
||||
});
|
||||
}
|
||||
|
||||
let is_registered= {};
|
||||
/** @param {string} page_id */
|
||||
function registerClientPart(page_id){
|
||||
if(is_registered[page_id]) return;
|
||||
|
||||
document.head.append(
|
||||
el("script", {
|
||||
// src: "https://unpkg.com/@beforesemicolon/html-parser/dist/client.js",
|
||||
src: "https://cdn.jsdelivr.net/npm/@beforesemicolon/html-parser/dist/client.js",
|
||||
type: "text/javascript",
|
||||
charset: "utf-8",
|
||||
defer: true
|
||||
}),
|
||||
);
|
||||
is_registered[page_id]= true;
|
||||
}
|
384
docs/components/converter.js.js
Normal file
384
docs/components/converter.js.js
Normal 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")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
@ -1,16 +1,110 @@
|
||||
import { styles } from "../ssr.js";
|
||||
import { page_id, 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,15 @@ import { relative } from "node:path";
|
||||
* @param {object} attrs
|
||||
* @param {URL} attrs.src Example code file path
|
||||
* @param {"js"|"ts"|"html"|"css"} [attrs.language="js"] Language of the code
|
||||
* @param {string} attrs.page_id ID of the page
|
||||
* @param {"normal"|"big"} [attrs.variant="normal"] Size of the example
|
||||
* */
|
||||
export function example({ src, language= "js", page_id }){
|
||||
export function example({ src, language= "js", variant= "normal" }){
|
||||
registerClientPart(page_id);
|
||||
const content= s.cat(src).toString()
|
||||
.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ' from "./esm-with-signals.js";');
|
||||
const id= "code-example-"+generateCodeId(src);
|
||||
return el().append(
|
||||
el(code, { id, content, language, className: example.name }),
|
||||
el(code, { id, content, language, className: example.name }, el=> el.dataset.variant= variant),
|
||||
elCode({ id, content, extension: "."+language })
|
||||
);
|
||||
}
|
||||
|
375
docs/components/examples/case-studies/data-dashboard.js
Normal file
375
docs/components/examples/case-studies/data-dashboard.js
Normal file
@ -0,0 +1,375 @@
|
||||
/**
|
||||
* Case Study: Data Dashboard with Charts
|
||||
*
|
||||
* This example demonstrates:
|
||||
* - Integration with a third-party charting library
|
||||
* - Data fetching and state management
|
||||
* - Responsive layout design
|
||||
* - Multiple interactive components working together
|
||||
*/
|
||||
|
||||
import { el, on } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
||||
/**
|
||||
* Data Dashboard Component with Chart Integration
|
||||
* @returns {HTMLElement} Dashboard element
|
||||
*/
|
||||
export function DataDashboard() {
|
||||
// Mock data for demonstration
|
||||
const DATA = {
|
||||
sales: [42, 58, 65, 49, 72, 85, 63, 70, 78, 89, 95, 86],
|
||||
visitors: [1420, 1620, 1750, 1850, 2100, 2400, 2250, 2500, 2750, 2900, 3100, 3200],
|
||||
conversion: [2.9, 3.5, 3.7, 2.6, 3.4, 3.5, 2.8, 2.8, 2.8, 3.1, 3.0, 2.7],
|
||||
months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
};
|
||||
const years = [2022, 2023, 2024];
|
||||
const dataTypes = [
|
||||
{ id: 'sales', label: 'Sales', unit: 'K' },
|
||||
{ id: 'visitors', label: 'Visitors', unit: '' },
|
||||
{ id: 'conversion', label: 'Conversion Rate', unit: '%' }
|
||||
];
|
||||
|
||||
// Filter options
|
||||
const selectedYear = S(2024);
|
||||
const onYearChange = on("change", e => {
|
||||
selectedYear.set(parseInt(/** @type {HTMLSelectElement} */(e.target).value));
|
||||
loadData();
|
||||
});
|
||||
const selectedDataType = S(/** @type {'sales' | 'visitors' | 'conversion'} */ ('sales'));
|
||||
const onDataTypeChange = on("click", e => {
|
||||
const type = /** @type {'sales' | 'visitors' | 'conversion'} */(
|
||||
/** @type {HTMLButtonElement} */(e.currentTarget).dataset.type);
|
||||
selectedDataType.set(type);
|
||||
});
|
||||
const currentDataType = S(() => dataTypes.find(type => type.id === selectedDataType.get()));
|
||||
const selectedData = S(() => DATA[selectedDataType.get()]);
|
||||
|
||||
// Values based on filters
|
||||
const totalValue = S(() => selectedData.get().reduce((sum, value) => sum + value, 0));
|
||||
const averageValue = S(() => {
|
||||
const data = selectedData.get();
|
||||
return data.reduce((sum, value) => sum + value, 0) / data.length;
|
||||
});
|
||||
const highestValue = S(() => Math.max(...selectedData.get()));
|
||||
|
||||
// Simulate data loading
|
||||
const isLoading = S(false);
|
||||
const error = S(null);
|
||||
function loadData() {
|
||||
isLoading.set(true);
|
||||
error.set(null);
|
||||
|
||||
// Simulate API call
|
||||
setTimeout(() => {
|
||||
if (Math.random() > 0.9) {
|
||||
// Simulate occasional error
|
||||
error.set('Failed to load data. Please try again.');
|
||||
}
|
||||
isLoading.set(false);
|
||||
}, 800);
|
||||
}
|
||||
|
||||
// Reactive chart rendering
|
||||
const chart = S(()=> {
|
||||
const chart= el("canvas", { id: "chart-canvas", width: 800, height: 400 });
|
||||
const ctx = chart.getContext('2d');
|
||||
const data = selectedData.get();
|
||||
const months = DATA.months;
|
||||
const width = chart.width;
|
||||
const height = chart.height;
|
||||
const maxValue = Math.max(...data) * 1.1;
|
||||
const barWidth = width / data.length - 10;
|
||||
|
||||
// Clear canvas
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
// Draw background grid
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = '#f0f0f0';
|
||||
ctx.lineWidth = 1;
|
||||
for(let i = 0; i < 5; i++) {
|
||||
const y = height - (height * (i / 5)) - 30;
|
||||
ctx.moveTo(50, y);
|
||||
ctx.lineTo(width - 20, y);
|
||||
|
||||
// Draw grid labels
|
||||
ctx.fillStyle = '#999';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.fillText(Math.round(maxValue * (i / 5)).toString(), 20, y + 5);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
// Draw bars
|
||||
data.forEach((value, index) => {
|
||||
const x = index * (barWidth + 10) + 60;
|
||||
const barHeight = (value / maxValue) * (height - 60);
|
||||
|
||||
// Bar
|
||||
ctx.fillStyle = '#4a90e2';
|
||||
ctx.fillRect(x, height - barHeight - 30, barWidth, barHeight);
|
||||
|
||||
// Month label
|
||||
ctx.fillStyle = '#666';
|
||||
ctx.font = '12px Arial';
|
||||
ctx.fillText(months[index], x + barWidth/2 - 10, height - 10);
|
||||
});
|
||||
|
||||
// Chart title
|
||||
ctx.fillStyle = '#333';
|
||||
ctx.font = 'bold 14px Arial';
|
||||
ctx.fillText(`${currentDataType.get().label} (${selectedYear.get()})`, width/2 - 80, 20);
|
||||
return chart;
|
||||
});
|
||||
|
||||
return el("div", { className: "dashboard" }).append(
|
||||
el("header", { className: "dashboard-header" }).append(
|
||||
el("h1", "Sales Performance Dashboard"),
|
||||
el("div", { className: "year-filter" }).append(
|
||||
el("label", { htmlFor: "yearSelect", textContent: "Select Year:" }),
|
||||
el("select", { id: "yearSelect" },
|
||||
on.defer(el=> el.value = selectedYear.get().toString()),
|
||||
onYearChange
|
||||
).append(
|
||||
...years.map(year => el("option", { value: year, textContent: year }))
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
S.el(error, errorMsg => !errorMsg
|
||||
? el()
|
||||
: el("div", { className: "error-message" }).append(
|
||||
el("p", errorMsg),
|
||||
el("button", { textContent: "Retry", type: "button" }, on("click", loadData)),
|
||||
),
|
||||
),
|
||||
|
||||
S.el(isLoading, loading => !loading
|
||||
? el()
|
||||
: el("div", { className: "loading-spinner" })
|
||||
),
|
||||
|
||||
// Main dashboard content
|
||||
el("div", { className: "dashboard-content" }).append(
|
||||
// Metrics cards
|
||||
el("div", { className: "metrics-container" }).append(
|
||||
el("div", { className: "metric-card" }).append(
|
||||
el("h3", "Total"),
|
||||
el("#text", S(() => `${totalValue.get().toLocaleString()}${currentDataType.get().unit}`)),
|
||||
),
|
||||
el("div", { className: "metric-card" }).append(
|
||||
el("h3", "Average"),
|
||||
el("#text", S(() => `${averageValue.get().toFixed(1)}${currentDataType.get().unit}`)),
|
||||
),
|
||||
el("div", { className: "metric-card" }).append(
|
||||
el("h3", "Highest"),
|
||||
el("#text", S(() => `${highestValue.get()}${currentDataType.get().unit}`)),
|
||||
),
|
||||
),
|
||||
|
||||
// Data type selection tabs
|
||||
el("div", { className: "data-type-tabs" }).append(
|
||||
...dataTypes.map(type =>
|
||||
el("button", {
|
||||
type: "button",
|
||||
className: S(() => selectedDataType.get() === type.id ? 'active' : ''),
|
||||
dataType: type.id,
|
||||
textContent: type.label
|
||||
}, onDataTypeChange)
|
||||
)
|
||||
),
|
||||
|
||||
// Chart container
|
||||
el("div", { className: "chart-container" }).append(
|
||||
S.el(chart, chart => chart)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Render the component
|
||||
document.body.append(
|
||||
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
|
||||
el(DataDashboard)
|
||||
),
|
||||
el("style", `
|
||||
.dashboard {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
padding-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.dashboard-header h1 {
|
||||
font-size: 1.5rem;
|
||||
margin: 0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.year-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.year-filter select {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.metrics-container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.metric-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.metric-card h3 {
|
||||
margin-top: 0;
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.metric-card p {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.data-type-tabs {
|
||||
display: flex;
|
||||
border-bottom: 1px solid #eee;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.data-type-tabs button {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
color: #666;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.data-type-tabs button.active {
|
||||
color: #4a90e2;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.data-type-tabs button.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background: #4a90e2;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.loading-spinner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.loading-spinner::before {
|
||||
content: '';
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid #f3f3f3;
|
||||
border-top: 4px solid #4a90e2;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #ffecec;
|
||||
color: #e74c3c;
|
||||
padding: 1rem;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.error-message p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.error-message button {
|
||||
background: #e74c3c;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metrics-container {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.year-filter {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.year-filter select {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
`)
|
||||
);
|
412
docs/components/examples/case-studies/image-gallery.js
Normal file
412
docs/components/examples/case-studies/image-gallery.js
Normal file
@ -0,0 +1,412 @@
|
||||
/**
|
||||
* Case Study: Interactive Image Gallery
|
||||
*
|
||||
* This example demonstrates:
|
||||
* - Dynamic loading of content
|
||||
* - Lightbox functionality
|
||||
* - Animation handling
|
||||
* - Keyboard and gesture navigation
|
||||
*/
|
||||
|
||||
import { el, memo, on } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
||||
// Sample image data
|
||||
const imagesSample = (url=> [
|
||||
{ id: 1, src: url+'nature', alt: 'Nature', title: 'Beautiful Landscape' },
|
||||
{ id: 2, src: url+'places', alt: 'City', title: 'Urban Architecture' },
|
||||
{ id: 3, src: url+'people', alt: 'People', title: 'Street Photography' },
|
||||
{ id: 4, src: url+'food', alt: 'Food', title: 'Culinary Delights' },
|
||||
{ id: 5, src: url+'animals', alt: 'Animals', title: 'Wildlife' },
|
||||
{ id: 6, src: url+'travel', alt: 'Travel', title: 'Adventure Awaits' },
|
||||
{ id: 7, src: url+'computer', alt: 'Technology', title: 'Modern Tech' },
|
||||
{ id: 8, src: url+'music', alt: 'Art', title: 'Creative Expression' },
|
||||
])('https://api.algobook.info/v1/randomimage?category=');
|
||||
/**
|
||||
* Interactive Image Gallery Component
|
||||
* @returns {HTMLElement} Gallery element
|
||||
*/
|
||||
export function ImageGallery(images= imagesSample) {
|
||||
const filterTag = S('all');
|
||||
const imagesToDisplay = S(() => {
|
||||
const tag = filterTag.get();
|
||||
if (tag === 'all') return images;
|
||||
else return images.filter(img => img.alt.toLowerCase() === tag);
|
||||
})
|
||||
const onFilterChange = tag => on("click", () => {
|
||||
filterTag.set(tag);
|
||||
});
|
||||
|
||||
// Lightbox
|
||||
const selectedImageId = S(null);
|
||||
const selectedImage = S(() => {
|
||||
const id = selectedImageId.get();
|
||||
return id ? images.find(img => img.id === id) : null;
|
||||
});
|
||||
const isLightboxOpen = S(() => selectedImage.get() !== null);
|
||||
const onImageClick = id => on("click", () => {
|
||||
selectedImageId.set(id);
|
||||
document.body.style.overflow = 'hidden'; // Prevent scrolling when lightbox is open
|
||||
|
||||
// Add keyboard event listeners when lightbox opens
|
||||
document.addEventListener('keydown', handleKeyDown);
|
||||
});
|
||||
const closeLightbox = () => {
|
||||
selectedImageId.set(null);
|
||||
document.body.style.overflow = ''; // Restore scrolling
|
||||
|
||||
// Remove keyboard event listeners when lightbox closes
|
||||
document.removeEventListener('keydown', handleKeyDown);
|
||||
};
|
||||
const onPrevImage = e => {
|
||||
e.stopPropagation(); // Prevent closing the lightbox
|
||||
const images = imagesToDisplay.get();
|
||||
const currentId = selectedImageId.get();
|
||||
const currentIndex = images.findIndex(img => img.id === currentId);
|
||||
const prevIndex = (currentIndex - 1 + images.length) % images.length;
|
||||
selectedImageId.set(images[prevIndex].id);
|
||||
};
|
||||
const onNextImage = e => {
|
||||
e.stopPropagation(); // Prevent closing the lightbox
|
||||
const images = imagesToDisplay.get();
|
||||
const currentId = selectedImageId.get();
|
||||
const currentIndex = images.findIndex(img => img.id === currentId);
|
||||
const nextIndex = (currentIndex + 1) % images.length;
|
||||
selectedImageId.set(images[nextIndex].id);
|
||||
};
|
||||
|
||||
// Keyboard navigation handler
|
||||
function handleKeyDown(e) {
|
||||
switch(e.key) {
|
||||
case 'Escape':
|
||||
closeLightbox();
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
onPrevImage(e);
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
onNextImage(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the gallery UI
|
||||
return el("div", { className: "gallery-container" }).append(
|
||||
// Gallery header
|
||||
el("header", { className: "gallery-header" }).append(
|
||||
el("h1", "Interactive Image Gallery"),
|
||||
el("p", "Click on any image to view it in the lightbox. Use arrow keys for navigation.")
|
||||
),
|
||||
|
||||
// Filter options
|
||||
el("div", { className: "gallery-filters" }).append(
|
||||
el("button", {
|
||||
classList: { active: S(() => filterTag.get() === 'all') },
|
||||
textContent: "All"
|
||||
}, onFilterChange('all')),
|
||||
el("button", {
|
||||
classList: { active: S(() => filterTag.get() === 'nature') },
|
||||
textContent: "Nature"
|
||||
}, onFilterChange('nature')),
|
||||
el("button", {
|
||||
classList: { active: S(() => filterTag.get() === 'urban') },
|
||||
textContent: "Urban"
|
||||
}, onFilterChange('urban')),
|
||||
el("button", {
|
||||
classList: { active: S(() => filterTag.get() === 'people') },
|
||||
textContent: "People"
|
||||
}, onFilterChange('people'))
|
||||
),
|
||||
|
||||
// Image grid
|
||||
el("div", { className: "gallery-grid" }).append(
|
||||
S.el(imagesToDisplay, images =>
|
||||
images.map(image =>
|
||||
memo(image.id, ()=>
|
||||
el("div", {
|
||||
className: "gallery-item",
|
||||
dataTag: image.alt.toLowerCase()
|
||||
}, onImageClick(image.id)).append(
|
||||
el("img", {
|
||||
src: image.src,
|
||||
alt: image.alt,
|
||||
loading: "lazy"
|
||||
}),
|
||||
el("div", { className: "gallery-item-caption" }).append(
|
||||
el("h3", image.title),
|
||||
el("p", image.alt)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// Lightbox (only shown when an image is selected)
|
||||
S.el(isLightboxOpen, open => !open
|
||||
? el()
|
||||
: el("div", { className: "lightbox-overlay" }, on("click", closeLightbox)).append(
|
||||
el("div", {
|
||||
className: "lightbox-content",
|
||||
onClick: e => e.stopPropagation() // Prevent closing when clicking inside
|
||||
}).append(
|
||||
el("button", {
|
||||
className: "lightbox-close-btn",
|
||||
ariaLabel: "Close lightbox",
|
||||
}, on("click", closeLightbox)).append("×"),
|
||||
|
||||
el("button", {
|
||||
className: "lightbox-prev-btn",
|
||||
ariaLabel: "Previous image",
|
||||
}, on("click", onPrevImage)).append("❮"),
|
||||
|
||||
el("button", {
|
||||
className: "lightbox-next-btn",
|
||||
ariaLabel: "Next image",
|
||||
}, on("click", onNextImage)).append("❯"),
|
||||
|
||||
S.el(selectedImage, img => !img
|
||||
? el()
|
||||
: el("div", { className: "lightbox-image-container" }).append(
|
||||
el("img", {
|
||||
src: img.src,
|
||||
alt: img.alt,
|
||||
className: "lightbox-image",
|
||||
}),
|
||||
el("div", { className: "lightbox-caption" }).append(
|
||||
el("h2", img.title),
|
||||
el("p", img.alt),
|
||||
),
|
||||
)
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Render the component
|
||||
document.body.append(
|
||||
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
|
||||
el(ImageGallery)
|
||||
),
|
||||
el("style", `
|
||||
.gallery-container {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.gallery-header {
|
||||
text-align: center;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.gallery-header h1 {
|
||||
margin-bottom: 0.5rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.gallery-header p {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.gallery-filters {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 2rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.gallery-filters button {
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0.5rem 1.5rem;
|
||||
margin: 0 0.5rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
border-radius: 30px;
|
||||
transition: all 0.3s ease;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.gallery-filters button:hover {
|
||||
background: #f0f0f0;
|
||||
}
|
||||
|
||||
.gallery-filters button.active {
|
||||
background: #4a90e2;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.gallery-item {
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gallery-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.gallery-item img {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.gallery-item:hover img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.gallery-item-caption {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent);
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
transform: translateY(100%);
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.gallery-item:hover .gallery-item-caption {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.gallery-item-caption h3 {
|
||||
margin: 0 0 0.5rem;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.gallery-item-caption p {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Lightbox styles */
|
||||
.lightbox-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.9);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.lightbox-content {
|
||||
position: relative;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
||||
.lightbox-image-container {
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.lightbox-image {
|
||||
max-width: 100%;
|
||||
max-height: 80vh;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.lightbox-caption {
|
||||
background: #222;
|
||||
color: white;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lightbox-caption h2 {
|
||||
margin: 0 0 0.5rem;
|
||||
}
|
||||
|
||||
.lightbox-caption p {
|
||||
margin: 0;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.lightbox-close-btn,
|
||||
.lightbox-prev-btn,
|
||||
.lightbox-next-btn {
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 1.5rem;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s ease;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.lightbox-close-btn:hover,
|
||||
.lightbox-prev-btn:hover,
|
||||
.lightbox-next-btn:hover {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.lightbox-close-btn {
|
||||
top: -25px;
|
||||
right: -25px;
|
||||
}
|
||||
|
||||
.lightbox-prev-btn {
|
||||
left: -25px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.lightbox-next-btn {
|
||||
right: -25px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.gallery-container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.gallery-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.lightbox-prev-btn,
|
||||
.lightbox-next-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
`)
|
||||
);
|
339
docs/components/examples/case-studies/interactive-form.js
Normal file
339
docs/components/examples/case-studies/interactive-form.js
Normal 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;
|
||||
}
|
||||
`),
|
||||
);
|
509
docs/components/examples/case-studies/products.js
Normal file
509
docs/components/examples/case-studies/products.js
Normal file
@ -0,0 +1,509 @@
|
||||
import { el, on, scope } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
||||
export function ProductCatalog() {
|
||||
const { signal }= scope;
|
||||
|
||||
const itemsPerPage = 5;
|
||||
const products = asyncSignal(S,
|
||||
fetchProducts,
|
||||
{ initial: [], keepLast: true, signal });
|
||||
const searchTerm = S("");
|
||||
const handleSearch = (e) => searchTerm.set(e.target.value);
|
||||
const sortOrder = S("default");
|
||||
const handleSort = (e) => sortOrder.set(e.target.value);
|
||||
const page = S(1);
|
||||
const handlePageChange = (newPage) => page.set(newPage);
|
||||
const resetFilters = () => {
|
||||
searchTerm.set("");
|
||||
sortOrder.set("default");
|
||||
page.set(1);
|
||||
};
|
||||
|
||||
const filteredProducts = S(() => {
|
||||
if (products.status.get() !== "resolved") return [];
|
||||
|
||||
const results = products.result.get().filter(product =>
|
||||
product.title.toLowerCase().includes(searchTerm.get().toLowerCase()) ||
|
||||
product.description.toLowerCase().includes(searchTerm.get().toLowerCase())
|
||||
);
|
||||
|
||||
return [...results].sort((a, b) => {
|
||||
const order = sortOrder.get();
|
||||
if (order === "price-asc") return a.price - b.price;
|
||||
if (order === "price-desc") return b.price - a.price;
|
||||
if (order === "rating") return b.rating - a.rating;
|
||||
return 0; // default: no sorting
|
||||
});
|
||||
});
|
||||
const totalPages = S(() => Math.ceil(filteredProducts.get().length / itemsPerPage));
|
||||
const paginatedProducts = S(() => {
|
||||
const currentPage = page.get();
|
||||
const filtered = filteredProducts.get();
|
||||
const start = (currentPage - 1) * itemsPerPage;
|
||||
return filtered.slice(start, start + itemsPerPage);
|
||||
});
|
||||
|
||||
// Component structure
|
||||
return el("div", { className: "product-catalog" }).append(
|
||||
el("header", { className: "catalog-header" }).append(
|
||||
el("h2", "Product Catalog"),
|
||||
el("div", { className: "toolbar" }).append(
|
||||
el("button", {
|
||||
className: "refresh-btn",
|
||||
textContent: "Refresh Products",
|
||||
type: "button",
|
||||
onclick: () => products.invoke(),
|
||||
}),
|
||||
el("button", {
|
||||
className: "reset-btn",
|
||||
textContent: "Reset Filters",
|
||||
type: "button",
|
||||
onclick: resetFilters,
|
||||
})
|
||||
)
|
||||
),
|
||||
|
||||
// Search and filter controls
|
||||
el("div", { className: "controls" }).append(
|
||||
el("div", { className: "search-box" }).append(
|
||||
el("input", {
|
||||
type: "search",
|
||||
placeholder: "Search products...",
|
||||
value: searchTerm,
|
||||
oninput: handleSearch,
|
||||
})
|
||||
),
|
||||
el("div", { className: "sort-options" }).append(
|
||||
el("label", "Sort by: "),
|
||||
el("select", { onchange: handleSort }, on.defer(el => el.value = sortOrder.get())).append(
|
||||
el("option", { value: "default", textContent: "Default" }),
|
||||
el("option", { value: "price-asc", textContent: "Price: Low to High" }),
|
||||
el("option", { value: "price-desc", textContent: "Price: High to Low" }),
|
||||
el("option", { value: "rating", textContent: "Top Rated" })
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// Status indicators
|
||||
el("div", { className: "status-container" }).append(
|
||||
S.el(products.status, status =>
|
||||
status === "pending" ?
|
||||
el("div", { className: "loader" }).append(
|
||||
el("div", { className: "spinner" }),
|
||||
el("p", "Loading products...")
|
||||
)
|
||||
: status === "rejected" ?
|
||||
el("div", { className: "error-message" }).append(
|
||||
el("p", products.error.get().message),
|
||||
el("button", {
|
||||
textContent: "Try Again",
|
||||
onclick: () => products.invoke()
|
||||
})
|
||||
)
|
||||
: el()
|
||||
)
|
||||
),
|
||||
|
||||
// Results count
|
||||
S.el(S(()=> [filteredProducts.get(), searchTerm.get()]), ([filtered, term]) =>
|
||||
products.status.get() === "resolved"
|
||||
? el("div", {
|
||||
className: "results-info",
|
||||
textContent: term ?
|
||||
`Found ${filtered.length} products matching "${term}"`
|
||||
: `Showing all ${filtered.length} products`
|
||||
})
|
||||
: el()
|
||||
),
|
||||
|
||||
// Products grid
|
||||
el("div", { className: "products-grid" }).append(
|
||||
S.el(paginatedProducts, paginatedItems =>
|
||||
products.status.get() === "resolved" && paginatedItems.length > 0 ?
|
||||
paginatedItems.map(product => el(ProductCard, { product }))
|
||||
: products.status.get() === "resolved" && paginatedItems.length === 0 ?
|
||||
el("p", { className: "no-results", textContent: "No products found matching your criteria." })
|
||||
: el()
|
||||
)
|
||||
),
|
||||
|
||||
// Pagination
|
||||
S.el(S(()=> [totalPages.get(), page.get()]), ([total, current]) =>
|
||||
products.status.get() === "resolved" && total > 1 ?
|
||||
el("div", { className: "pagination" }).append(
|
||||
el("button", {
|
||||
textContent: "Previous",
|
||||
disabled: current === 1,
|
||||
onclick: () => handlePageChange(current - 1)
|
||||
}),
|
||||
...Array.from({ length: total }, (_, i) => i + 1).map(num =>
|
||||
el("button", {
|
||||
className: num === current ? "current-page" : "",
|
||||
textContent: num,
|
||||
onclick: () => handlePageChange(num)
|
||||
})
|
||||
),
|
||||
el("button", {
|
||||
textContent: "Next",
|
||||
disabled: current === total,
|
||||
onclick: () => handlePageChange(current + 1)
|
||||
})
|
||||
)
|
||||
: el()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Product card component
|
||||
function ProductCard({ product }) {
|
||||
const showDetails = S(false);
|
||||
|
||||
return el("div", { className: "product-card" }).append(
|
||||
el("div", { className: "product-image" }).append(
|
||||
el("img", { src: product.thumbnail, alt: product.title })
|
||||
),
|
||||
el("div", { className: "product-info" }).append(
|
||||
el("h3", { className: "product-title", textContent: product.title }),
|
||||
el("div", { className: "product-price-rating" }).append(
|
||||
el("span", { className: "product-price", textContent: `$${product.price.toFixed(2)}` }),
|
||||
el("span", { className: "product-rating" }).append(
|
||||
el("span", { className: "stars", textContent: "★".repeat(Math.round(product.rating)) }),
|
||||
el("span", { className: "rating-value", textContent: `(${product.rating})` }),
|
||||
)
|
||||
),
|
||||
el("p", { className: "product-category", textContent: `Category: ${product.category}` }),
|
||||
S.el(showDetails, details =>
|
||||
details ?
|
||||
el("div", { className: "product-details" }).append(
|
||||
el("p", { className: "product-description", textContent: product.description }),
|
||||
el("div", { className: "product-meta" }).append(
|
||||
el("p", `Brand: ${product.brand}`),
|
||||
el("p", `Stock: ${product.stock} units`),
|
||||
el("p", `Discount: ${product.discountPercentage}%`)
|
||||
)
|
||||
)
|
||||
: el()
|
||||
),
|
||||
el("div", { className: "product-actions" }).append(
|
||||
el("button", {
|
||||
className: "details-btn",
|
||||
textContent: S(() => showDetails.get() ? "Hide Details" : "Show Details"),
|
||||
onclick: () => showDetails.set(!showDetails.get())
|
||||
}),
|
||||
el("button", {
|
||||
className: "add-to-cart-btn",
|
||||
textContent: "Add to Cart"
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Data fetching function
|
||||
async function fetchProducts({ signal }) {
|
||||
await simulateNetworkDelay();
|
||||
// Simulate random errors for demonstration
|
||||
if (Math.random() > 0.9) throw new Error("Failed to load products. Network error.");
|
||||
|
||||
const response = await fetch("https://dummyjson.com/products", { signal });
|
||||
if (!response.ok) throw new Error(`API error: ${response.status}`);
|
||||
|
||||
const data = await response.json();
|
||||
return data.products.slice(0, 20); // Limit to 20 products for the demo
|
||||
}
|
||||
|
||||
// Utility for simulating network latency
|
||||
function simulateNetworkDelay(min = 300, max = 1200) {
|
||||
const delay = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
return new Promise(resolve => setTimeout(resolve, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom hook for async data fetching with signals
|
||||
* @template T
|
||||
* @param {typeof S} S - Signal constructor
|
||||
* @param {(params: { signal: AbortSignal }) => Promise<T>} invoker - Async function to execute
|
||||
* @param {{ initial?: T, keepLast?: boolean, signal?: AbortSignal }} options - Configuration options
|
||||
* @returns {Object} Status signals and control methods
|
||||
*/
|
||||
export function asyncSignal(S, invoker, { initial, keepLast, signal } = {}) {
|
||||
/** @type {(s: AbortSignal) => AbortSignal} */
|
||||
const anySignal = !signal || !AbortSignal.any // TODO: make better
|
||||
? s=> s
|
||||
: s=> AbortSignal.any([s, signal]);
|
||||
// Status tracking signals
|
||||
const status = S("pending");
|
||||
const result = S(initial);
|
||||
const error = S(null);
|
||||
let controller = null;
|
||||
|
||||
// Function to trigger data fetching
|
||||
async function invoke() {
|
||||
// Cancel any in-flight request
|
||||
if (controller) controller.abort();
|
||||
controller = new AbortController();
|
||||
|
||||
status.set("pending");
|
||||
error.set(null);
|
||||
if (!keepLast) result.set(initial);
|
||||
|
||||
try {
|
||||
const data = await invoker({
|
||||
signal: anySignal(controller.signal),
|
||||
});
|
||||
if (!controller.signal.aborted) {
|
||||
status.set("resolved");
|
||||
result.set(data);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.name !== "AbortError") {
|
||||
error.set(e);
|
||||
status.set("rejected");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initial data fetch
|
||||
invoke();
|
||||
|
||||
return { status, result, error, invoke };
|
||||
}
|
||||
|
||||
// Initialize the component
|
||||
document.body.append(
|
||||
el(ProductCatalog),
|
||||
el("style", `
|
||||
.product-catalog {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.catalog-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.toolbar button {
|
||||
margin-left: 10px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
background: #4a6cf7;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
gap: 15px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
width: 300px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.sort-options select {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 4px solid rgba(0, 0, 0, 0.1);
|
||||
border-left-color: #4a6cf7;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #ffebee;
|
||||
color: #c62828;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.results-info {
|
||||
margin-bottom: 15px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.products-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.product-card {
|
||||
border: 1px solid #eee;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.product-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.product-image img {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.product-title {
|
||||
margin: 0 0 10px;
|
||||
font-size: 1.1rem;
|
||||
height: 2.4rem;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.product-price-rating {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-weight: bold;
|
||||
color: #4a6cf7;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.stars {
|
||||
color: gold;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.product-category {
|
||||
color: #666;
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.product-details {
|
||||
margin: 15px 0;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.product-description {
|
||||
line-height: 1.5;
|
||||
margin-bottom: 10px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.product-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
color: #666;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.product-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.product-actions button {
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.details-btn {
|
||||
background: #eee;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.add-to-cart-btn {
|
||||
background: #4a6cf7;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.pagination button {
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #ddd;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pagination button.current-page {
|
||||
background: #4a6cf7;
|
||||
color: white;
|
||||
border-color: #4a6cf7;
|
||||
}
|
||||
|
||||
.pagination button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
grid-column: 1 / -1;
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.controls {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.products-grid {
|
||||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||
}
|
||||
}
|
||||
`),
|
||||
);
|
715
docs/components/examples/case-studies/task-manager.js
Normal file
715
docs/components/examples/case-studies/task-manager.js
Normal file
@ -0,0 +1,715 @@
|
||||
/**
|
||||
* Case Study: Task Manager Application
|
||||
*
|
||||
* This example demonstrates:
|
||||
* - Complex state management with signals
|
||||
* - Drag and drop functionality
|
||||
* - Local storage persistence
|
||||
* - Responsive design for different devices
|
||||
*/
|
||||
|
||||
import { el, on, dispatchEvent, scope } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
||||
/** @typedef {{ id: number, title: string, description: string, priority: string, status: string }} Task */
|
||||
/**
|
||||
* Task Manager Component
|
||||
* @returns {HTMLElement} Task manager UI
|
||||
*/
|
||||
export function TaskManager() {
|
||||
// <Tasks store>
|
||||
const STORAGE_KEY = 'dde-task-manager';
|
||||
const STATUSES = {
|
||||
TODO: 'todo',
|
||||
IN_PROGRESS: 'in-progress',
|
||||
DONE: 'done'
|
||||
};
|
||||
/** @type {Task[]} */
|
||||
let initialTasks = [];
|
||||
try {
|
||||
const saved = localStorage.getItem(STORAGE_KEY);
|
||||
if (saved) {
|
||||
initialTasks = JSON.parse(saved);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to load tasks from localStorage', e);
|
||||
}
|
||||
if (!initialTasks.length) {
|
||||
initialTasks = [
|
||||
{ id: 1, title: 'Create project structure', description: 'Set up folders and initial files',
|
||||
status: STATUSES.DONE, priority: 'high' },
|
||||
{ id: 2, title: 'Design UI components', description: 'Create mockups for main views',
|
||||
status: STATUSES.IN_PROGRESS, priority: 'medium' },
|
||||
{ id: 3, title: 'Implement authentication', description: 'Set up user login and registration',
|
||||
status: STATUSES.TODO, priority: 'high' },
|
||||
{ id: 4, title: 'Write documentation', description: 'Document API endpoints and usage examples',
|
||||
status: STATUSES.TODO, priority: 'low' },
|
||||
];
|
||||
}
|
||||
const tasks = S(initialTasks, {
|
||||
add(task) { this.value.push(task); },
|
||||
remove(id) { this.value = this.value.filter(task => task.id !== id); },
|
||||
update(id, task) {
|
||||
const current= this.value.find(t => t.id === id);
|
||||
if (current) Object.assign(current, task);
|
||||
}
|
||||
});
|
||||
S.on(tasks, value => {
|
||||
try {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(value));
|
||||
} catch (e) {
|
||||
console.error('Failed to save tasks to localStorage', e);
|
||||
}
|
||||
});
|
||||
// </Tasks store>
|
||||
|
||||
const filterPriority = S('all');
|
||||
const searchQuery = S('');
|
||||
// Filtered tasks based on priority and search query
|
||||
const filteredTasks = S(() => {
|
||||
let filtered = tasks.get();
|
||||
|
||||
// Filter by priority
|
||||
if (filterPriority.get() !== 'all') {
|
||||
filtered = filtered.filter(task => task.priority === filterPriority.get());
|
||||
}
|
||||
|
||||
// Filter by search query
|
||||
const query = searchQuery.get().toLowerCase();
|
||||
if (query) {
|
||||
filtered = filtered.filter(task =>
|
||||
task.title.toLowerCase().includes(query) ||
|
||||
task.description.toLowerCase().includes(query)
|
||||
);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
});
|
||||
/** Tasks grouped by status for display in columns */
|
||||
const tasksByStatus = S(() => {
|
||||
const filtered = filteredTasks.get();
|
||||
return {
|
||||
[STATUSES.TODO]: filtered.filter(t => t.status === STATUSES.TODO),
|
||||
[STATUSES.IN_PROGRESS]: filtered.filter(t => t.status === STATUSES.IN_PROGRESS),
|
||||
[STATUSES.DONE]: filtered.filter(t => t.status === STATUSES.DONE)
|
||||
};
|
||||
});
|
||||
|
||||
// <Add> signals and handlers for adding new tasks
|
||||
const newTask = { title: '', description: '', priority: 'medium' };
|
||||
const onAddTask = e => {
|
||||
e.preventDefault();
|
||||
if (!newTask.title) return;
|
||||
|
||||
S.action(tasks, "add", {
|
||||
id: Date.now(),
|
||||
status: STATUSES.TODO,
|
||||
...newTask
|
||||
});
|
||||
e.target.reset();
|
||||
};
|
||||
// </Add>
|
||||
const onCardEdit= on("card:edit", /** @param {CardEditEvent} ev */({ detail: [ id, task ] })=>
|
||||
S.action(tasks, "update", id, task));
|
||||
const onCardDelete= on("card:delete", /** @param {CardDeleteEvent} ev */({ detail: id })=>
|
||||
S.action(tasks, "remove", id));
|
||||
|
||||
const { onDragable, onDragArea }= moveElementAddon(
|
||||
(id, status) => S.action(tasks, "update", id, { status })
|
||||
);
|
||||
|
||||
// Build the task manager UI
|
||||
return el("div", { className: "task-manager" }).append(
|
||||
el("header", { className: "app-header" }).append(
|
||||
el("h1", "DDE Task Manager"),
|
||||
el("div", { className: "app-controls" }).append(
|
||||
el("input", {
|
||||
type: "text",
|
||||
placeholder: "Search tasks...",
|
||||
value: searchQuery.get()
|
||||
}, on("input", e => searchQuery.set(e.target.value))),
|
||||
el("select", null,
|
||||
on.defer(el=> el.value= filterPriority.get()),
|
||||
on("change", e => filterPriority.set(e.target.value))
|
||||
).append(
|
||||
el("option", { value: "all", textContent: "All Priorities" }),
|
||||
el("option", { value: "low", textContent: "Low Priority" }),
|
||||
el("option", { value: "medium", textContent: "Medium Priority" }),
|
||||
el("option", { value: "high", textContent: "High Priority" })
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// Add new task form
|
||||
el("form", { className: "new-task-form" }, on("submit", onAddTask)).append(
|
||||
el("div", { className: "form-row" }).append(
|
||||
el("input", {
|
||||
type: "text",
|
||||
placeholder: "New task title",
|
||||
value: newTask.title,
|
||||
required: true
|
||||
}, on("input", e => newTask.title= e.target.value.trim())),
|
||||
el("select", null,
|
||||
on.defer(el=> el.value= newTask.priority),
|
||||
on("change", e => newTask.priority= e.target.value)
|
||||
).append(
|
||||
el("option", { value: "low", textContent: "Low" }),
|
||||
el("option", { value: "medium", textContent: "Medium" }),
|
||||
el("option", { value: "high", textContent: "High" })
|
||||
),
|
||||
el("button", { type: "submit", className: "add-btn" }).append("Add Task")
|
||||
),
|
||||
el("textarea", {
|
||||
placeholder: "Task description (optional)",
|
||||
value: newTask.description
|
||||
}, on("input", e => newTask.description= e.target.value.trim()))
|
||||
),
|
||||
|
||||
// Task board with columns
|
||||
el("div", { className: "task-board" }).append(
|
||||
// Todo column
|
||||
el("div", {
|
||||
id: `column-${STATUSES.TODO}`,
|
||||
className: "task-column"
|
||||
}, onDragArea(STATUSES.TODO)).append(
|
||||
el("h2", { className: "column-header" }).append(
|
||||
"To Do ",
|
||||
el("span", {
|
||||
textContent: S(() => tasksByStatus.get()[STATUSES.TODO].length),
|
||||
className: "task-count"
|
||||
}),
|
||||
),
|
||||
S.el(S(() => tasksByStatus.get()[STATUSES.TODO]), tasks =>
|
||||
el("div", { className: "column-tasks" }).append(
|
||||
...tasks.map(task=> el(TaskCard, { task, onDragable }, onCardEdit, onCardDelete))
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// In Progress column
|
||||
el("div", {
|
||||
id: `column-${STATUSES.IN_PROGRESS}`,
|
||||
className: "task-column"
|
||||
}, onDragArea(STATUSES.IN_PROGRESS)).append(
|
||||
el("h2", { className: "column-header" }).append(
|
||||
"In Progress ",
|
||||
el("span", {
|
||||
textContent: S(() => tasksByStatus.get()[STATUSES.IN_PROGRESS].length),
|
||||
className: "task-count",
|
||||
}),
|
||||
),
|
||||
S.el(S(() => tasksByStatus.get()[STATUSES.IN_PROGRESS]), tasks =>
|
||||
el("div", { className: "column-tasks" }).append(
|
||||
...tasks.map(task=> el(TaskCard, { task, onDragable }, onCardEdit, onCardDelete))
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// Done column
|
||||
el("div", {
|
||||
id: `column-${STATUSES.DONE}`,
|
||||
className: "task-column"
|
||||
}, onDragArea(STATUSES.DONE)).append(
|
||||
el("h2", { className: "column-header" }).append(
|
||||
"Done ",
|
||||
el("span", {
|
||||
textContent: S(() => tasksByStatus.get()[STATUSES.DONE].length),
|
||||
className: "task-count",
|
||||
}),
|
||||
),
|
||||
S.el(S(() => tasksByStatus.get()[STATUSES.DONE]), tasks =>
|
||||
el("div", { className: "column-tasks" }).append(
|
||||
...tasks.map(task=> el(TaskCard, { task, onDragable }, onCardEdit, onCardDelete))
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
/** @typedef {CustomEvent<[ string, Task ]>} CardEditEvent */
|
||||
/** @typedef {CustomEvent<string>} CardDeleteEvent */
|
||||
/**
|
||||
* Task Card Component
|
||||
* @type {(props: { task: Task, onDragable: (id: number) => ddeElementAddon<HTMLDivElement> }) => HTMLElement}
|
||||
* @fires {CardEditEvent} card:edit
|
||||
* @fires {CardDeleteEvent} card:delete
|
||||
* */
|
||||
function TaskCard({ task, onDragable }){
|
||||
const { host }= scope;
|
||||
const isEditing = S(false);
|
||||
const onEditStart = () => isEditing.set(true);
|
||||
|
||||
const dispatchEdit= dispatchEvent("card:edit", host);
|
||||
const dispatchDelete= dispatchEvent("card:delete", host).bind(null, task.id);
|
||||
|
||||
return el("div", {
|
||||
id: `task-${task.id}`,
|
||||
className: `task-card priority-${task.priority}`,
|
||||
draggable: true
|
||||
}, onDragable(task.id)).append(
|
||||
S.el(isEditing, editing => editing
|
||||
? el(EditMode)
|
||||
: el().append(
|
||||
el("div", { className: "task-header" }).append(
|
||||
el("h3", { className: "task-title", textContent: task.title }),
|
||||
el("div", { className: "task-actions" }).append(
|
||||
el("button", {
|
||||
textContent: "✎",
|
||||
className: "edit-btn",
|
||||
ariaLabel: "Edit task"
|
||||
}, on("click", onEditStart)),
|
||||
el("button", {
|
||||
textContent: "✕",
|
||||
className: "delete-btn",
|
||||
ariaLabel: "Delete task"
|
||||
}, on("click", dispatchDelete))
|
||||
)
|
||||
),
|
||||
!task.description
|
||||
? el()
|
||||
: el("p", { className: "task-description", textContent: task.description }),
|
||||
el("div", { className: "task-meta" }).append(
|
||||
el("span", {
|
||||
className: `priority-badge priority-${task.priority}`,
|
||||
textContent: task.priority.charAt(0).toUpperCase() + task.priority.slice(1)
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
function EditMode(){
|
||||
const onSubmit = on("submit", e => {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(/** @type {HTMLFormElement} */(e.target));
|
||||
const title = formData.get("title");
|
||||
const description = formData.get("description");
|
||||
const priority = formData.get("priority");
|
||||
isEditing.set(false);
|
||||
dispatchEdit([ task.id, { title, description, priority } ]);
|
||||
})
|
||||
const onEditCancel = () => isEditing.set(false);
|
||||
|
||||
return el("form", { className: "task-edit-form" }, onSubmit).append(
|
||||
el("input", {
|
||||
name: "title",
|
||||
className: "task-title-input",
|
||||
defaultValue: task.title,
|
||||
placeholder: "Task title",
|
||||
required: true,
|
||||
autoFocus: true
|
||||
}),
|
||||
el("textarea", {
|
||||
name: "description",
|
||||
className: "task-desc-input",
|
||||
defaultValue: task.description,
|
||||
placeholder: "Description (optional)"
|
||||
}),
|
||||
el("select", {
|
||||
name: "priority",
|
||||
}, on.defer(el=> el.value = task.priority)).append(
|
||||
el("option", { value: "low", textContent: "Low Priority" }),
|
||||
el("option", { value: "medium", textContent: "Medium Priority" }),
|
||||
el("option", { value: "high", textContent: "High Priority" })
|
||||
),
|
||||
el("div", { className: "task-edit-actions" }).append(
|
||||
el("button", {
|
||||
textContent: "Cancel",
|
||||
type: "button",
|
||||
className: "cancel-btn"
|
||||
}, on("click", onEditCancel)),
|
||||
el("button", {
|
||||
textContent: "Save",
|
||||
type: "submit",
|
||||
className: "save-btn"
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to handle move an element
|
||||
* @param {(id: string, status: string) => void} onMoved
|
||||
* */
|
||||
function moveElementAddon(onMoved){
|
||||
let draggedTaskId = null;
|
||||
function onDragable(id) {
|
||||
return element => {
|
||||
on("dragstart", e => {
|
||||
draggedTaskId= id;
|
||||
e.dataTransfer.effectAllowed = 'move';
|
||||
|
||||
// Add some styling to the element being dragged
|
||||
setTimeout(() => {
|
||||
const el = document.getElementById(`task-${id}`);
|
||||
if (el) el.classList.add('dragging');
|
||||
}, 0);
|
||||
})(element);
|
||||
|
||||
on("dragend", () => {
|
||||
draggedTaskId= null;
|
||||
|
||||
// Remove the styling
|
||||
const el = document.getElementById(`task-${id}`);
|
||||
if (el) el.classList.remove('dragging');
|
||||
})(element);
|
||||
};
|
||||
}
|
||||
function onDragArea(status) {
|
||||
return element => {
|
||||
on("dragover", e => {
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
|
||||
// Add a visual indicator for the drop target
|
||||
const column = document.getElementById(`column-${status}`);
|
||||
if (column) column.classList.add('drag-over');
|
||||
})(element);
|
||||
|
||||
on("dragleave", () => {
|
||||
// Remove the visual indicator
|
||||
const column = document.getElementById(`column-${status}`);
|
||||
if (column) column.classList.remove('drag-over');
|
||||
})(element);
|
||||
|
||||
on("drop", e => {
|
||||
e.preventDefault();
|
||||
const id = draggedTaskId;
|
||||
if (id) onMoved(id, status);
|
||||
// Remove the visual indicator
|
||||
const column = document.getElementById(`column-${status}`);
|
||||
if (column) column.classList.remove('drag-over');
|
||||
})(element);
|
||||
};
|
||||
}
|
||||
return { onDragable, onDragArea };
|
||||
}
|
||||
|
||||
// Render the component
|
||||
document.body.append(
|
||||
el("div", { style: "padding: 20px; background: #f5f5f5; min-height: 100vh;" }).append(
|
||||
el(TaskManager)
|
||||
),
|
||||
el("style", `
|
||||
.task-manager {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 1rem;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.app-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.app-header h1 {
|
||||
margin: 0;
|
||||
color: #2d3748;
|
||||
}
|
||||
|
||||
.app-controls {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.app-controls input,
|
||||
.app-controls select {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.new-task-form {
|
||||
background: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.form-row input {
|
||||
flex-grow: 1;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.form-row select {
|
||||
width: 100px;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
background: #4a90e2;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.add-btn:hover {
|
||||
background: #3a7bc8;
|
||||
}
|
||||
|
||||
.new-task-form textarea {
|
||||
width: 100%;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
font-size: 1rem;
|
||||
resize: vertical;
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.task-board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.task-column {
|
||||
background: #f7fafc;
|
||||
border-radius: 8px;
|
||||
padding: 1rem;
|
||||
min-height: 400px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.column-header {
|
||||
margin-top: 0;
|
||||
padding-bottom: 0.75rem;
|
||||
border-bottom: 2px solid #e2e8f0;
|
||||
font-size: 1.25rem;
|
||||
color: #2d3748;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.task-count {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #e2e8f0;
|
||||
color: #4a5568;
|
||||
border-radius: 50%;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
font-size: 0.875rem;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.column-tasks {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.task-card {
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
padding: 1rem;
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
|
||||
cursor: grab;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
position: relative;
|
||||
border-left: 4px solid #ccc;
|
||||
}
|
||||
|
||||
.task-card:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.task-card.dragging {
|
||||
opacity: 0.5;
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.task-card.priority-low {
|
||||
border-left-color: #38b2ac;
|
||||
}
|
||||
|
||||
.task-card.priority-medium {
|
||||
border-left-color: #ecc94b;
|
||||
}
|
||||
|
||||
.task-card.priority-high {
|
||||
border-left-color: #e53e3e;
|
||||
}
|
||||
|
||||
.task-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
color: #2d3748;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.task-description {
|
||||
margin: 0.5rem 0;
|
||||
font-size: 0.9rem;
|
||||
color: #4a5568;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.edit-btn,
|
||||
.delete-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
color: #718096;
|
||||
transition: background 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.edit-btn:hover {
|
||||
background: #edf2f7;
|
||||
color: #4a5568;
|
||||
}
|
||||
|
||||
.delete-btn:hover {
|
||||
background: #fed7d7;
|
||||
color: #e53e3e;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.priority-badge {
|
||||
font-size: 0.75rem;
|
||||
padding: 0.2rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.priority-badge.priority-low {
|
||||
background: #e6fffa;
|
||||
color: #2c7a7b;
|
||||
}
|
||||
|
||||
.priority-badge.priority-medium {
|
||||
background: #fefcbf;
|
||||
color: #975a16;
|
||||
}
|
||||
|
||||
.priority-badge.priority-high {
|
||||
background: #fed7d7;
|
||||
color: #c53030;
|
||||
}
|
||||
|
||||
.drag-over {
|
||||
background: #f0f9ff;
|
||||
border: 2px dashed #4a90e2;
|
||||
}
|
||||
|
||||
.task-edit-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.task-title-input,
|
||||
.task-desc-input {
|
||||
width: 100%;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.task-desc-input {
|
||||
min-height: 60px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.task-edit-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
.cancel-btn,
|
||||
.save-btn {
|
||||
padding: 0.4rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #edf2f7;
|
||||
color: #4a5568;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
|
||||
.save-btn {
|
||||
background: #4a90e2;
|
||||
color: white;
|
||||
border: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.app-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.app-controls {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.task-board {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
`)
|
||||
);
|
@ -8,7 +8,8 @@ export class HTMLCustomElement extends HTMLElement{
|
||||
connectedCallback(){
|
||||
customElementRender(
|
||||
this.attachShadow({ mode: "open" }),
|
||||
ddeComponent
|
||||
ddeComponent,
|
||||
this
|
||||
);
|
||||
}
|
||||
set attr(value){ this.setAttribute("attr", value); }
|
||||
|
@ -2,7 +2,6 @@
|
||||
import {
|
||||
customElementRender,
|
||||
customElementWithDDE,
|
||||
observedAttributes,
|
||||
} from "deka-dom-el";
|
||||
/** @type {ddePublicElementTagNameMap} */
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
@ -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
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {
|
||||
customElementRender,
|
||||
customElementWithDDE,
|
||||
observedAttributes,
|
||||
el, on, scope,
|
||||
} from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
@ -9,7 +8,6 @@ export class HTMLCustomElement extends HTMLElement{
|
||||
static tagName= "custom-element";
|
||||
static observedAttributes= [ "attr" ];
|
||||
connectedCallback(){
|
||||
console.log(observedAttributes(this));
|
||||
customElementRender(
|
||||
this.attachShadow({ mode: "open" }),
|
||||
ddeComponent,
|
||||
@ -26,7 +24,7 @@ function ddeComponent({ attr }){
|
||||
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);
|
||||
|
15
docs/components/examples/debugging/consoleLog.js
Normal file
15
docs/components/examples/debugging/consoleLog.js
Normal 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`)
|
67
docs/components/examples/debugging/debouncing.js
Normal file
67
docs/components/examples/debugging/debouncing.js
Normal 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));
|
@ -0,0 +1,4 @@
|
||||
// Example of reactive element marker
|
||||
<!--<dde:mark type="reactive" component="<component-name>">-->
|
||||
<!-- content that updates when signal changes -->
|
||||
<!--</dde:mark>-->
|
15
docs/components/examples/debugging/mutations.js
Normal file
15
docs/components/examples/debugging/mutations.js
Normal 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");
|
14
docs/components/examples/elements/dde-dom-create.js
Normal file
14
docs/components/examples/elements/dde-dom-create.js
Normal 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);
|
11
docs/components/examples/elements/dde-dom-tree.js
Normal file
11
docs/components/examples/elements/dde-dom-tree.js
Normal 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"),
|
||||
),
|
||||
);
|
@ -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(
|
||||
|
19
docs/components/examples/elements/native-dom-create.js
Normal file
19
docs/components/examples/elements/native-dom-create.js
Normal 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);
|
15
docs/components/examples/elements/native-dom-tree.js
Normal file
15
docs/components/examples/elements/native-dom-tree.js
Normal 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);
|
8
docs/components/examples/events/append-event.js
Normal file
8
docs/components/examples/events/append-event.js
Normal 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))
|
||||
);
|
7
docs/components/examples/events/attribute-event.js
Normal file
7
docs/components/examples/events/attribute-event.js
Normal 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)"
|
||||
});
|
8
docs/components/examples/events/chain-event.js
Normal file
8
docs/components/examples/events/chain-event.js
Normal 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))
|
||||
);
|
19
docs/components/examples/events/dispatch.js
Normal file
19
docs/components/examples/events/dispatch.js
Normal 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))
|
||||
);
|
||||
}
|
@ -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 */
|
||||
|
2
docs/components/examples/events/native-event.js
Normal file
2
docs/components/examples/events/native-event.js
Normal file
@ -0,0 +1,2 @@
|
||||
// Standard DOM event listener approach
|
||||
element.addEventListener('click', callback, options);
|
7
docs/components/examples/events/property-event.js
Normal file
7
docs/components/examples/events/property-event.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { el } from "deka-dom-el";
|
||||
|
||||
// Using events with property assignment
|
||||
el("button", {
|
||||
textContent: "click me",
|
||||
onclick: console.log
|
||||
});
|
13
docs/components/examples/introducing/3ps-before.js
Normal file
13
docs/components/examples/introducing/3ps-before.js
Normal 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;
|
||||
});
|
@ -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);
|
||||
});
|
||||
|
@ -1,19 +1,30 @@
|
||||
import { el } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
const threePS= ({ emoji= "🚀" })=> {
|
||||
const clicks= S(0); // A
|
||||
|
||||
// A HelloWorld component using the 3PS pattern
|
||||
function HelloWorld({ emoji = "🚀" }) {
|
||||
// PART 1: Create reactive state
|
||||
const clicks = S(0);
|
||||
|
||||
return el().append(
|
||||
el("p", S(()=>
|
||||
"Hello World "+emoji.repeat(clicks()) // B
|
||||
)),
|
||||
// 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)
|
||||
})
|
||||
);
|
||||
};
|
||||
document.body.append(
|
||||
el(threePS, { emoji: "🎉" }),
|
||||
);
|
||||
}
|
||||
|
||||
// Use the component in your app
|
||||
document.body.append(
|
||||
el(HelloWorld, { emoji: "🎉" })
|
||||
);
|
||||
|
@ -15,15 +15,15 @@ function HelloWorldComponent({ initial }){
|
||||
|
||||
return el().append(
|
||||
el("p", {
|
||||
textContent: S(() => `Hello World ${emoji().repeat(clicks())}`),
|
||||
textContent: S(() => `Hello World ${emoji.get().repeat(clicks.get())}`),
|
||||
className: "example",
|
||||
ariaLive: "polite", //OR ariaset: { live: "polite" },
|
||||
dataset: { example: "Example" }, //OR dataExample: "Example",
|
||||
}),
|
||||
el("button",
|
||||
{ textContent: "Fire", type: "button" },
|
||||
on("click", ()=> clicks(clicks() + 1)),
|
||||
on("keyup", ()=> clicks(clicks() - 2)),
|
||||
on("click", ()=> clicks.set(clicks.get() + 1)),
|
||||
on("keyup", ()=> clicks.set(clicks.get() - 2)),
|
||||
),
|
||||
el("select", null, onChange).append(
|
||||
el(OptionComponent, "🎉", isSelected),//OR { textContent: "🎉" }
|
||||
|
37
docs/components/examples/ireland-test/counter.js
Normal file
37
docs/components/examples/ireland-test/counter.js
Normal 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: "+",
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
2
docs/components/examples/optimization/intro.js
Normal file
2
docs/components/examples/optimization/intro.js
Normal 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";
|
115
docs/components/examples/optimization/memo.js
Normal file
115
docs/components/examples/optimization/memo.js
Normal 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));
|
381
docs/components/examples/reallife/todomvc.js
Normal file
381
docs/components/examples/reallife/todomvc.js
Normal file
@ -0,0 +1,381 @@
|
||||
import { dispatchEvent, el, memo, on, scope } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
||||
/**
|
||||
* Main TodoMVC application component
|
||||
*
|
||||
* Creates and manages the TodoMVC application with the following features:
|
||||
* - Todo items management (add, edit, delete)
|
||||
* - Filtering by status (all, active, completed)
|
||||
* - Client-side routing via URL hash
|
||||
* - Persistent storage with localStorage
|
||||
*
|
||||
* @returns {HTMLElement} The root TodoMVC application element
|
||||
*/
|
||||
function Todos(){
|
||||
const { signal } = scope;
|
||||
const pageS = routerSignal(S, signal);
|
||||
const todosS = todosSignal();
|
||||
/** Derived signal that filters todos based on current route */
|
||||
const todosFilteredS = S(()=> {
|
||||
const todos = todosS.get();
|
||||
const filter = pageS.get();
|
||||
if (filter === "all") return todos;
|
||||
return todos.filter(todo => {
|
||||
if (filter === "active") return !todo.completed;
|
||||
if (filter === "completed") return todo.completed;
|
||||
});
|
||||
});
|
||||
const todosRemainingS = S(()=> todosS.get().filter(todo => !todo.completed).length);
|
||||
|
||||
/** @type {ddeElementAddon<HTMLInputElement>} */
|
||||
const onToggleAll = on("change", event => {
|
||||
const checked = /** @type {HTMLInputElement} */ (event.target).checked;
|
||||
S.action(todosS, "completeAll", checked);
|
||||
});
|
||||
const formNewTodo = "newTodo";
|
||||
/** @type {ddeElementAddon<HTMLFormElement>} */
|
||||
const onSubmitNewTodo = on("submit", event => {
|
||||
event.preventDefault();
|
||||
const input = /** @type {HTMLInputElement} */(
|
||||
/** @type {HTMLFormElement} */(event.target).elements.namedItem(formNewTodo)
|
||||
);
|
||||
const title = input.value.trim();
|
||||
if (!title) return;
|
||||
|
||||
S.action(todosS, "add", title);
|
||||
input.value = "";
|
||||
});
|
||||
const onClearCompleted = on("click", () => S.action(todosS, "clearCompleted"));
|
||||
const onDelete = on("todo:delete", ev =>
|
||||
S.action(todosS, "delete", /** @type {{ detail: Todo["id"] }} */(ev).detail));
|
||||
const onEdit = on("todo:edit", ev =>
|
||||
S.action(todosS, "edit", /** @type {{ detail: Partial<Todo> & { id: Todo["id"] } }} */(ev).detail));
|
||||
|
||||
return el("section", { className: "todoapp" }).append(
|
||||
el("header", { className: "header" }).append(
|
||||
el("h1", "todos"),
|
||||
el("form", null, onSubmitNewTodo).append(
|
||||
el("input", {
|
||||
className: "new-todo",
|
||||
name: formNewTodo,
|
||||
placeholder: "What needs to be done?",
|
||||
autocomplete: "off",
|
||||
autofocus: true
|
||||
})
|
||||
)
|
||||
),
|
||||
S.el(todosS, todos => !todos.length
|
||||
? el()
|
||||
: el("main", { className: "main" }).append(
|
||||
el("input", {
|
||||
id: "toggle-all",
|
||||
className: "toggle-all",
|
||||
type: "checkbox"
|
||||
}, onToggleAll),
|
||||
el("label", { htmlFor: "toggle-all", title: "Mark all as complete" }),
|
||||
el("ul", { className: "todo-list" }).append(
|
||||
S.el(todosFilteredS, filteredTodos => filteredTodos.map(todo =>
|
||||
memo(todo.id, ()=> el(TodoItem, todo, onDelete, onEdit)))
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
S.el(todosS, ({ length }) => !length
|
||||
? el()
|
||||
: el("footer", { className: "footer" }).append(
|
||||
el("span", { className: "todo-count" }).append(
|
||||
el("strong", length + " " + (length === 1 ? "item" : "items")),
|
||||
),
|
||||
memo("filters", ()=>
|
||||
el("ul", { className: "filters" }).append(
|
||||
...[ "All", "Active", "Completed" ].map(textContent =>
|
||||
el("li").append(
|
||||
el("a", {
|
||||
textContent,
|
||||
classList: { selected: S(()=> pageS.get() === textContent.toLowerCase()) },
|
||||
href: `#${textContent.toLowerCase()}`
|
||||
})
|
||||
)
|
||||
)
|
||||
),
|
||||
),
|
||||
length - todosRemainingS.get() === 0
|
||||
? el()
|
||||
: memo("delete", () =>
|
||||
el("button",
|
||||
{ textContent: "Clear completed", className: "clear-completed" },
|
||||
onClearCompleted)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo item data structure
|
||||
* @typedef {{ title: string, id: string, completed: boolean }} Todo
|
||||
*/
|
||||
|
||||
/**
|
||||
* Component for rendering an individual todo item
|
||||
*
|
||||
* Features:
|
||||
* - Display todo with completed state
|
||||
* - Toggle completion status
|
||||
* - Delete todo
|
||||
* - Edit todo with double-click
|
||||
* - Cancel edit with Escape key
|
||||
*
|
||||
* @param {Todo} todo - The todo item data
|
||||
* @fires {void} todo:delete - todo deletion event
|
||||
* @fires {Partial<Todo>} todo:edit - todo edits event
|
||||
*/
|
||||
function TodoItem({ id, title, completed }) {
|
||||
const { host }= scope;
|
||||
const isEditing = S(false);
|
||||
const isCompleted = S(completed);
|
||||
|
||||
/** @type {(id: string) => void} Dispatch function for deleting todo */
|
||||
const dispatchDelete= dispatchEvent("todo:delete", host);
|
||||
/** @type {(data: {id: string, [key: string]: any}) => void} Dispatch function for editing todo */
|
||||
const dispatchEdit = dispatchEvent("todo:edit", host);
|
||||
|
||||
/** @type {ddeElementAddon<HTMLInputElement>} */
|
||||
const onToggleCompleted = on("change", (ev) => {
|
||||
const completed= /** @type {HTMLInputElement} */(ev.target).checked;
|
||||
isCompleted.set(completed);
|
||||
dispatchEdit({ id, completed });
|
||||
});
|
||||
/** @type {ddeElementAddon<HTMLButtonElement>} */
|
||||
const onDelete = on("click", () => dispatchDelete(id));
|
||||
/** @type {ddeElementAddon<HTMLLabelElement>} */
|
||||
const onStartEdit = on("dblclick", () => isEditing.set(true));
|
||||
/** @type {ddeElementAddon<HTMLInputElement>} */
|
||||
const onBlurEdit = on("blur", event => {
|
||||
const value = /** @type {HTMLInputElement} */(event.target).value.trim();
|
||||
if (value) {
|
||||
dispatchEdit({ id, title: value });
|
||||
} else {
|
||||
dispatchDelete(id);
|
||||
}
|
||||
isEditing.set(false);
|
||||
});
|
||||
const formEdit = "edit";
|
||||
/** @type {ddeElementAddon<HTMLFormElement>} */
|
||||
const onSubmitEdit = on("submit", event => {
|
||||
event.preventDefault();
|
||||
const input = /** @type {HTMLFormElement} */(event.target).elements.namedItem(formEdit);
|
||||
const value = /** @type {HTMLInputElement} */(input).value.trim();
|
||||
if (value) {
|
||||
dispatchEdit({ id, title: value });
|
||||
} else {
|
||||
dispatchDelete(id);
|
||||
}
|
||||
isEditing.set(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* Event handler for keyboard events in edit mode
|
||||
* @type {ddeElementAddon<HTMLInputElement>}
|
||||
*/
|
||||
const onKeyDown = on("keydown", event => {
|
||||
if (event.key !== "Escape") return;
|
||||
isEditing.set(false);
|
||||
});
|
||||
|
||||
return el("li", { classList: { completed: isCompleted, editing: isEditing } }).append(
|
||||
el("div", { className: "view" }).append(
|
||||
el("input", {
|
||||
className: "toggle",
|
||||
type: "checkbox",
|
||||
checked: completed
|
||||
}, onToggleCompleted),
|
||||
el("label", { textContent: title }, onStartEdit),
|
||||
el("button", { ariaLabel: "Delete todo", className: "destroy" }, onDelete)
|
||||
),
|
||||
S.el(isEditing, editing => !editing
|
||||
? el()
|
||||
: el("form", null, onSubmitEdit).append(
|
||||
el("input", {
|
||||
className: "edit",
|
||||
name: formEdit,
|
||||
value: title,
|
||||
}, onBlurEdit, onKeyDown, addFocus)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Set up the document head
|
||||
document.head.append(
|
||||
el("title", "TodoMVC: dd<el>"),
|
||||
el("meta", { name: "description", content: "A TodoMVC implementation using dd<el>." }),
|
||||
el("link", {
|
||||
rel: "stylesheet",
|
||||
href: "https://cdn.jsdelivr.net/npm/todomvc-common@1.0.5/base.css"
|
||||
}),
|
||||
el("link", {
|
||||
rel: "stylesheet",
|
||||
href: "https://cdn.jsdelivr.net/npm/todomvc-app-css@2.4.2/index.css"
|
||||
})
|
||||
);
|
||||
|
||||
// Set up the document body
|
||||
document.body.append(
|
||||
el(Todos),
|
||||
el("footer", { className: "info" }).append(
|
||||
el("p", "Double-click to edit a todo"),
|
||||
el("p").append(
|
||||
"Created with ",
|
||||
el("a", { textContent: "deka-dom-el", href: "https://github.com/jaandrle/deka-dom-el" })
|
||||
),
|
||||
el("p").append(
|
||||
"Part of ",
|
||||
el("a", { textContent: "TodoMVC", href: "http://todomvc.com" })
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Utility function to set focus on an input element
|
||||
* Uses requestAnimationFrame to ensure the element is rendered
|
||||
* before trying to focus it
|
||||
*
|
||||
* @param {HTMLInputElement} editInput - The input element to focus
|
||||
* @returns {number} The requestAnimationFrame ID
|
||||
*/
|
||||
function addFocus(editInput){
|
||||
return requestAnimationFrame(()=> {
|
||||
editInput.focus();
|
||||
editInput.selectionStart = editInput.selectionEnd = editInput.value.length;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a signal for managing todos with persistence
|
||||
*
|
||||
* Features:
|
||||
* - Loads todos from localStorage on initialization
|
||||
* - Automatically saves todos to localStorage on changes
|
||||
* - Provides actions for adding, editing, deleting todos
|
||||
*/
|
||||
function todosSignal(){
|
||||
const store_key = "dde-todos";
|
||||
// Try to load todos from localStorage
|
||||
let savedTodos = [];
|
||||
try {
|
||||
const stored = localStorage.getItem(store_key);
|
||||
if (stored) {
|
||||
savedTodos = JSON.parse(stored);
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
const out= S(/** @type {Todo[]} */(savedTodos || []), {
|
||||
/**
|
||||
* Add a new todo
|
||||
* @param {string} value - The title of the new todo
|
||||
*/
|
||||
add(value){
|
||||
this.value.push({
|
||||
completed: false,
|
||||
title: value,
|
||||
id: uuid(),
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Edit an existing todo
|
||||
* @param {{ id: string, [key: string]: any }} data - Object containing id and fields to update
|
||||
*/
|
||||
edit({ id, ...update }){
|
||||
const index = this.value.findIndex(t => t.id === id);
|
||||
if (index === -1) return this.stopPropagation();
|
||||
Object.assign(this.value[index], update);
|
||||
},
|
||||
/**
|
||||
* Delete a todo by id
|
||||
* @param {string} id - The id of the todo to delete
|
||||
*/
|
||||
delete(id){
|
||||
const index = this.value.findIndex(t => t.id === id);
|
||||
if (index === -1) return this.stopPropagation();
|
||||
this.value.splice(index, 1);
|
||||
},
|
||||
/**
|
||||
* Remove all completed todos
|
||||
*/
|
||||
clearCompleted() {
|
||||
this.value = this.value.filter(todo => !todo.completed);
|
||||
},
|
||||
completeAll(state= true) {
|
||||
this.value.forEach(todo => todo.completed = state);
|
||||
},
|
||||
/**
|
||||
* Handle cleanup when signal is cleared
|
||||
*/
|
||||
[S.symbols.onclear](){
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Save todos to localStorage whenever the signal changes
|
||||
* @param {Todo[]} value - Current todos array
|
||||
*/
|
||||
S.on(out, /** @param {Todo[]} value */ function saveTodos(value) {
|
||||
try {
|
||||
localStorage.setItem(store_key, JSON.stringify(value));
|
||||
} catch (e) {
|
||||
console.error("Failed to save todos to localStorage", e);
|
||||
// Optionally, provide user feedback
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a signal for managing route state
|
||||
*
|
||||
* @param {typeof S} signal - The signal constructor from a library
|
||||
* @param {AbortSignal} abortSignal
|
||||
*/
|
||||
function routerSignal(signal, abortSignal){
|
||||
const initial = location.hash.replace("#", "") || "all";
|
||||
const out = signal(initial, {
|
||||
/**
|
||||
* Set the current route
|
||||
* @param {"all"|"active"|"completed"} hash - The route to set
|
||||
*/
|
||||
set(hash){
|
||||
location.hash = hash;
|
||||
//this.value = hash;
|
||||
},
|
||||
});
|
||||
|
||||
// Setup hash change listener
|
||||
window.addEventListener("hashchange", () => {
|
||||
const hash = location.hash.replace("#", "") || "all";
|
||||
//S.action(out, "set", /** @type {"all"|"active"|"completed"} */(hash));
|
||||
out.set(hash);
|
||||
}, { signal: abortSignal });
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a RFC4122 version 4 compliant UUID
|
||||
* Used to create unique identifiers for todo items
|
||||
*
|
||||
* @returns {string} A randomly generated UUID
|
||||
*/
|
||||
function uuid() {
|
||||
let uuid = "";
|
||||
for (let i = 0; i < 32; i++) {
|
||||
let random = (Math.random() * 16) | 0;
|
||||
|
||||
if (i === 8 || i === 12 || i === 16 || i === 20)
|
||||
uuid += "-";
|
||||
|
||||
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16);
|
||||
}
|
||||
return uuid;
|
||||
}
|
@ -14,7 +14,7 @@ function component(){
|
||||
const textContent= S("Click to change text.");
|
||||
|
||||
const onclickChange= on("click", function redispatch(){
|
||||
textContent("Text changed! "+(new Date()).toString())
|
||||
textContent.set("Text changed! "+(new Date()).toString())
|
||||
});
|
||||
return el("p", textContent, onclickChange);
|
||||
}
|
||||
|
@ -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 });
|
||||
}
|
||||
|
@ -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!!!
|
||||
* */
|
||||
}
|
||||
|
38
docs/components/examples/scopes/mixed.js
Normal file
38
docs/components/examples/scopes/mixed.js
Normal 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"
|
||||
})
|
||||
);
|
||||
}
|
45
docs/components/examples/scopes/with-scope.js
Normal file
45
docs/components/examples/scopes/with-scope.js
Normal 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)
|
||||
);
|
@ -15,4 +15,4 @@ setTimeout(
|
||||
clearInterval,
|
||||
10*interval,
|
||||
setInterval(oninterval, interval)
|
||||
);
|
||||
);
|
@ -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*/:
|
||||
|
@ -1,16 +1,16 @@
|
||||
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));
|
||||
|
||||
setTimeout(()=> ac.abort(), 3*interval)
|
||||
setTimeout(()=> ac.abort(), 3*interval)
|
20
docs/components/examples/signals/debugging-console.js
Normal file
20
docs/components/examples/signals/debugging-console.js
Normal 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
|
38
docs/components/examples/signals/debugging-dom.js
Normal file
38
docs/components/examples/signals/debugging-dom.js
Normal 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
|
13
docs/components/examples/signals/derived.js
Normal file
13
docs/components/examples/signals/derived.js
Normal 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"
|
@ -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 === 0) }, 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));
|
@ -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());
|
||||
}, interval));
|
||||
S.action(numbers, "push", count.get());
|
||||
}, interval));
|
@ -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;
|
||||
|
47
docs/components/examples/ssr/async-data.js
Normal file
47
docs/components/examples/ssr/async-data.js
Normal 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)
|
||||
);
|
||||
}
|
48
docs/components/examples/ssr/basic-example.js
Normal file
48
docs/components/examples/ssr/basic-example.js
Normal 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);
|
2
docs/components/examples/ssr/intro.js
Normal file
2
docs/components/examples/ssr/intro.js
Normal 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";
|
36
docs/components/examples/ssr/pages.js
Normal file
36
docs/components/examples/ssr/pages.js
Normal 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();
|
||||
}
|
27
docs/components/examples/ssr/start.js
Normal file
27
docs/components/examples/ssr/start.js
Normal 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();
|
45
docs/components/examples/ssr/static-site-generator.js
Normal file
45
docs/components/examples/ssr/static-site-generator.js
Normal 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);
|
83
docs/components/getLibraryUrl.html.js
Normal file
83
docs/components/getLibraryUrl.html.js
Normal file
@ -0,0 +1,83 @@
|
||||
import { styles, page_id } from "../ssr.js";
|
||||
|
||||
styles.css`
|
||||
#library-url-form {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--bg-sidebar);
|
||||
box-shadow: var(--shadow);
|
||||
border: 1px solid var(--border);
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
#library-url-form .selectors {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
#library-url-form output {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
#library-url-form output p {
|
||||
font-weight: 500;
|
||||
margin: 0.25rem 0;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
#library-url-form .url-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: -0.25rem;
|
||||
}
|
||||
|
||||
#library-url-form .url-title strong {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
#library-url-form .url-title span {
|
||||
color: var(--text-light);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
#library-url-form .code {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
#library-url-form .info-text {
|
||||
font-size: 0.9rem;
|
||||
font-style: italic;
|
||||
margin-top: 1rem;
|
||||
color: var(--text-light);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
#library-url-form .selectors {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#library-url-form select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
import { el } from "deka-dom-el";
|
||||
import { ireland } from "./ireland.html.js";
|
||||
|
||||
export function getLibraryUrl(){
|
||||
return el(ireland, {
|
||||
src: new URL("./getLibraryUrl.js.js", import.meta.url),
|
||||
exportName: "getLibraryUrl",
|
||||
page_id,
|
||||
});
|
||||
}
|
92
docs/components/getLibraryUrl.js.js
Normal file
92
docs/components/getLibraryUrl.js.js
Normal file
@ -0,0 +1,92 @@
|
||||
import { el, on } from "deka-dom-el";
|
||||
import { S } from "deka-dom-el/signals";
|
||||
|
||||
const url_base= {
|
||||
jsdeka: "https://cdn.jsdelivr.net/gh/jaandrle/deka-dom-el/dist/",
|
||||
};
|
||||
|
||||
export function getLibraryUrl(){
|
||||
const lib= S([ "esm", "-with-signals", ".min" ]);
|
||||
const url= S(()=> url_base.jsdeka+lib.get().join(""));
|
||||
const urlLabel= S(() => {
|
||||
const [format, signalsPart, minified] = lib.get();
|
||||
const formatText = format === "esm" ? "ES Module" : "IIFE";
|
||||
const signalsText = signalsPart ? " with signals" : "";
|
||||
const minText = minified ? " (minified)" : "";
|
||||
return `${formatText}${signalsText}${minText}`;
|
||||
})
|
||||
const onSubmit= on("submit", ev => {
|
||||
ev.preventDefault();
|
||||
const form= new FormData(/** @type {HTMLFormElement} */ (ev.target));
|
||||
lib.set([
|
||||
"module",
|
||||
"what",
|
||||
"minified",
|
||||
].map(name => /** @type {string} */(form.get(name))));
|
||||
});
|
||||
const onChangeSubmit= on("change",
|
||||
ev=> /** @type {HTMLSelectElement} */(ev.target).form.requestSubmit()
|
||||
);
|
||||
|
||||
return el("form", { id: "library-url-form" }, onSubmit).append(
|
||||
el("h4", "Select your preferred library format:"),
|
||||
el("div", { className: "selectors" }).append(
|
||||
el("select", { name: "module" }, onChangeSubmit,
|
||||
on.defer(select => select.value = lib.get()[0]),
|
||||
).append(
|
||||
el("option", { value: "esm", textContent: "ESM — modern JavaScript module" }),
|
||||
el("option", { value: "iife", textContent: "IIFE — legacy JavaScript with DDE global variable" }),
|
||||
),
|
||||
el("select", { name: "what" }, onChangeSubmit,
|
||||
on.defer(select => select.value = lib.get()[1]),
|
||||
).append(
|
||||
el("option", { value: "", textContent: "DOM part only" }),
|
||||
el("option", { value: "-with-signals", textContent: "DOM + signals" }),
|
||||
),
|
||||
el("select", { name: "minified" }, onChangeSubmit,
|
||||
on.defer(select => select.value = lib.get()[2]),
|
||||
).append(
|
||||
el("option", { value: "", textContent: "Unminified" }),
|
||||
el("option", { value: ".min", textContent: "Minified" }),
|
||||
),
|
||||
),
|
||||
el("output").append(
|
||||
el("div", { className: "url-title" }).append(
|
||||
el("strong", "JavaScript:"),
|
||||
el("span", urlLabel),
|
||||
),
|
||||
el(code, { value: S(()=> url.get()+".js") }),
|
||||
el("div", { className: "url-title" }).append(
|
||||
el("strong", "TypeScript definition:")
|
||||
),
|
||||
el(code, { value: S(()=> url.get()+".d.ts") }),
|
||||
el("p", { className: "info-text",
|
||||
textContent: "Use the CDN URL in your HTML or import it in your JavaScript files."
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
/** @param {{ value: ddeSignal<string> }} props */
|
||||
function code({ value }){
|
||||
/** @type {ddeSignal<"Copy"|"Copied!">} */
|
||||
const textContent= S("Copy");
|
||||
const onCopy= on("click", () => {
|
||||
navigator.clipboard.writeText(value.get());
|
||||
|
||||
textContent.set("Copied!");
|
||||
setTimeout(() => {
|
||||
textContent.set("Copy");
|
||||
}, 1500);
|
||||
});
|
||||
return el("div", { className: "code", dataJs: "done", tabIndex: 0 }).append(
|
||||
el("code").append(
|
||||
el("pre", value),
|
||||
),
|
||||
el("button", {
|
||||
className: "copy-button",
|
||||
textContent,
|
||||
ariaLabel: "Copy code to clipboard",
|
||||
}, onCopy)
|
||||
)
|
||||
;
|
||||
}
|
124
docs/components/ireland.html.js
Normal file
124
docs/components/ireland.html.js
Normal file
@ -0,0 +1,124 @@
|
||||
import { styles } from "../ssr.js";
|
||||
styles.css`
|
||||
[data-dde-mark] {
|
||||
opacity: .5;
|
||||
filter: grayscale();
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
animation: fadein 2s infinite ease forwards;;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
&::after {
|
||||
content: "Loading Ireland…";
|
||||
background-color: rgba(0, 0, 0, .5);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
border-radius: 5px;
|
||||
padding: 5px 10px;
|
||||
position: absolute;
|
||||
top: 3%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
@keyframes fadein {
|
||||
from { opacity: .5; }
|
||||
to { opacity: .85; }
|
||||
}
|
||||
`;
|
||||
|
||||
import { el, queue } from "deka-dom-el";
|
||||
import { addEventListener, registerClientFile } from "../ssr.js";
|
||||
import { relative } from "node:path";
|
||||
|
||||
const dir= new URL("./", import.meta.url).pathname;
|
||||
const dirFE= "irelands";
|
||||
// Track all component instances for client-side rehydration
|
||||
const componentsRegistry = new Map();
|
||||
/**
|
||||
* Creates a component that shows code and its runtime output
|
||||
* with server-side pre-rendering and client-side rehydration
|
||||
*
|
||||
* @param {object} attrs
|
||||
* @param {URL} attrs.src - Path to the file containing the component
|
||||
* @param {string} [attrs.exportName="default"] - Name of the export to use
|
||||
* @param {object} [attrs.props={}] - Props to pass to the component
|
||||
*/
|
||||
export function ireland({ src, exportName = "default", props = {} }) {
|
||||
// relative src against the current directory
|
||||
const path= "./"+relative(dir, src.pathname);
|
||||
const id = "ireland-" + generateComponentId(src);
|
||||
const element = el.mark({ type: "later", name: ireland.name });
|
||||
queue(
|
||||
import(path)
|
||||
.then(module => {
|
||||
const component = module[exportName];
|
||||
const content= el(component, props, mark(id));
|
||||
element.replaceWith(content);
|
||||
content.querySelectorAll("input, textarea, button")
|
||||
.forEach(el=> el.disabled= true);
|
||||
})
|
||||
.catch(console.error)
|
||||
);
|
||||
|
||||
if(!componentsRegistry.size)
|
||||
addEventListener("oneachrender", registerClientPart);
|
||||
componentsRegistry.set(id, {
|
||||
src,
|
||||
path: dirFE+"/"+path.split("/").pop(),
|
||||
exportName,
|
||||
props,
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
function registerClientPart(){
|
||||
const todo= Array.from(componentsRegistry.entries())
|
||||
.map(([ id, d ]) => {
|
||||
registerClientFile(d.src, {
|
||||
folder: dirFE,
|
||||
// not all browsers support importmap
|
||||
replacer(file){
|
||||
return file
|
||||
.replaceAll(/ from "deka-dom-el(\/signals)?";/g, ` from "./esm-with-signals.js";`);
|
||||
}
|
||||
});
|
||||
return [ id, d ];
|
||||
});
|
||||
const store = JSON.stringify(JSON.stringify(todo));
|
||||
registerClientFile(new URL("./ireland.js.js", import.meta.url));
|
||||
registerClientFile(new URL("../../dist/esm-with-signals.js", import.meta.url), { folder: dirFE });
|
||||
document.head.append(
|
||||
// not all browsers support importmap
|
||||
el("script", { type: "importmap" }).append(`
|
||||
{
|
||||
"imports": {
|
||||
"deka-dom-el": "./${dirFE}/esm-with-signals.js",
|
||||
"deka-dom-el/signals": "./${dirFE}/esm-with-signals.js"
|
||||
}
|
||||
}
|
||||
`.trim())
|
||||
);
|
||||
document.body.append(
|
||||
el("script", { type: "module" }).append(`
|
||||
import { loadIrelands } from "./ireland.js.js";
|
||||
loadIrelands(new Map(JSON.parse(${store})));
|
||||
`.trim())
|
||||
)
|
||||
}
|
||||
function mark(id) { return el=> el.dataset.ddeMark= id; }
|
||||
const store_prev= new Map();
|
||||
/** @param {URL} src */
|
||||
function generateComponentId(src){
|
||||
const candidate= parseInt(relative((new URL("..", import.meta.url)).pathname, src.pathname)
|
||||
.split("")
|
||||
.map(ch=> ch.charCodeAt(0))
|
||||
.join(""), 10)
|
||||
.toString(36)
|
||||
.replace(/000+/g, "");
|
||||
const count= 1 + ( store_prev.get(candidate) || 0 );
|
||||
store_prev.set(candidate, count);
|
||||
return count.toString()+"-"+candidate;
|
||||
}
|
13
docs/components/ireland.js.js
Normal file
13
docs/components/ireland.js.js
Normal file
@ -0,0 +1,13 @@
|
||||
// not all browsers support importmaps
|
||||
// import { el } from "deka-dom-el";
|
||||
import { el } from "./irelands/esm-with-signals.js";
|
||||
export function loadIrelands(store) {
|
||||
document.body.querySelectorAll("[data-dde-mark]").forEach(ireland => {
|
||||
const { ddeMark }= ireland.dataset;
|
||||
if(!store.has(ddeMark)) return;
|
||||
const { path, exportName, props }= store.get(ddeMark);
|
||||
import("./"+path).then(module => {
|
||||
ireland.replaceWith(el(module[exportName], props));
|
||||
})
|
||||
});
|
||||
}
|
@ -11,10 +11,6 @@ export function mnemonic(){
|
||||
el("code", "customElementWithDDE(<custom-element>)"),
|
||||
" — register <custom-element> to DDE library, see also `lifecyclesToEvents`, can be also used as decorator",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "observedAttributes(<custom-element>)"),
|
||||
" — returns record of observed attributes (keys uses camelCase)",
|
||||
),
|
||||
el("li").append(
|
||||
el("code", "S.observedAttributes(<custom-element>)"),
|
||||
" — returns record of observed attributes (keys uses camelCase and values are signals)",
|
||||
@ -32,4 +28,4 @@ export function mnemonic(){
|
||||
" — simulate slots for “dde”/functional components",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user