Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ea895024c0 | |||
| 078ccdf1c3 | |||
| 918769e416 | |||
| 039ee19ffd | |||
| d16bc1652d | |||
| 6fa99f8ed8 | |||
| 5ce92130b4 | |||
| b0774b0cba | |||
| c44e19f3e0 | |||
| 07f09b4457 | |||
| 10ea4ca265 | |||
| dfd61ce744 | |||
| 3093ccd4f6 | |||
| eb1ac75e49 | |||
| 0683378e39 | |||
| 25a813d35f | |||
| 916fd48858 | |||
| 90bb1eb432 | |||
| 126e0fc900 | |||
| a20b321bb0 | |||
| 33721f86ab | |||
| 987c821eed | |||
| b0bed44810 | |||
| 0a7da5132d |
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
|
||||
87
changelog.md
87
changelog.md
@@ -1,5 +1,92 @@
|
||||
# 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)
|
||||
Removed passive option from scroll event listener
|
||||
|
||||
- The 'passive: true' option was removed from the native scroll event listener attachment.
|
||||
|
||||
## 2025-01-31 - 2.3.0 - feat(scroller)
|
||||
Enhance Scroller class with callback execution and adaptive scroll listener
|
||||
|
||||
- Added support for executing scroll callbacks in the Scroller class.
|
||||
- Integrated adaptive scrolling mechanism using Lenis, with native smooth scrolling detection.
|
||||
- Ensured seamless switching between native scroll listener and Lenis scroll listener.
|
||||
|
||||
## 2025-01-31 - 2.2.0 - feat(core)
|
||||
Enhance scrolling capabilities by integrating Scroller class and adding lenis support
|
||||
|
||||
- Replaced SweetScroll setup in domtools.classes.domtools.ts with Scroller class.
|
||||
- Moved SweetScroll initialization and methods to new Scroller class.
|
||||
- Added lenis support in the Scroller class for enhanced scrolling capabilities.
|
||||
- Updated dev and dependencies versions in package.json.
|
||||
|
||||
## 2025-01-09 - 2.1.1 - fix(themamanager)
|
||||
Fixed automatic global theme change subscription for background updates.
|
||||
|
||||
- Corrected the logic for updating the document's background color upon theme changes using themeObservable subscription.
|
||||
|
||||
## 2025-01-09 - 2.1.0 - feat(themeManager)
|
||||
Exposed method to enable automatic global theme change.
|
||||
|
||||
- Enable easier application of dark and bright themes by exposing a method.
|
||||
- Updated devDependencies and dependencies in package.json to latest versions.
|
||||
|
||||
## 2024-10-21 - 2.0.65 - fix(ThemeManager)
|
||||
Refactor ThemeManager class to separate global style setting logic
|
||||
|
||||
- Moved logic to set global styles into a dedicated function setGlobalStylesOnPurpose in ThemeManager.
|
||||
|
||||
## 2024-10-06 - 2.0.64 - fix(pluginexports)
|
||||
Add missing import for smartrouter in pluginexports.
|
||||
|
||||
- Fixed a missing import for smartrouter in ts/domtools.pluginexports.ts.
|
||||
|
||||
## 2024-10-06 - 2.0.63 - fix(dependencies)
|
||||
Update @push.rocks/smartrouter to version ^1.3.2 for better compatibility
|
||||
|
||||
- Updated @push.rocks/smartrouter from version ^1.2.1 to ^1.3.2 in package.json.
|
||||
|
||||
## 2024-10-06 - 2.0.62 - fix(dependencies)
|
||||
Update dependencies to resolve potential issues and improve stability
|
||||
|
||||
|
||||
34
package.json
34
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@design.estate/dees-domtools",
|
||||
"version": "2.0.62",
|
||||
"version": "2.3.5",
|
||||
"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.",
|
||||
"main": "dist_ts/index.js",
|
||||
@@ -15,29 +15,30 @@
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@git.zone/tsbuild": "^2.1.84",
|
||||
"@git.zone/tsbundle": "^2.0.15",
|
||||
"@git.zone/tstest": "^1.0.90",
|
||||
"@push.rocks/tapbundle": "^5.3.0",
|
||||
"@types/node": "^22.7.4"
|
||||
"@git.zone/tsbuild": "^2.7.1",
|
||||
"@git.zone/tsbundle": "^2.5.1",
|
||||
"@git.zone/tstest": "^2.7.0",
|
||||
"@push.rocks/tapbundle": "^6.0.3",
|
||||
"@types/node": "^22.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@api.global/typedrequest": "^3.0.32",
|
||||
"@api.global/typedrequest": "^3.1.10",
|
||||
"@design.estate/dees-comms": "^1.0.27",
|
||||
"@push.rocks/lik": "^6.0.15",
|
||||
"@push.rocks/lik": "^6.2.2",
|
||||
"@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/smartpromise": "^4.0.4",
|
||||
"@push.rocks/smartrouter": "^1.2.1",
|
||||
"@push.rocks/smartrx": "^3.0.7",
|
||||
"@push.rocks/smartstate": "^2.0.19",
|
||||
"@push.rocks/smartstring": "^4.0.15",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
"@push.rocks/smartrouter": "^1.3.3",
|
||||
"@push.rocks/smartrx": "^3.0.10",
|
||||
"@push.rocks/smartstate": "^2.0.27",
|
||||
"@push.rocks/smartstring": "^4.1.0",
|
||||
"@push.rocks/smarturl": "^3.1.0",
|
||||
"@push.rocks/webrequest": "^3.0.37",
|
||||
"@push.rocks/websetup": "^3.0.19",
|
||||
"@push.rocks/webstore": "^2.0.20",
|
||||
"lit": "^3.2.0",
|
||||
"lenis": "^1.3.4",
|
||||
"lit": "^3.3.0",
|
||||
"sweet-scroll": "^4.0.0"
|
||||
},
|
||||
"files": [
|
||||
@@ -70,5 +71,6 @@
|
||||
"state management",
|
||||
"routing",
|
||||
"performance optimization"
|
||||
]
|
||||
],
|
||||
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
|
||||
}
|
||||
|
||||
8253
pnpm-lock.yaml
generated
8253
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
|
||||
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
|
||||
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`.
|
||||
|
||||
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:
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import { DomTools, elementBasic, breakpoints, css } from '@design.estate/dees-domtools';
|
||||
import { DomTools } from '@design.estate/dees-domtools';
|
||||
|
||||
// Setting up DomTools for your application
|
||||
const domToolsInstance = await DomTools.setupDomTools();
|
||||
// Initialize DomTools (singleton pattern - safe to call multiple times)
|
||||
const domtools = await DomTools.setupDomTools();
|
||||
|
||||
// Example: Using elementBasic utilities
|
||||
elementBasic.setup();
|
||||
// Wait for DOM to be ready
|
||||
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
|
||||
import { css } from '@design.estate/dees-domtools';
|
||||
|
||||
// Example: Creating a CSS grid with a specified number of columns and gap size
|
||||
const gridStyles = css.cssGridColumns(3, 10); // 3 columns with a 10px gap
|
||||
// Create responsive grid columns
|
||||
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
|
||||
|
||||
Responsive design is critical for modern web development. `@design.estate/dees-domtools` provides utilities to make it easier to manage responsive designs and breakpoints.
|
||||
### Global Styles & External Resources
|
||||
|
||||
```typescript
|
||||
import { breakpoints } from '@design.estate/dees-domtools';
|
||||
const domtools = await DomTools.setupDomTools();
|
||||
|
||||
// Example: Applying styles for different viewport sizes
|
||||
const tabletStyles = breakpoints.cssForTablet(myCssStyles);
|
||||
const phoneStyles = breakpoints.cssForPhone(myCssStyles);
|
||||
// Add global CSS
|
||||
await domtools.setGlobalStyles(`
|
||||
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
|
||||
// Toggle between light and dark themes
|
||||
domToolsInstance.themeManager.toggleDarkBright();
|
||||
```
|
||||
const domtools = await DomTools.setupDomTools();
|
||||
|
||||
### Handling Keyboard Events
|
||||
|
||||
Keyboard event handling is made easy with the utilities provided by the toolkit. This can be particularly useful for adding keyboard navigation and shortcuts.
|
||||
|
||||
```typescript
|
||||
import { Keyboard } from '@design.estate/dees-domtools';
|
||||
|
||||
// Initialize keyboard handling
|
||||
const keyboard = new Keyboard(document.body);
|
||||
|
||||
// Bind an event to a specific key combination
|
||||
keyboard.on([Keyboard.Key.Ctrl, Keyboard.Key.S]).subscribe(() => {
|
||||
console.log('Ctrl+S was pressed');
|
||||
await domtools.setWebsiteInfo({
|
||||
metaObject: {
|
||||
title: 'My Awesome App',
|
||||
description: 'The best app ever created',
|
||||
keywords: ['awesome', 'app', 'web'],
|
||||
author: 'Your Name'
|
||||
},
|
||||
faviconUrl: '/favicon.ico',
|
||||
appleTouchIconUrl: '/apple-touch-icon.png'
|
||||
});
|
||||
```
|
||||
|
||||
### 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.
|
||||
- **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.
|
||||
Kickstart your Lit elements with pre-configured styles:
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@design.estate/dees-domtools',
|
||||
version: '2.0.62',
|
||||
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.'
|
||||
}
|
||||
|
||||
@@ -18,12 +18,22 @@ export class DomTools {
|
||||
// ======
|
||||
// STATIC
|
||||
// ======
|
||||
private static initializationPromise: Promise<DomTools> | null = null;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
if (!globalThis.deesDomTools && !optionsArg.ignoreGlobal) {
|
||||
if (!globalThis.deesDomTools) {
|
||||
globalThis.deesDomTools = new DomTools(optionsArg);
|
||||
domToolsInstance = globalThis.deesDomTools;
|
||||
|
||||
@@ -32,18 +42,30 @@ export class DomTools {
|
||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||
domToolsInstance.elements.headElement = document.querySelector('head');
|
||||
domToolsInstance.elements.bodyElement = document.querySelector('body');
|
||||
// Initialize keyboard now that document.body exists
|
||||
domToolsInstance.keyboard = new Keyboard(document.body);
|
||||
domToolsInstance.domReady.resolve();
|
||||
}
|
||||
};
|
||||
// Check current state immediately to avoid race condition
|
||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
||||
readyStateChangedFunc();
|
||||
} else {
|
||||
document.addEventListener('readystatechange', readyStateChangedFunc);
|
||||
}
|
||||
domToolsInstance.domToolsReady.resolve();
|
||||
} else if (optionsArg.ignoreGlobal) {
|
||||
domToolsInstance = new DomTools(optionsArg);
|
||||
} else {
|
||||
domToolsInstance = globalThis.deesDomTools;
|
||||
}
|
||||
await domToolsInstance.domToolsReady.promise;
|
||||
return domToolsInstance;
|
||||
})();
|
||||
return await DomTools.initializationPromise;
|
||||
} else {
|
||||
// ignoreGlobal case - create isolated instance
|
||||
const domToolsInstance = new DomTools(optionsArg);
|
||||
return domToolsInstance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -93,11 +115,9 @@ export class DomTools {
|
||||
};
|
||||
|
||||
public deesComms = new plugins.deesComms.DeesComms();
|
||||
public scroller = new plugins.SweetScroll({
|
||||
/* some options */
|
||||
}); // TODO: switch to scroller class
|
||||
public scroller = new Scroller(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 domReady = plugins.smartpromise.defer();
|
||||
@@ -107,6 +127,7 @@ export class DomTools {
|
||||
|
||||
private runOnceTrackerStringMap = new plugins.lik.Stringmap();
|
||||
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
|
||||
@@ -118,15 +139,27 @@ export class DomTools {
|
||||
if (!this.runOnceTrackerStringMap.checkString(identifierArg)) {
|
||||
this.runOnceTrackerStringMap.addString(identifierArg);
|
||||
this.runOnceTrackerStringMap.addString(runningId);
|
||||
try {
|
||||
const result = await funcArg();
|
||||
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);
|
||||
}
|
||||
}
|
||||
return await this.runOnceTrackerStringMap.registerUntilTrue(
|
||||
(stringMap) => {
|
||||
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);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -1,5 +1,175 @@
|
||||
import type { DomTools } from './domtools.classes.domtools.js';
|
||||
import * as plugins from './domtools.plugins.js';
|
||||
|
||||
export class Scroller {
|
||||
// TODO: move sweet scroll over to here;
|
||||
public domtoolsInstance: DomTools;
|
||||
|
||||
// Array to store scroll callback functions.
|
||||
private scrollCallbacks: Array<() => void> = [];
|
||||
|
||||
// Lenis instance (if activated) or null.
|
||||
private lenisInstance: plugins.Lenis | null = null;
|
||||
|
||||
// Bound handlers to allow removal from event listeners.
|
||||
private handleNativeScroll = (event: Event): void => {
|
||||
this.executeScrollCallbacks();
|
||||
};
|
||||
|
||||
private handleLenisScroll = (info: any): void => {
|
||||
this.executeScrollCallbacks();
|
||||
};
|
||||
|
||||
constructor(domtoolsInstanceArg: DomTools) {
|
||||
this.domtoolsInstance = domtoolsInstanceArg;
|
||||
// Attach the native scroll listener by default.
|
||||
this.attachNativeScrollListener();
|
||||
}
|
||||
|
||||
private sweetScroller = new plugins.SweetScroll({});
|
||||
|
||||
/**
|
||||
* Scrolls to a given element with options.
|
||||
*/
|
||||
public async toElement(
|
||||
elementArg: HTMLElement,
|
||||
optionsArg: Parameters<typeof this.sweetScroller.toElement>[1]
|
||||
) {
|
||||
this.sweetScroller.toElement(elementArg, optionsArg);
|
||||
await plugins.smartdelay.delayFor(optionsArg.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects whether native smooth scrolling is enabled.
|
||||
*/
|
||||
public async detectNativeSmoothScroll() {
|
||||
const done = plugins.smartpromise.defer<boolean>();
|
||||
const sampleSize = 100;
|
||||
const acceptableDeltaDifference = 3;
|
||||
const minimumSmoothRatio = 0.75;
|
||||
|
||||
const eventDeltas: number[] = [];
|
||||
|
||||
function onWheel(event: WheelEvent) {
|
||||
eventDeltas.push(event.deltaY);
|
||||
|
||||
if (eventDeltas.length >= sampleSize) {
|
||||
window.removeEventListener('wheel', onWheel);
|
||||
analyzeEvents();
|
||||
}
|
||||
}
|
||||
|
||||
function analyzeEvents() {
|
||||
const totalDiffs = eventDeltas.length - 1;
|
||||
let smallDiffCount = 0;
|
||||
|
||||
for (let i = 0; i < totalDiffs; i++) {
|
||||
const diff = Math.abs(eventDeltas[i + 1] - eventDeltas[i]);
|
||||
if (diff <= acceptableDeltaDifference) {
|
||||
smallDiffCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const smoothRatio = smallDiffCount / totalDiffs;
|
||||
if (smoothRatio >= minimumSmoothRatio) {
|
||||
console.log('Smooth scrolling detected.');
|
||||
done.resolve(true);
|
||||
} else {
|
||||
console.log('Smooth scrolling NOT detected.');
|
||||
done.resolve(false);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('wheel', onWheel);
|
||||
return done.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables Lenis scrolling.
|
||||
* If optionsArg.disableOnNativeSmoothScroll is true and native smooth scrolling is detected,
|
||||
* Lenis will be destroyed immediately.
|
||||
*/
|
||||
public async enableLenisScroll(optionsArg?: { disableOnNativeSmoothScroll?: boolean }) {
|
||||
const lenis = new plugins.Lenis({
|
||||
autoRaf: true,
|
||||
});
|
||||
|
||||
if (optionsArg?.disableOnNativeSmoothScroll) {
|
||||
if (await this.detectNativeSmoothScroll()) {
|
||||
lenis.destroy();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Activate Lenis scrolling.
|
||||
this.lenisInstance = lenis;
|
||||
// Switch from native scroll listener to Lenis scroll listener.
|
||||
this.detachNativeScrollListener();
|
||||
this.attachLenisScrollListener();
|
||||
|
||||
// Monkey-patch the destroy method so that when Lenis is destroyed,
|
||||
// the native scroll listener is reattached.
|
||||
const originalDestroy = lenis.destroy.bind(lenis);
|
||||
lenis.destroy = () => {
|
||||
originalDestroy();
|
||||
this.detachLenisScrollListener();
|
||||
this.attachNativeScrollListener();
|
||||
this.lenisInstance = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback to be executed on scroll.
|
||||
* @param callback A function to execute on each scroll event.
|
||||
*/
|
||||
public onScroll(callback: () => void): void {
|
||||
this.scrollCallbacks.push(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes all registered scroll callbacks concurrently.
|
||||
*/
|
||||
private executeScrollCallbacks(): void {
|
||||
// Execute all callbacks in parallel.
|
||||
this.scrollCallbacks.forEach((callback) => {
|
||||
try {
|
||||
callback();
|
||||
} catch (error) {
|
||||
console.error('Error in scroll callback:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the native scroll event listener.
|
||||
*/
|
||||
private attachNativeScrollListener(): void {
|
||||
window.addEventListener('scroll', this.handleNativeScroll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the native scroll event listener.
|
||||
*/
|
||||
private detachNativeScrollListener(): void {
|
||||
window.removeEventListener('scroll', this.handleNativeScroll);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the Lenis scroll event listener.
|
||||
*/
|
||||
private attachLenisScrollListener(): void {
|
||||
if (this.lenisInstance) {
|
||||
// Assuming that Lenis exposes an `on` method to listen to scroll events.
|
||||
this.lenisInstance.on('scroll', this.handleLenisScroll);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches the Lenis scroll event listener.
|
||||
*/
|
||||
private detachLenisScrollListener(): void {
|
||||
if (this.lenisInstance) {
|
||||
// Assuming that Lenis exposes an `off` method to remove scroll event listeners.
|
||||
this.lenisInstance.off('scroll', this.handleLenisScroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,10 +21,17 @@ export class ThemeManager {
|
||||
this.updateAllConnectedElements();
|
||||
}
|
||||
|
||||
private async updateAllConnectedElements() {
|
||||
public async enableAutomaticGlobalThemeChange() {
|
||||
if (document.body && document.body.style) {
|
||||
document.body.style.background = this.goBrightBoolean ? '#fff' : '#000';
|
||||
this.themeObservable.subscribe({
|
||||
next: (goBright) => {
|
||||
document.body.style.background = goBright ? '#fff' : '#000';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async updateAllConnectedElements() {
|
||||
this.themeObservable.next(this.goBrightBoolean);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import * as smartmarkdown from '@push.rocks/smartmarkdown';
|
||||
import * as smartpromise from '@push.rocks/smartpromise';
|
||||
import SweetScroll from 'sweet-scroll';
|
||||
import * as smartstate from '@push.rocks/smartstate';
|
||||
import * as smartrouter from '@push.rocks/smartrouter';
|
||||
import * as smartrx from '@push.rocks/smartrx';
|
||||
import * as smartstring from '@push.rocks/smartstring';
|
||||
import * as smarturl from '@push.rocks/smarturl';
|
||||
@@ -14,6 +15,7 @@ export {
|
||||
smartpromise,
|
||||
SweetScroll,
|
||||
smartstate,
|
||||
smartrouter,
|
||||
smartrx,
|
||||
smartstring,
|
||||
smarturl,
|
||||
|
||||
@@ -49,6 +49,7 @@ export {
|
||||
};
|
||||
|
||||
// third party scope
|
||||
import Lenis from 'lenis'
|
||||
import SweetScroll from 'sweet-scroll';
|
||||
|
||||
export { SweetScroll };
|
||||
export { Lenis, SweetScroll };
|
||||
|
||||
Reference in New Issue
Block a user