Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea895024c0 | |||
| 078ccdf1c3 | |||
| 918769e416 | |||
| 039ee19ffd | |||
| d16bc1652d | |||
| 6fa99f8ed8 | |||
| 5ce92130b4 | |||
| b0774b0cba |
128
.gitlab-ci.yml
128
.gitlab-ci.yml
@@ -1,128 +0,0 @@
|
|||||||
# gitzone ci_default
|
|
||||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
|
||||||
|
|
||||||
cache:
|
|
||||||
paths:
|
|
||||||
- .npmci_cache/
|
|
||||||
key: '$CI_BUILD_STAGE'
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- security
|
|
||||||
- test
|
|
||||||
- release
|
|
||||||
- metadata
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- pnpm install -g pnpm
|
|
||||||
- pnpm install -g @shipzone/npmci
|
|
||||||
- npmci npm prepare
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# security stage
|
|
||||||
# ====================
|
|
||||||
# ====================
|
|
||||||
# security stage
|
|
||||||
# ====================
|
|
||||||
auditProductionDependencies:
|
|
||||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
|
||||||
stage: security
|
|
||||||
script:
|
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
|
||||||
- npmci command pnpm audit --audit-level=high --prod
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
allow_failure: true
|
|
||||||
|
|
||||||
auditDevDependencies:
|
|
||||||
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
|
|
||||||
stage: security
|
|
||||||
script:
|
|
||||||
- npmci command npm config set registry https://registry.npmjs.org
|
|
||||||
- npmci command pnpm audit --audit-level=high --dev
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
allow_failure: true
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# test stage
|
|
||||||
# ====================
|
|
||||||
|
|
||||||
testStable:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm install
|
|
||||||
- npmci npm test
|
|
||||||
coverage: /\d+.?\d+?\%\s*coverage/
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
testBuild:
|
|
||||||
stage: test
|
|
||||||
script:
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm install
|
|
||||||
- npmci npm build
|
|
||||||
coverage: /\d+.?\d+?\%\s*coverage/
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
release:
|
|
||||||
stage: release
|
|
||||||
script:
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm publish
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
|
|
||||||
# ====================
|
|
||||||
# metadata stage
|
|
||||||
# ====================
|
|
||||||
codequality:
|
|
||||||
stage: metadata
|
|
||||||
allow_failure: true
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
script:
|
|
||||||
- npmci command npm install -g typescript
|
|
||||||
- npmci npm prepare
|
|
||||||
- npmci npm install
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- priv
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
stage: metadata
|
|
||||||
script:
|
|
||||||
- npmci trigger
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
|
|
||||||
pages:
|
|
||||||
stage: metadata
|
|
||||||
script:
|
|
||||||
- npmci node install stable
|
|
||||||
- npmci npm install
|
|
||||||
- npmci command npm run buildDocs
|
|
||||||
tags:
|
|
||||||
- lossless
|
|
||||||
- docker
|
|
||||||
- notpriv
|
|
||||||
only:
|
|
||||||
- tags
|
|
||||||
artifacts:
|
|
||||||
expire_in: 1 week
|
|
||||||
paths:
|
|
||||||
- public
|
|
||||||
allow_failure: true
|
|
||||||
41
changelog.md
41
changelog.md
@@ -1,5 +1,46 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-16 - 2.3.5 - fix(dependencies)
|
||||||
|
bump dependency versions in package.json
|
||||||
|
|
||||||
|
- Updated devDependencies:
|
||||||
|
- - @git.zone/tsbuild: ^2.6.4 -> ^2.7.1
|
||||||
|
- - @git.zone/tsbundle: ^2.4.0 -> ^2.5.1
|
||||||
|
- - @git.zone/tstest: ^2.3.1 -> ^2.7.0
|
||||||
|
- Updated dependencies:
|
||||||
|
- - @push.rocks/smartjson: ^5.0.20 -> ^5.2.0
|
||||||
|
- - @push.rocks/smartrouter: ^1.3.2 -> ^1.3.3
|
||||||
|
- - @push.rocks/smartstate: ^2.0.20 -> ^2.0.27
|
||||||
|
- - @push.rocks/smartstring: ^4.0.15 -> ^4.1.0
|
||||||
|
|
||||||
|
## 2025-11-16 - 2.3.4 - fix(domtools)
|
||||||
|
Prevent race conditions during DomTools initialization and improve runOnce error handling
|
||||||
|
|
||||||
|
- Add a static initializationPromise to serialize and await concurrent DomTools.setupDomTools() calls to avoid race conditions when multiple initializations are requested.
|
||||||
|
- Initialize Keyboard only after the document is ready (check document.readyState and use readystatechange listener) so document.body/head are available before creating the Keyboard instance; resolve domReady and domToolsReady appropriately.
|
||||||
|
- Support ignoreGlobal path to create isolated DomTools instances while keeping global initialization guarded by initializationPromise.
|
||||||
|
- Enhance runOnce: store errors from the first execution and re-throw them to all waiting callers; always clear the running flag in a finally block to prevent permanent stuck state.
|
||||||
|
|
||||||
|
## 2025-06-20 - 2.3.3 - fix(package.json)
|
||||||
|
Update dependency versions and add pnpm package manager field
|
||||||
|
|
||||||
|
- Update @git.zone/tsbuild from ^2.2.1 to ^2.6.4
|
||||||
|
- Update @git.zone/tsbundle from ^2.2.5 to ^2.4.0
|
||||||
|
- Update @git.zone/tstest from ^1.0.96 to ^2.3.1
|
||||||
|
- Update @push.rocks/tapbundle from ^5.5.6 to ^6.0.3
|
||||||
|
- Update @push.rocks/lik from ^6.1.0 to ^6.2.2
|
||||||
|
- Update @push.rocks/smartpromise from ^4.2.2 to ^4.2.3
|
||||||
|
- Update @push.rocks/smartrx from ^3.0.7 to ^3.0.10
|
||||||
|
- Update @push.rocks/smartstate from ^2.0.19 to ^2.0.20
|
||||||
|
- Update lenis from ^1.1.20 to ^1.3.4
|
||||||
|
- Update lit from ^3.2.1 to ^3.3.0
|
||||||
|
- Add packageManager field for pnpm
|
||||||
|
|
||||||
|
## 2025-02-01 - 2.3.2 - fix(scroller)
|
||||||
|
Rename method from scrollToElement to toElement for consistency
|
||||||
|
|
||||||
|
- Updated method name in Scroller class for better coherence with existing naming conventions.
|
||||||
|
|
||||||
## 2025-01-31 - 2.3.1 - fix(scroller)
|
## 2025-01-31 - 2.3.1 - fix(scroller)
|
||||||
Removed passive option from scroll event listener
|
Removed passive option from scroll event listener
|
||||||
|
|
||||||
|
|||||||
31
package.json
31
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@design.estate/dees-domtools",
|
"name": "@design.estate/dees-domtools",
|
||||||
"version": "2.3.1",
|
"version": "2.3.5",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.",
|
"description": "A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
@@ -15,30 +15,30 @@
|
|||||||
"buildDocs": "tsdoc"
|
"buildDocs": "tsdoc"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^2.2.1",
|
"@git.zone/tsbuild": "^2.7.1",
|
||||||
"@git.zone/tsbundle": "^2.2.5",
|
"@git.zone/tsbundle": "^2.5.1",
|
||||||
"@git.zone/tstest": "^1.0.96",
|
"@git.zone/tstest": "^2.7.0",
|
||||||
"@push.rocks/tapbundle": "^5.5.6",
|
"@push.rocks/tapbundle": "^6.0.3",
|
||||||
"@types/node": "^22.12.0"
|
"@types/node": "^22.12.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@api.global/typedrequest": "^3.1.10",
|
"@api.global/typedrequest": "^3.1.10",
|
||||||
"@design.estate/dees-comms": "^1.0.27",
|
"@design.estate/dees-comms": "^1.0.27",
|
||||||
"@push.rocks/lik": "^6.1.0",
|
"@push.rocks/lik": "^6.2.2",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartjson": "^5.0.20",
|
"@push.rocks/smartjson": "^5.2.0",
|
||||||
"@push.rocks/smartmarkdown": "^3.0.3",
|
"@push.rocks/smartmarkdown": "^3.0.3",
|
||||||
"@push.rocks/smartpromise": "^4.2.2",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"@push.rocks/smartrouter": "^1.3.2",
|
"@push.rocks/smartrouter": "^1.3.3",
|
||||||
"@push.rocks/smartrx": "^3.0.7",
|
"@push.rocks/smartrx": "^3.0.10",
|
||||||
"@push.rocks/smartstate": "^2.0.19",
|
"@push.rocks/smartstate": "^2.0.27",
|
||||||
"@push.rocks/smartstring": "^4.0.15",
|
"@push.rocks/smartstring": "^4.1.0",
|
||||||
"@push.rocks/smarturl": "^3.1.0",
|
"@push.rocks/smarturl": "^3.1.0",
|
||||||
"@push.rocks/webrequest": "^3.0.37",
|
"@push.rocks/webrequest": "^3.0.37",
|
||||||
"@push.rocks/websetup": "^3.0.19",
|
"@push.rocks/websetup": "^3.0.19",
|
||||||
"@push.rocks/webstore": "^2.0.20",
|
"@push.rocks/webstore": "^2.0.20",
|
||||||
"lenis": "^1.1.20",
|
"lenis": "^1.3.4",
|
||||||
"lit": "^3.2.1",
|
"lit": "^3.3.0",
|
||||||
"sweet-scroll": "^4.0.0"
|
"sweet-scroll": "^4.0.0"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@@ -71,5 +71,6 @@
|
|||||||
"state management",
|
"state management",
|
||||||
"routing",
|
"routing",
|
||||||
"performance optimization"
|
"performance optimization"
|
||||||
]
|
],
|
||||||
|
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
|
||||||
}
|
}
|
||||||
|
|||||||
7022
pnpm-lock.yaml
generated
7022
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
497
readme.md
497
readme.md
@@ -1,100 +1,475 @@
|
|||||||
# @design.estate/dees-domtools
|
# @design.estate/dees-domtools
|
||||||
tools to simplify complex css structures
|
|
||||||
|
|
||||||
## Install
|
> 🎨 A comprehensive TypeScript toolkit for simplifying DOM manipulation, CSS management, and web component development
|
||||||
|
|
||||||
To install `@design.estate/dees-domtools`, simply use npm:
|
Modern web development made elegant. `@design.estate/dees-domtools` provides a powerful suite of utilities for managing complex CSS structures, handling browser events, implementing smooth scrolling, and building responsive web applications with ease.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
🚀 **Smart DOM Management** - Singleton-based DomTools instance with race-condition-free initialization
|
||||||
|
📱 **Responsive Breakpoints** - Built-in support for desktop, tablet, phablet, and phone viewports with container queries
|
||||||
|
🎭 **Theme Management** - Automatic dark/light mode detection and switching with RxJS observables
|
||||||
|
⌨️ **Keyboard Shortcuts** - Elegant keyboard event handling with combo support
|
||||||
|
📜 **Smooth Scrolling** - Native and Lenis-powered smooth scrolling with automatic detection
|
||||||
|
🎯 **State Management** - Integrated state management with smartstate
|
||||||
|
🧭 **Routing** - Client-side routing with smartrouter
|
||||||
|
🌐 **WebSetup** - Easy management of website metadata, favicons, and SEO tags
|
||||||
|
💅 **CSS Utilities** - Grid helpers, breakpoint utilities, and base styles for web components
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install --save @design.estate/dees-domtools
|
npm install @design.estate/dees-domtools
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensure your project supports TypeScript and ESM syntax for the best development experience.
|
Or with pnpm:
|
||||||
|
|
||||||
## Usage
|
```bash
|
||||||
|
pnpm add @design.estate/dees-domtools
|
||||||
|
```
|
||||||
|
|
||||||
This documentation aims to give you comprehensive guidance on how to utilize `@design.estate/dees-domtools` in your web projects to simplify complex CSS structures. Whether you're building a sophisticated web application or need a more manageable way to handle your CSS, this toolkit offers a variety of features designed to enhance and streamline your workflow. Throughout this guide, we'll cover the installation process, key features, examples, and best practices when working with `@design.estate/dees-domtools`.
|
## Quick Start
|
||||||
|
|
||||||
Before diving into the examples, please ensure that you've completed the installation step as described above.
|
|
||||||
|
|
||||||
### Key Features
|
|
||||||
|
|
||||||
- **CSS Simplification**: Tools and utilities designed to abstract and simplify the handling of complex CSS structures.
|
|
||||||
- **Responsive Design Helpers**: Tools for managing responsive designs more easily, with utilities for breakpoints, and adaptable grids.
|
|
||||||
- **Element Utilities**: Simplified interactions with DOM elements, including dynamic style manipulations and more.
|
|
||||||
- **Theme Management**: Simplify the process of theme toggling (dark mode/light mode) in your applications.
|
|
||||||
- **Keyboard Event Handling**: Utilities for managing keyboard events, facilitating better interaction handling.
|
|
||||||
- **External Resources Management**: Easily manage external CSS and JavaScript resources within your project.
|
|
||||||
|
|
||||||
### Getting Started
|
|
||||||
|
|
||||||
To get started with `@design.estate/dees-domtools`, you first need to import the modules you intend to use in your project. Here's a simple example to illustrate how to import and use the package:
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { DomTools, elementBasic, breakpoints, css } from '@design.estate/dees-domtools';
|
import { DomTools } from '@design.estate/dees-domtools';
|
||||||
|
|
||||||
// Setting up DomTools for your application
|
// Initialize DomTools (singleton pattern - safe to call multiple times)
|
||||||
const domToolsInstance = await DomTools.setupDomTools();
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
|
||||||
// Example: Using elementBasic utilities
|
// Wait for DOM to be ready
|
||||||
elementBasic.setup();
|
await domtools.domReady.promise;
|
||||||
|
|
||||||
|
// Now you're ready to rock! 🎸
|
||||||
|
console.log('DOM is ready, head and body elements are available');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Simplifying CSS with CSS Utilities
|
## Core API
|
||||||
|
|
||||||
The package offers a variety of CSS utilities that simplify the handling of complex CSS structures. Here's how you can use them in your project:
|
### DomTools Instance
|
||||||
|
|
||||||
|
The `DomTools` class is the heart of the library. It provides a singleton instance that manages all the utilities.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { DomTools } from '@design.estate/dees-domtools';
|
||||||
|
|
||||||
|
// Setup with options
|
||||||
|
const domtools = await DomTools.setupDomTools({
|
||||||
|
ignoreGlobal: false // Set to true to create isolated instance
|
||||||
|
});
|
||||||
|
|
||||||
|
// Access DOM elements (available after domReady)
|
||||||
|
await domtools.domReady.promise;
|
||||||
|
const head = domtools.elements.headElement;
|
||||||
|
const body = domtools.elements.bodyElement;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Properties:**
|
||||||
|
|
||||||
|
- `domtools.router` - SmartRouter instance for client-side routing
|
||||||
|
- `domtools.themeManager` - Theme management (dark/light mode)
|
||||||
|
- `domtools.scroller` - Smooth scrolling utilities
|
||||||
|
- `domtools.keyboard` - Keyboard event handling
|
||||||
|
- `domtools.websetup` - Website metadata management
|
||||||
|
- `domtools.smartstate` - State management
|
||||||
|
- `domtools.deesComms` - Communication utilities
|
||||||
|
|
||||||
|
**Lifecycle Promises:**
|
||||||
|
|
||||||
|
- `domtools.domToolsReady.promise` - Resolves when DomTools is initialized
|
||||||
|
- `domtools.domReady.promise` - Resolves when DOM is interactive/complete
|
||||||
|
- `domtools.globalStylesReady.promise` - Resolves when global styles are set
|
||||||
|
|
||||||
|
### Responsive Breakpoints
|
||||||
|
|
||||||
|
Built-in breakpoint system with both media queries and container queries:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { breakpoints, css } from '@design.estate/dees-domtools';
|
||||||
|
import { css as litCss } from 'lit';
|
||||||
|
|
||||||
|
// Breakpoint values (in pixels)
|
||||||
|
breakpoints.desktop // 1600px
|
||||||
|
breakpoints.notebook // 1240px
|
||||||
|
breakpoints.tablet // 1024px
|
||||||
|
breakpoints.phablet // 600px
|
||||||
|
breakpoints.phone // 400px
|
||||||
|
|
||||||
|
// Use with Lit components
|
||||||
|
const myStyles = litCss`
|
||||||
|
.container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
${breakpoints.cssForTablet(litCss`
|
||||||
|
.container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
`)}
|
||||||
|
|
||||||
|
${breakpoints.cssForPhone(litCss`
|
||||||
|
.container {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
`)}
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
||||||
|
Available breakpoint helpers:
|
||||||
|
- `cssForDesktop(css)` - Styles for 1600px and above
|
||||||
|
- `cssForNotebook(css)` - Styles for 1240px and below
|
||||||
|
- `cssForTablet(css)` - Styles for 1024px and below
|
||||||
|
- `cssForPhablet(css)` - Styles for 600px and below
|
||||||
|
- `cssForPhone(css)` - Styles for 400px and below
|
||||||
|
|
||||||
|
### Theme Management
|
||||||
|
|
||||||
|
Automatic theme detection with system preference support:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
const { themeManager } = domtools;
|
||||||
|
|
||||||
|
// Toggle between dark and light
|
||||||
|
themeManager.toggleDarkBright();
|
||||||
|
|
||||||
|
// Set specific theme
|
||||||
|
themeManager.goDark();
|
||||||
|
themeManager.goBright();
|
||||||
|
|
||||||
|
// Enable automatic global background changes
|
||||||
|
await themeManager.enableAutomaticGlobalThemeChange();
|
||||||
|
|
||||||
|
// Subscribe to theme changes
|
||||||
|
themeManager.themeObservable.subscribe((isBright) => {
|
||||||
|
console.log(`Theme is now: ${isBright ? 'light' : 'dark'}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check current theme
|
||||||
|
if (themeManager.goBrightBoolean) {
|
||||||
|
console.log('Light mode active');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keyboard Shortcuts
|
||||||
|
|
||||||
|
Handle keyboard events with ease, including complex combinations:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Keyboard, Key } from '@design.estate/dees-domtools';
|
||||||
|
|
||||||
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
await domtools.domReady.promise;
|
||||||
|
|
||||||
|
// Access the keyboard instance
|
||||||
|
const { keyboard } = domtools;
|
||||||
|
|
||||||
|
// Listen for Ctrl+S
|
||||||
|
keyboard.on([Key.Ctrl, Key.S]).subscribe((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
console.log('Save triggered!');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for Ctrl+Shift+P
|
||||||
|
keyboard.on([Key.Ctrl, Key.Shift, Key.P]).subscribe(() => {
|
||||||
|
console.log('Command palette opened!');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Programmatically trigger key presses
|
||||||
|
keyboard.triggerKeyPress([Key.Ctrl, Key.S]);
|
||||||
|
|
||||||
|
// Clean up when done
|
||||||
|
keyboard.stopListening();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available Keys:**
|
||||||
|
|
||||||
|
All standard keyboard keys are available in the `Key` enum, including:
|
||||||
|
- Modifiers: `Ctrl`, `Shift`, `Alt`
|
||||||
|
- Letters: `A` through `Z`
|
||||||
|
- Numbers: `Zero` through `Nine`
|
||||||
|
- Function keys: `F1` through `F12`
|
||||||
|
- Navigation: `Home`, `End`, `PageUp`, `PageDown`, arrows
|
||||||
|
- And many more...
|
||||||
|
|
||||||
|
### Smooth Scrolling
|
||||||
|
|
||||||
|
Powerful scrolling utilities with Lenis integration:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
const { scroller } = domtools;
|
||||||
|
|
||||||
|
// Scroll to an element smoothly
|
||||||
|
const targetElement = document.querySelector('#section-2');
|
||||||
|
await scroller.toElement(targetElement, {
|
||||||
|
duration: 1000,
|
||||||
|
easing: 'easeInOutQuad'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable Lenis smooth scrolling
|
||||||
|
await scroller.enableLenisScroll({
|
||||||
|
disableOnNativeSmoothScroll: true // Auto-disable if browser has native smooth scroll
|
||||||
|
});
|
||||||
|
|
||||||
|
// Register scroll callbacks
|
||||||
|
scroller.onScroll(() => {
|
||||||
|
console.log('Page scrolled!');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Detect if native smooth scrolling is enabled
|
||||||
|
const hasNativeSmooth = await scroller.detectNativeSmoothScroll();
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS Utilities
|
||||||
|
|
||||||
|
Helper functions for common CSS patterns:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { css } from '@design.estate/dees-domtools';
|
import { css } from '@design.estate/dees-domtools';
|
||||||
|
|
||||||
// Example: Creating a CSS grid with a specified number of columns and gap size
|
// Create responsive grid columns
|
||||||
const gridStyles = css.cssGridColumns(3, 10); // 3 columns with a 10px gap
|
const gridTemplate = css.cssGridColumns(4, 16);
|
||||||
|
// Returns: calc((100%/4) - (48px/4)) calc((100%/4) - (48px/4)) ...
|
||||||
|
|
||||||
|
// Use in your styles
|
||||||
|
const styles = `
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: ${gridTemplate};
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
```
|
```
|
||||||
|
|
||||||
### Managing Responsive Design
|
### Global Styles & External Resources
|
||||||
|
|
||||||
Responsive design is critical for modern web development. `@design.estate/dees-domtools` provides utilities to make it easier to manage responsive designs and breakpoints.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { breakpoints } from '@design.estate/dees-domtools';
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
|
||||||
// Example: Applying styles for different viewport sizes
|
// Add global CSS
|
||||||
const tabletStyles = breakpoints.cssForTablet(myCssStyles);
|
await domtools.setGlobalStyles(`
|
||||||
const phoneStyles = breakpoints.cssForPhone(myCssStyles);
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
// Load external CSS
|
||||||
|
await domtools.setExternalCss('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
|
||||||
|
|
||||||
|
// Load external JavaScript
|
||||||
|
await domtools.setExternalScript('https://cdn.example.com/analytics.js');
|
||||||
```
|
```
|
||||||
|
|
||||||
### Theme Management
|
### Website Metadata
|
||||||
|
|
||||||
Managing themes (such as toggling between dark and light mode) is simplified with the built-in theme management tools.
|
Manage your website's metadata easily:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Toggle between light and dark themes
|
const domtools = await DomTools.setupDomTools();
|
||||||
domToolsInstance.themeManager.toggleDarkBright();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Handling Keyboard Events
|
await domtools.setWebsiteInfo({
|
||||||
|
metaObject: {
|
||||||
Keyboard event handling is made easy with the utilities provided by the toolkit. This can be particularly useful for adding keyboard navigation and shortcuts.
|
title: 'My Awesome App',
|
||||||
|
description: 'The best app ever created',
|
||||||
```typescript
|
keywords: ['awesome', 'app', 'web'],
|
||||||
import { Keyboard } from '@design.estate/dees-domtools';
|
author: 'Your Name'
|
||||||
|
},
|
||||||
// Initialize keyboard handling
|
faviconUrl: '/favicon.ico',
|
||||||
const keyboard = new Keyboard(document.body);
|
appleTouchIconUrl: '/apple-touch-icon.png'
|
||||||
|
|
||||||
// Bind an event to a specific key combination
|
|
||||||
keyboard.on([Keyboard.Key.Ctrl, Keyboard.Key.S]).subscribe(() => {
|
|
||||||
console.log('Ctrl+S was pressed');
|
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
### Best Practices
|
### Web Component Base Styles
|
||||||
|
|
||||||
- **Organize Your CSS**: Use the provided CSS utilities to structure your styles in a way that makes them easy to maintain and scale.
|
Kickstart your Lit elements with pre-configured styles:
|
||||||
- **Embrace Responsiveness**: Leverage the responsive design helpers to ensure your application looks great on any device.
|
|
||||||
- **Consistent Theme Handling**: Utilize the theme management tools to provide a seamless experience for your users, allowing them to choose their preferred theme.
|
|
||||||
|
|
||||||
By integrating `@design.estate/dees-domtools` into your project, you can leverage a comprehensive suite of utilities designed to simplify complex CSS structures and enhance your web development workflow.
|
```typescript
|
||||||
|
import { LitElement } from 'lit';
|
||||||
|
import { elementBasic } from '@design.estate/dees-domtools';
|
||||||
|
|
||||||
|
class MyElement extends LitElement {
|
||||||
|
static styles = [elementBasic.staticStyles];
|
||||||
|
|
||||||
|
async connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
await elementBasic.setup(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `elementBasic.staticStyles` includes:
|
||||||
|
- Box-sizing reset
|
||||||
|
- Smooth transitions for background and color
|
||||||
|
- Custom scrollbar styles
|
||||||
|
- Default font family (Geist Sans, Inter fallback)
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
|
||||||
|
Integrated state management with smartstate:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
|
||||||
|
// Access the state part
|
||||||
|
const state = domtools.domToolsStatePart;
|
||||||
|
|
||||||
|
// Get current state
|
||||||
|
const currentState = state.getState();
|
||||||
|
console.log(currentState.virtualViewport); // 'native'
|
||||||
|
console.log(currentState.jwt); // null
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
state.setState({
|
||||||
|
virtualViewport: 'tablet',
|
||||||
|
jwt: 'your-token-here'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to state changes
|
||||||
|
state.subscribe((newState) => {
|
||||||
|
console.log('State updated:', newState);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Once Pattern
|
||||||
|
|
||||||
|
Execute expensive operations only once, even if called multiple times:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const domtools = await DomTools.setupDomTools();
|
||||||
|
|
||||||
|
// This will only execute once, even if called multiple times
|
||||||
|
const result = await domtools.runOnce('myExpensiveOperation', async () => {
|
||||||
|
console.log('Running expensive operation...');
|
||||||
|
await someExpensiveAsyncOperation();
|
||||||
|
return 'result';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subsequent calls return the same result without re-executing
|
||||||
|
const sameResult = await domtools.runOnce('myExpensiveOperation', async () => {
|
||||||
|
console.log('This will never run!');
|
||||||
|
return 'different result';
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(result === sameResult); // true
|
||||||
|
```
|
||||||
|
|
||||||
|
Error handling is built-in - if the function throws, all waiting callers receive the same error.
|
||||||
|
|
||||||
|
## Advanced Usage
|
||||||
|
|
||||||
|
### Combining Features
|
||||||
|
|
||||||
|
Here's a real-world example combining multiple features:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { DomTools, breakpoints, elementBasic, Key } from '@design.estate/dees-domtools';
|
||||||
|
import { LitElement, html, css as litCss } from 'lit';
|
||||||
|
import { customElement } from 'lit/decorators.js';
|
||||||
|
|
||||||
|
@customElement('my-app')
|
||||||
|
class MyApp extends LitElement {
|
||||||
|
static styles = [
|
||||||
|
elementBasic.staticStyles,
|
||||||
|
litCss`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
${breakpoints.cssForTablet(litCss`
|
||||||
|
:host {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
`)}
|
||||||
|
`
|
||||||
|
];
|
||||||
|
|
||||||
|
private domtools?: DomTools;
|
||||||
|
|
||||||
|
async connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
// Setup DomTools
|
||||||
|
this.domtools = await elementBasic.setup(this);
|
||||||
|
await this.domtools.domReady.promise;
|
||||||
|
|
||||||
|
// Setup keyboard shortcuts
|
||||||
|
this.domtools.keyboard.on([Key.Ctrl, Key.K]).subscribe(() => {
|
||||||
|
this.openCommandPalette();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Subscribe to theme changes
|
||||||
|
this.domtools.themeManager.themeObservable.subscribe((isBright) => {
|
||||||
|
this.requestUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable smooth scrolling
|
||||||
|
await this.domtools.scroller.enableLenisScroll({
|
||||||
|
disableOnNativeSmoothScroll: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private openCommandPalette() {
|
||||||
|
console.log('Command palette opened!');
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const isDark = !this.domtools?.themeManager.goBrightBoolean;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="app" style="background: ${isDark ? '#1a1a1a' : '#ffffff'}">
|
||||||
|
<h1>My Awesome App</h1>
|
||||||
|
<button @click=${() => this.domtools?.themeManager.toggleDarkBright()}>
|
||||||
|
Toggle Theme
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## TypeScript Support
|
||||||
|
|
||||||
|
This package is written in TypeScript and provides full type definitions:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import type {
|
||||||
|
IDomToolsState,
|
||||||
|
IDomToolsContructorOptions,
|
||||||
|
TViewport
|
||||||
|
} from '@design.estate/dees-domtools';
|
||||||
|
|
||||||
|
// Custom state interface
|
||||||
|
interface MyState extends IDomToolsState {
|
||||||
|
customProperty: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type-safe viewport handling
|
||||||
|
const viewport: TViewport = 'tablet';
|
||||||
|
```
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
Targets the latest version of Chrome. For other browsers, you may need to include polyfills.
|
||||||
|
|
||||||
|
## Why @design.estate/dees-domtools?
|
||||||
|
|
||||||
|
✅ **Race-condition free** - Carefully designed initialization prevents common timing issues
|
||||||
|
✅ **TypeScript first** - Full type safety and IntelliSense support
|
||||||
|
✅ **Modern APIs** - Built on Lit, RxJS, and other modern web standards
|
||||||
|
✅ **Batteries included** - Everything you need for sophisticated web apps
|
||||||
|
✅ **Production ready** - Used in real-world applications at design.estate
|
||||||
|
✅ **Well maintained** - Active development and support
|
||||||
|
|
||||||
|
## Related Packages
|
||||||
|
|
||||||
|
This library integrates with the design.estate ecosystem:
|
||||||
|
|
||||||
|
- `@design.estate/dees-comms` - Communication utilities
|
||||||
|
- `@push.rocks/websetup` - Website setup and meta management
|
||||||
|
- `@push.rocks/smartrouter` - Client-side routing
|
||||||
|
- `@push.rocks/smartstate` - State management
|
||||||
|
|
||||||
## License and Legal Information
|
## License and Legal Information
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@design.estate/dees-domtools',
|
name: '@design.estate/dees-domtools',
|
||||||
version: '2.3.1',
|
version: '2.3.5',
|
||||||
description: 'A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.'
|
description: 'A package providing tools to simplify complex CSS structures and web development tasks, featuring TypeScript support and integration with various web technologies.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,22 @@ export class DomTools {
|
|||||||
// ======
|
// ======
|
||||||
// STATIC
|
// STATIC
|
||||||
// ======
|
// ======
|
||||||
|
private static initializationPromise: Promise<DomTools> | null = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setups domtools
|
* setups domtools
|
||||||
*/
|
*/
|
||||||
public static async setupDomTools(optionsArg: IDomToolsContructorOptions = {}) {
|
public static async setupDomTools(optionsArg: IDomToolsContructorOptions = {}): Promise<DomTools> {
|
||||||
|
// If initialization is already in progress and we're not ignoring global, wait for it
|
||||||
|
if (!optionsArg.ignoreGlobal && DomTools.initializationPromise) {
|
||||||
|
return await DomTools.initializationPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create initialization promise to prevent race conditions
|
||||||
|
if (!optionsArg.ignoreGlobal) {
|
||||||
|
DomTools.initializationPromise = (async () => {
|
||||||
let domToolsInstance: DomTools;
|
let domToolsInstance: DomTools;
|
||||||
if (!globalThis.deesDomTools && !optionsArg.ignoreGlobal) {
|
if (!globalThis.deesDomTools) {
|
||||||
globalThis.deesDomTools = new DomTools(optionsArg);
|
globalThis.deesDomTools = new DomTools(optionsArg);
|
||||||
domToolsInstance = globalThis.deesDomTools;
|
domToolsInstance = globalThis.deesDomTools;
|
||||||
|
|
||||||
@@ -32,18 +42,30 @@ export class DomTools {
|
|||||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||||
domToolsInstance.elements.headElement = document.querySelector('head');
|
domToolsInstance.elements.headElement = document.querySelector('head');
|
||||||
domToolsInstance.elements.bodyElement = document.querySelector('body');
|
domToolsInstance.elements.bodyElement = document.querySelector('body');
|
||||||
|
// Initialize keyboard now that document.body exists
|
||||||
|
domToolsInstance.keyboard = new Keyboard(document.body);
|
||||||
domToolsInstance.domReady.resolve();
|
domToolsInstance.domReady.resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Check current state immediately to avoid race condition
|
||||||
|
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||||
|
readyStateChangedFunc();
|
||||||
|
} else {
|
||||||
document.addEventListener('readystatechange', readyStateChangedFunc);
|
document.addEventListener('readystatechange', readyStateChangedFunc);
|
||||||
|
}
|
||||||
domToolsInstance.domToolsReady.resolve();
|
domToolsInstance.domToolsReady.resolve();
|
||||||
} else if (optionsArg.ignoreGlobal) {
|
|
||||||
domToolsInstance = new DomTools(optionsArg);
|
|
||||||
} else {
|
} else {
|
||||||
domToolsInstance = globalThis.deesDomTools;
|
domToolsInstance = globalThis.deesDomTools;
|
||||||
}
|
}
|
||||||
await domToolsInstance.domToolsReady.promise;
|
await domToolsInstance.domToolsReady.promise;
|
||||||
return domToolsInstance;
|
return domToolsInstance;
|
||||||
|
})();
|
||||||
|
return await DomTools.initializationPromise;
|
||||||
|
} else {
|
||||||
|
// ignoreGlobal case - create isolated instance
|
||||||
|
const domToolsInstance = new DomTools(optionsArg);
|
||||||
|
return domToolsInstance;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,7 +117,7 @@ export class DomTools {
|
|||||||
public deesComms = new plugins.deesComms.DeesComms();
|
public deesComms = new plugins.deesComms.DeesComms();
|
||||||
public scroller = new Scroller(this);
|
public scroller = new Scroller(this);
|
||||||
public themeManager = new ThemeManager(this);
|
public themeManager = new ThemeManager(this);
|
||||||
public keyboard = new Keyboard(document.body);
|
public keyboard: Keyboard = null; // Initialized after DOM ready to avoid accessing document.body before it exists
|
||||||
|
|
||||||
public domToolsReady = plugins.smartpromise.defer();
|
public domToolsReady = plugins.smartpromise.defer();
|
||||||
public domReady = plugins.smartpromise.defer();
|
public domReady = plugins.smartpromise.defer();
|
||||||
@@ -105,6 +127,7 @@ export class DomTools {
|
|||||||
|
|
||||||
private runOnceTrackerStringMap = new plugins.lik.Stringmap();
|
private runOnceTrackerStringMap = new plugins.lik.Stringmap();
|
||||||
private runOnceResultMap = new plugins.lik.FastMap();
|
private runOnceResultMap = new plugins.lik.FastMap();
|
||||||
|
private runOnceErrorMap = new plugins.lik.FastMap();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* run a function once and always get the Promise of the first execution
|
* run a function once and always get the Promise of the first execution
|
||||||
@@ -116,15 +139,27 @@ export class DomTools {
|
|||||||
if (!this.runOnceTrackerStringMap.checkString(identifierArg)) {
|
if (!this.runOnceTrackerStringMap.checkString(identifierArg)) {
|
||||||
this.runOnceTrackerStringMap.addString(identifierArg);
|
this.runOnceTrackerStringMap.addString(identifierArg);
|
||||||
this.runOnceTrackerStringMap.addString(runningId);
|
this.runOnceTrackerStringMap.addString(runningId);
|
||||||
|
try {
|
||||||
const result = await funcArg();
|
const result = await funcArg();
|
||||||
this.runOnceResultMap.addToMap(identifierArg, result);
|
this.runOnceResultMap.addToMap(identifierArg, result);
|
||||||
|
} catch (error) {
|
||||||
|
// Store error so waiting callers can receive it
|
||||||
|
this.runOnceErrorMap.addToMap(identifierArg, error);
|
||||||
|
} finally {
|
||||||
|
// Always remove running flag to prevent permanent stuck state
|
||||||
this.runOnceTrackerStringMap.removeString(runningId);
|
this.runOnceTrackerStringMap.removeString(runningId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return await this.runOnceTrackerStringMap.registerUntilTrue(
|
return await this.runOnceTrackerStringMap.registerUntilTrue(
|
||||||
(stringMap) => {
|
(stringMap) => {
|
||||||
return !stringMap.includes(runningId);
|
return !stringMap.includes(runningId);
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
|
// Check if there was an error and re-throw it
|
||||||
|
const error = this.runOnceErrorMap.getByKey(identifierArg);
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
return this.runOnceResultMap.getByKey(identifierArg);
|
return this.runOnceResultMap.getByKey(identifierArg);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export class Scroller {
|
|||||||
/**
|
/**
|
||||||
* Scrolls to a given element with options.
|
* Scrolls to a given element with options.
|
||||||
*/
|
*/
|
||||||
public async scrollToElement(
|
public async toElement(
|
||||||
elementArg: HTMLElement,
|
elementArg: HTMLElement,
|
||||||
optionsArg: Parameters<typeof this.sweetScroller.toElement>[1]
|
optionsArg: Parameters<typeof this.sweetScroller.toElement>[1]
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user