Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
20a52d1b3e | |||
dafcf3834c | |||
639672358a | |||
671fb7dc66 | |||
b92966ef28 | |||
c1102634f3 | |||
ee470775b2 | |||
ba0f1602a1 | |||
682955212e | |||
0410f6c196 | |||
24aa7588c5 | |||
b46fe8fe93 | |||
b47c2053b5 | |||
16bf8001ae | |||
792e77f824 |
174
CLAUDE.md
174
CLAUDE.md
@@ -1,174 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
@design.estate/dees-catalog is a comprehensive web components library built with TypeScript and LitElement. It provides a large collection of UI components for building modern web applications with consistent design and behavior.
|
|
||||||
|
|
||||||
## Build and Development Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install dependencies
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
# Build the project
|
|
||||||
pnpm run build
|
|
||||||
# This runs: tsbuild tsfolders --allowimplicitany && tsbundle element --production --bundler esbuild
|
|
||||||
|
|
||||||
# Run development watch mode
|
|
||||||
pnpm run watch
|
|
||||||
# This runs: tswatch element
|
|
||||||
|
|
||||||
# Run tests (browser tests)
|
|
||||||
pnpm test
|
|
||||||
# This runs: tstest test/ --web --verbose --timeout 30 --logfile
|
|
||||||
|
|
||||||
# Run a specific test file
|
|
||||||
tsx test/test.wysiwyg-basic.browser.ts --verbose
|
|
||||||
|
|
||||||
# Build documentation
|
|
||||||
pnpm run buildDocs
|
|
||||||
```
|
|
||||||
|
|
||||||
### Testing Notes
|
|
||||||
- Test files follow the pattern: `test.*.browser.ts`, `test.*.node.ts`, or `test.*.both.ts`
|
|
||||||
- Browser tests run in a headless browser environment
|
|
||||||
- Use `--logfile` option to store logs in `.nogit/testlogs/`
|
|
||||||
- For debugging, create files in `.nogit/debug/` and run with `tsx`
|
|
||||||
|
|
||||||
## Architecture Overview
|
|
||||||
|
|
||||||
### Component Structure
|
|
||||||
The library is organized into several categories:
|
|
||||||
|
|
||||||
1. **Core UI Components** (`dees-button`, `dees-badge`, `dees-icon`, etc.)
|
|
||||||
- Basic building blocks with consistent theming
|
|
||||||
- All support light/dark themes via `cssManager.bdTheme()`
|
|
||||||
|
|
||||||
2. **Form Components** (`dees-form`, `dees-input-*`)
|
|
||||||
- Complete form system with validation
|
|
||||||
- Base class `DeesInputBase` provides common functionality
|
|
||||||
- Form data collection via `DeesForm` container
|
|
||||||
|
|
||||||
3. **Layout Components** (`dees-appui-*`)
|
|
||||||
- Application shell components
|
|
||||||
- `DeesAppuiBase` orchestrates the entire layout
|
|
||||||
- Grid-based responsive design
|
|
||||||
|
|
||||||
4. **Data Display** (`dees-table`, `dees-dataview-*`, `dees-statsgrid`)
|
|
||||||
- Complex data visualization components
|
|
||||||
- Interactive tables with sorting/filtering
|
|
||||||
- Chart components using ApexCharts
|
|
||||||
|
|
||||||
5. **Overlays** (`dees-modal`, `dees-contextmenu`, `dees-toast`)
|
|
||||||
- Managed by central z-index registry
|
|
||||||
- Window layer system for proper stacking
|
|
||||||
|
|
||||||
### Key Architectural Patterns
|
|
||||||
|
|
||||||
#### Z-Index Management
|
|
||||||
All overlay components use a centralized z-index registry system:
|
|
||||||
- Definition in `ts_web/elements/00zindex.ts`
|
|
||||||
- Dynamic z-index assignment via `ZIndexRegistry` class
|
|
||||||
- Components get z-index from registry when showing
|
|
||||||
- Ensures proper stacking order (dropdowns above modals, etc.)
|
|
||||||
|
|
||||||
#### Theme System
|
|
||||||
- All components support light/dark themes
|
|
||||||
- Use `cssManager.bdTheme(lightValue, darkValue)` for theme-aware colors
|
|
||||||
- Consistent color palette defined in `00colors.ts`
|
|
||||||
|
|
||||||
#### Component Demo System
|
|
||||||
- Each component has a static `demo` property
|
|
||||||
- Demo functions in separate `.demo.ts` files
|
|
||||||
- Showcase pages aggregate demos (e.g., `input-showcase.ts`)
|
|
||||||
|
|
||||||
#### WYSIWYG Editor Architecture
|
|
||||||
The WYSIWYG editor uses a sophisticated architecture with separated concerns:
|
|
||||||
- **Main Component**: `dees-input-wysiwyg.ts` - Orchestrates the editor
|
|
||||||
- **Handler Classes**:
|
|
||||||
- `WysiwygInputHandler` - Handles text input and block transformations
|
|
||||||
- `WysiwygKeyboardHandler` - Manages keyboard shortcuts and navigation
|
|
||||||
- `WysiwygDragDropHandler` - Manages block reordering
|
|
||||||
- `WysiwygModalManager` - Shows configuration modals
|
|
||||||
- `WysiwygBlockOperations` - Core block manipulation logic
|
|
||||||
- **Global Menus**:
|
|
||||||
- `DeesSlashMenu` and `DeesFormattingMenu` render globally to avoid focus issues
|
|
||||||
- Singleton pattern ensures single instance
|
|
||||||
- **Programmatic Rendering**: Uses manual DOM manipulation to prevent focus loss
|
|
||||||
|
|
||||||
### Component Communication
|
|
||||||
- Custom events for parent-child communication
|
|
||||||
- Form components emit standardized events (`change`, `blur`, etc.)
|
|
||||||
- Complex components like `DeesAppuiBase` re-emit child events
|
|
||||||
|
|
||||||
### Build System
|
|
||||||
- TypeScript compilation with decorators support
|
|
||||||
- Web component bundling with esbuild
|
|
||||||
- Element exports in `ts_web/elements/index.ts`
|
|
||||||
- Distribution builds in `dist_ts_web/`
|
|
||||||
|
|
||||||
## Important Implementation Details
|
|
||||||
|
|
||||||
### When Creating New Components
|
|
||||||
1. Extend `DeesElement` from `@design.estate/dees-element`
|
|
||||||
2. Use `@customElement('dees-componentname')` decorator
|
|
||||||
3. Implement theme support with `cssManager.bdTheme()`
|
|
||||||
4. Create a demo function in a separate `.demo.ts` file
|
|
||||||
5. Export from `elements/index.ts`
|
|
||||||
|
|
||||||
### Form Input Components
|
|
||||||
1. Extend `DeesInputBase` for form inputs
|
|
||||||
2. Implement `getValue()` and `setValue()` methods
|
|
||||||
3. Use `changeSubject.next(this)` to emit changes
|
|
||||||
4. Support `disabled` and `required` properties
|
|
||||||
|
|
||||||
### Overlay Components
|
|
||||||
1. Import z-index from `00zindex.ts`
|
|
||||||
2. Get z-index from registry when showing: `zIndexRegistry.getNextZIndex()`
|
|
||||||
3. Register/unregister with the registry
|
|
||||||
4. Use `DeesWindowLayer` for backdrop if needed
|
|
||||||
|
|
||||||
### Testing Components
|
|
||||||
1. Create test files in `test/` directory
|
|
||||||
2. Use `@git.zone/tstest` with tap-bundle
|
|
||||||
3. Test in browser environment for web components
|
|
||||||
4. Use proper async/await for component lifecycle
|
|
||||||
|
|
||||||
## Common Patterns and Pitfalls
|
|
||||||
|
|
||||||
### Focus Management
|
|
||||||
- WYSIWYG editor uses programmatic rendering to prevent focus loss
|
|
||||||
- Use `requestAnimationFrame` for timing-sensitive focus operations
|
|
||||||
- Avoid reactive re-renders during user input
|
|
||||||
|
|
||||||
### Event Handling
|
|
||||||
- Prevent event bubbling in nested interactive components
|
|
||||||
- Use `pointer-events: none/auto` for click-through behavior
|
|
||||||
- Handle both mouse and keyboard events for accessibility
|
|
||||||
|
|
||||||
### Performance Considerations
|
|
||||||
- Large components (editor, terminal) use lazy loading
|
|
||||||
- Charts use debounced resize observers
|
|
||||||
- Tables implement virtual scrolling for large datasets
|
|
||||||
|
|
||||||
## File Organization
|
|
||||||
```
|
|
||||||
ts_web/
|
|
||||||
├── elements/ # All component files
|
|
||||||
│ ├── 00*.ts # Shared utilities (colors, z-index, plugins)
|
|
||||||
│ ├── dees-*.ts # Component implementations
|
|
||||||
│ ├── dees-*.demo.ts # Component demos
|
|
||||||
│ ├── interfaces/ # Shared TypeScript interfaces
|
|
||||||
│ ├── helperclasses/ # Utility classes (FormController)
|
|
||||||
│ └── wysiwyg/ # WYSIWYG editor subsystem
|
|
||||||
├── pages/ # Demo showcase pages
|
|
||||||
└── index.ts # Main export file
|
|
||||||
```
|
|
||||||
|
|
||||||
## Recent Major Changes
|
|
||||||
- Z-Index Registry System (2025-12-24): Dynamic stacking order management
|
|
||||||
- WYSIWYG Refactoring (2025-06-24): Complete architecture overhaul with separated concerns
|
|
||||||
- Form System Enhancement: Unified validation and data collection
|
|
||||||
- Theme System: Consistent light/dark theme support across all components
|
|
26
changelog.md
26
changelog.md
@@ -1,5 +1,29 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-09-18 - 1.12.2 - fix(dees-input-wysiwyg)
|
||||||
|
Integrate output format preview into WYSIWYG demo; update plan and add local dev settings
|
||||||
|
|
||||||
|
- Wire output format preview into the WYSIWYG demo (ts_web/elements/dees-input-wysiwyg.demo.ts) by calling setupOutputFormatDemo(editors.meeting, editors.recipe) so HTML/Markdown preview controls are initialized.
|
||||||
|
- Update readme.plan.md: mark the Output Formats review tasks as completed and document that preview controls were added.
|
||||||
|
- Add a local settings file to allow running local tooling tasks (grants permission for pnpm run scripts and related local commands).
|
||||||
|
- No library API or runtime component behavior changed — this is a demo/documentation and local-settings update.
|
||||||
|
|
||||||
|
## 2025-09-18 - 1.12.1 - fix(ci)
|
||||||
|
Add local settings to allow running pnpm scripts and enable dev chat permission
|
||||||
|
|
||||||
|
- Add a repository-local settings file granting permission to run pnpm scripts (Bash(pnpm run:*)) for development tooling.
|
||||||
|
- Enable the mcp__zen__chat permission for local dev workflows.
|
||||||
|
|
||||||
|
## 2025-09-18 - 1.12.0 - feat(dees-stepper)
|
||||||
|
Revamp dees-stepper: modern styling, new steps and improved navigation/validation
|
||||||
|
|
||||||
|
- Visual refresh for dees-stepper: updated card shapes, shadows, refined borders and stronger selected-state visuals for a modern shadcn-inspired look
|
||||||
|
- Improved transitions and animations (transform, box-shadow, filter) for smoother step selection and show/hide behavior
|
||||||
|
- Expanded default/demo steps: replaced small sample with a richer multi-step flow (Account Setup, Profile Details, Contact Information, Team Size, Goals, Brand Preferences, Integrations, Review & Launch)
|
||||||
|
- Enhanced step interactions: safer goNext/goBack handling with boundary checks and reset of validation flags to avoid stale validation state
|
||||||
|
- Better toolbar/controls placement for stepper demo (spacing, counters, accessible back control) and improved keyboard/UX affordances
|
||||||
|
- Minor documentation and meta updates: readme.plan.md extended with dees-stepper plan items and added .claude/settings.local.json
|
||||||
|
|
||||||
## 2025-09-18 - 1.11.8 - fix(ci)
|
## 2025-09-18 - 1.11.8 - fix(ci)
|
||||||
Add local tool permissions config to allow running pnpm scripts and enable mcp__zen__chat
|
Add local tool permissions config to allow running pnpm scripts and enable mcp__zen__chat
|
||||||
|
|
||||||
@@ -221,7 +245,7 @@ Add dees-searchbar component with live search and filter demo
|
|||||||
## 2025-04-22 - 1.6.0 - feat(documentation/dees-heading)
|
## 2025-04-22 - 1.6.0 - feat(documentation/dees-heading)
|
||||||
Add codex documentation overview and dees-heading component demo
|
Add codex documentation overview and dees-heading component demo
|
||||||
|
|
||||||
- Introduce 'codex.md' to provide a high-level overview of project layout, component patterns, and build workflow
|
- Introduce contributor overview doc (`codex.md`, now consolidated into `readme.info.md`) to provide a high-level overview of project layout, component patterns, and build workflow
|
||||||
- Add and update dees-heading component with demo to support multiple heading levels and horizontal rule styles
|
- Add and update dees-heading component with demo to support multiple heading levels and horizontal rule styles
|
||||||
- Update component export index to include dees-heading
|
- Update component export index to include dees-heading
|
||||||
|
|
||||||
|
43
codex.md
43
codex.md
@@ -1,43 +0,0 @@
|
|||||||
# Codex: Project Overview and Codebase Structure
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
- Package: `@design.estate/dees-catalog`
|
|
||||||
- Focus: Web Components library providing UI elements and layouts for modern web apps.
|
|
||||||
|
|
||||||
## Directory Layout
|
|
||||||
- ts_web/: TypeScript source files
|
|
||||||
- elements/: Individual Web Component definitions
|
|
||||||
- pages/: Page-level templates for composite layouts
|
|
||||||
- html/: Demo/app entry point loading the bundled scripts
|
|
||||||
- dist_bundle/: Bundled browser JS and source maps
|
|
||||||
- dist_ts_web/: ES module outputs for TypeScript/web consumers
|
|
||||||
- dist_watch/: Watch-mode development bundle with live reload
|
|
||||||
- test/: Browser-based tests using `@push.rocks/tapbundle`
|
|
||||||
|
|
||||||
## Component Patterns
|
|
||||||
- Each component in ts_web/elements/:
|
|
||||||
- Decorated with `@customElement('tag-name')`
|
|
||||||
- Extends `DeesElement` from `@design.estate/dees-element`
|
|
||||||
- Uses `@property` for reactive, reflected attributes
|
|
||||||
- Defines `static styles = [cssManager.defaultStyles, css`...`]`
|
|
||||||
- Implements `render()` returning a Lit `html` template with slots or markup
|
|
||||||
- Exposes a demo via `public static demo` linking to `.demo.ts` files
|
|
||||||
|
|
||||||
## Build & Development Workflow
|
|
||||||
- Install dependencies: `npm install` or `pnpm install`
|
|
||||||
- Build production bundle: `npm run build`
|
|
||||||
- Start dev watch mode: `npm run watch`
|
|
||||||
- Run tests: `npm test` (launches browser fixtures)
|
|
||||||
|
|
||||||
## Theming & Utilities
|
|
||||||
- Default global styles via `cssManager.defaultStyles`
|
|
||||||
- Theme-aware values with `cssManager.bdTheme(light, dark)`
|
|
||||||
- DOM utilities set up in `html/index.ts` using `@design.estate/dees-domtools`
|
|
||||||
|
|
||||||
## Documentation
|
|
||||||
- `readme.md` provides an overview of all components and basic usage
|
|
||||||
- Live examples in `.demo.ts` files
|
|
||||||
accessible via component `demo` static property
|
|
||||||
|
|
||||||
## Updates to this file
|
|
||||||
If you have pattern insisights or general changes to the codebase, please update this file.
|
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@design.estate/dees-catalog",
|
"name": "@design.estate/dees-catalog",
|
||||||
"version": "1.11.8",
|
"version": "1.12.2",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
"description": "A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.",
|
||||||
"main": "dist_ts_web/index.js",
|
"main": "dist_ts_web/index.js",
|
||||||
@@ -10,7 +10,8 @@
|
|||||||
"test": "tstest test/ --web --verbose --timeout 30 --logfile",
|
"test": "tstest test/ --web --verbose --timeout 30 --logfile",
|
||||||
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production --bundler esbuild",
|
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production --bundler esbuild",
|
||||||
"watch": "tswatch element",
|
"watch": "tswatch element",
|
||||||
"buildDocs": "tsdoc"
|
"buildDocs": "tsdoc",
|
||||||
|
"postinstall": "node scripts/update-monaco-version.cjs"
|
||||||
},
|
},
|
||||||
"author": "Lossless GmbH",
|
"author": "Lossless GmbH",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
80
readme.info.md
Normal file
80
readme.info.md
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# Contributor Information
|
||||||
|
|
||||||
|
This reference consolidates the helper notes previously split across `codex.md` and `CLAUDE.md`. Use it to get oriented quickly when working on `@design.estate/dees-catalog`, a TypeScript/Lit web-components library that ships themed UI building blocks for modern web applications.
|
||||||
|
|
||||||
|
## Project Snapshot
|
||||||
|
- Package: `@design.estate/dees-catalog`
|
||||||
|
- Description: Comprehensive catalog of reusable web components with cohesive design, advanced form inputs, data displays, and layout scaffolding.
|
||||||
|
- Entry points: builds ship to `dist_ts_web/` (ES modules) and `dist_bundle/` (browser bundle); demos live in `html/`.
|
||||||
|
- Type system: strict TypeScript targeting modern browsers (see `tsconfig.json`).
|
||||||
|
|
||||||
|
## Repository Layout
|
||||||
|
- `ts_web/` – TypeScript source
|
||||||
|
- `elements/` – component implementations (`00*.ts` shared utilities, `dees-*.ts` components, `*.demo.ts` demos)
|
||||||
|
- `pages/` – showcase pages aggregating demos
|
||||||
|
- `index.ts` – main export surface
|
||||||
|
- `html/` – demo entry point bootstrapping bundles
|
||||||
|
- `dist_bundle/`, `dist_ts_web/`, `dist_watch/` – build outputs (production, module, and watch bundles)
|
||||||
|
- `test/` – browser/node tests powered by `@push.rocks/tapbundle`
|
||||||
|
- `scripts/` – maintenance utilities (e.g., Monaco version sync postinstall)
|
||||||
|
|
||||||
|
## Build & Development Commands
|
||||||
|
All workflows use pnpm (see `package.json`).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm install # install dependencies
|
||||||
|
pnpm run build # tsbuild tsfolders --allowimplicitany && tsbundle element --production --bundler esbuild
|
||||||
|
pnpm run watch # tswatch element (development watch/dev server)
|
||||||
|
pnpm test # tstest test/ --web --verbose --timeout 30 --logfile
|
||||||
|
pnpm run buildDocs # tsdoc (generates docs)
|
||||||
|
tsx test/test.file.ts # run a specific test file (file must be named test.*)
|
||||||
|
```
|
||||||
|
|
||||||
|
`postinstall` runs `node scripts/update-monaco-version.cjs` to sync the Monaco editor version, so keep the script intact when updating dependencies.
|
||||||
|
|
||||||
|
## Testing Guidelines
|
||||||
|
- Framework: `@push.rocks/tapbundle` with smartexpect assertions. Always review https://code.foss.global/push.rocks/smartexpect/raw/branch/master/readme.md when adding tests.
|
||||||
|
- Import pattern:
|
||||||
|
```typescript
|
||||||
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
|
```
|
||||||
|
- Test naming: `test.*.both.ts` for dual runtime, `.node.ts` for Node-only, `.browser.ts` for browser-only suites.
|
||||||
|
- Prefer `pnpm test` for full runs; use `tsx` for focused debugging. Type-check failing tests with `tsc --noEmit`.
|
||||||
|
- Logs live under `.nogit/testlogs/`; put ad-hoc debug artefacts in `.nogit/debug/`.
|
||||||
|
|
||||||
|
## Component Architecture
|
||||||
|
- **Base pattern**: Components extend `DeesElement` from `@design.estate/dees-element`, use Lit decorators (`@customElement`, `@property`), and combine `cssManager.defaultStyles` with component styles. Rendering happens via Lit `html` templates; demos sit on a static `demo` property referencing a `.demo.ts` module.
|
||||||
|
- **Theming**: `cssManager.bdTheme(light, dark)` selects theme-aware values. Shared palettes live in `ts_web/elements/00colors.ts`.
|
||||||
|
- **Z-index management**: Overlays consult the registry in `ts_web/elements/00zindex.ts` (`ZIndexRegistry`) to coordinate stacking.
|
||||||
|
- **Component families**:
|
||||||
|
- Core UI (`dees-button`, `dees-badge`, `dees-icon`, …) focus on consistent theming and interactions.
|
||||||
|
- Form inputs (`dees-form`, `dees-input-*`) build on `DeesInputBase` and communicate through subjects/events for validation.
|
||||||
|
- Layout shells (`dees-appui-*`) orchestrate responsive app frames with centralized event rebroadcasts.
|
||||||
|
- Data views (`dees-table`, `dees-dataview-*`, `dees-statsgrid`) handle large datasets with virtualisation and chart integrations.
|
||||||
|
- Overlays (`dees-modal`, `dees-contextmenu`, `dees-toast`) respect the z-index registry and use shared window-layer utilities.
|
||||||
|
- **WYSIWYG editor**: `dees-input-wysiwyg` coordinates specialized handler classes (`WysiwygInputHandler`, `WysiwygKeyboardHandler`, drag/drop & modal managers) and global menus (`DeesSlashMenu`, `DeesFormattingMenu`). Rendering is imperative to preserve caret focus.
|
||||||
|
|
||||||
|
## Implementation Guidelines
|
||||||
|
- Import external modules through `ts_web/elements/00plugins.ts`: `import * as plugins from './plugins.ts';` then reference `plugins.moduleName`.
|
||||||
|
- When creating new components:
|
||||||
|
1. Extend `DeesElement` and decorate with `@customElement('dees-component')`.
|
||||||
|
2. Support theming, slots, and accessibility; provide meaningful default styles.
|
||||||
|
3. Expose a `.demo.ts` for the component and re-export via `elements/index.ts`.
|
||||||
|
- Form components must implement `getValue()` / `setValue()` and emit through `changeSubject` while honoring `disabled` and `required` states.
|
||||||
|
- Overlay components retrieve z-indices from the registry, register/unregister on show/hide, and use `DeesWindowLayer` for backdrops when appropriate.
|
||||||
|
- Avoid simplifying away functionality; prefer small, targeted changes and keep compatibility with existing APIs.
|
||||||
|
|
||||||
|
## Common Patterns & Pitfalls
|
||||||
|
- Focus management: schedule DOM updates with `requestAnimationFrame` inside interactive editors to avoid focus loss.
|
||||||
|
- Event handling: stop propagation where nested interactive elements coexist; mix pointer and keyboard handling for accessibility.
|
||||||
|
- Performance: heavy blocks/components may load lazily; charts use debounced observers, tables rely on virtual scrolling. Watch bundle size when adding dependencies.
|
||||||
|
|
||||||
|
## Documentation & Demos
|
||||||
|
- `readme.md` surfaces component overviews; demos in `.demo.ts` illustrate real usage.
|
||||||
|
- Update this `readme.info.md` when architectural patterns or workflows change so contributors stay in sync.
|
||||||
|
|
||||||
|
## Recent Highlights
|
||||||
|
- Z-index registry overhaul enables dynamic stacking control across overlays.
|
||||||
|
- WYSIWYG refactor separated block handlers for maintainability.
|
||||||
|
- Dashboard grid enhancements added live drag-and-drop previews and overlap fixes.
|
||||||
|
- Monaco editor integration now reads the installed version at build time.
|
BIN
readme.plan.md
BIN
readme.plan.md
Binary file not shown.
44
scripts/update-monaco-version.cjs
Executable file
44
scripts/update-monaco-version.cjs
Executable file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const projectRoot = path.resolve(__dirname, '..');
|
||||||
|
|
||||||
|
function resolveMonacoPackageJson() {
|
||||||
|
try {
|
||||||
|
const resolvedPath = require.resolve('monaco-editor/package.json', {
|
||||||
|
paths: [projectRoot],
|
||||||
|
});
|
||||||
|
return resolvedPath;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[dees-editor] Unable to resolve monaco-editor/package.json');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMonacoVersion() {
|
||||||
|
const monacoPackagePath = resolveMonacoPackageJson();
|
||||||
|
const monacoPackage = require(monacoPackagePath);
|
||||||
|
if (!monacoPackage.version) {
|
||||||
|
throw new Error('[dees-editor] monaco-editor/package.json does not expose a version field');
|
||||||
|
}
|
||||||
|
return monacoPackage.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeVersionModule(version) {
|
||||||
|
const targetDir = path.join(projectRoot, 'ts_web', 'elements', 'dees-editor');
|
||||||
|
fs.mkdirSync(targetDir, { recursive: true });
|
||||||
|
const targetFile = path.join(targetDir, 'version.ts');
|
||||||
|
const fileContent = `// Auto-generated by scripts/update-monaco-version.cjs\nexport const MONACO_VERSION = '${version}';\n`;
|
||||||
|
fs.writeFileSync(targetFile, fileContent, 'utf8');
|
||||||
|
console.log(`[dees-editor] Wrote ${path.relative(projectRoot, targetFile)} with monaco-editor@${version}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const version = getMonacoVersion();
|
||||||
|
writeVersionModule(version);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[dees-editor] Failed to update Monaco version module.');
|
||||||
|
console.error(error instanceof Error ? error.message : error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
28
test/test.dashboardgrid-layout.node.ts
Normal file
28
test/test.dashboardgrid-layout.node.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
|
|
||||||
|
import {
|
||||||
|
resolveWidgetPlacement,
|
||||||
|
collectCollisions,
|
||||||
|
} from '../ts_web/elements/dees-dashboardgrid/layout.ts';
|
||||||
|
import type { DashboardWidget } from '../ts_web/elements/dees-dashboardgrid/types.ts';
|
||||||
|
|
||||||
|
tap.test('dashboardgrid does not overlap widgets after swap attempt', async () => {
|
||||||
|
const widgets: DashboardWidget[] = [
|
||||||
|
{ id: 'w0', x: 6, y: 5, w: 1, h: 3 },
|
||||||
|
{ id: 'w1', x: 6, y: 1, w: 1, h: 3 },
|
||||||
|
{ id: 'w2', x: 3, y: 0, w: 2, h: 2 },
|
||||||
|
{ id: 'w3', x: 9, y: 0, w: 1, h: 2 },
|
||||||
|
{ id: 'w4', x: 4, y: 3, w: 1, h: 2 },
|
||||||
|
];
|
||||||
|
|
||||||
|
const placement = resolveWidgetPlacement(widgets, 'w0', { x: 6, y: 3 }, 12);
|
||||||
|
expect(placement).toBeTruthy();
|
||||||
|
|
||||||
|
const layout = placement!.widgets;
|
||||||
|
for (const widget of layout) {
|
||||||
|
const collisions = collectCollisions(layout, widget, widget.x, widget.y, widget.w, widget.h);
|
||||||
|
expect(collisions).toBeEmptyArray();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@design.estate/dees-catalog',
|
name: '@design.estate/dees-catalog',
|
||||||
version: '1.11.8',
|
version: '1.12.2',
|
||||||
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
|
||||||
}
|
}
|
||||||
|
@@ -159,21 +159,169 @@ export const demoFunc = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enhanced logging for reflow events
|
||||||
|
let lastPlaceholderPosition = null;
|
||||||
|
let moveEventCounter = 0;
|
||||||
|
|
||||||
|
// Helper function to log grid state
|
||||||
|
const logGridState = (eventName: string, details?: any) => {
|
||||||
|
const layout = grid.getLayout();
|
||||||
|
console.group(`🔄 ${eventName} [Event #${++moveEventCounter}]`);
|
||||||
|
console.log('Timestamp:', new Date().toISOString());
|
||||||
|
console.log('Grid Configuration:', {
|
||||||
|
columns: grid.columns,
|
||||||
|
cellHeight: grid.cellHeight,
|
||||||
|
margin: grid.margin,
|
||||||
|
editable: grid.editable,
|
||||||
|
activeBreakpoint: grid.activeBreakpoint
|
||||||
|
});
|
||||||
|
console.log('Current Layout:', layout);
|
||||||
|
console.log('Widget Count:', layout.length);
|
||||||
|
console.log('Grid Bounds:', {
|
||||||
|
totalWidgets: grid.widgets.length,
|
||||||
|
maxY: Math.max(...layout.map(w => w.y + w.h)),
|
||||||
|
occupied: layout.map(w => `${w.id}: (${w.x},${w.y}) ${w.w}x${w.h}`).join(', ')
|
||||||
|
});
|
||||||
|
if (details) {
|
||||||
|
console.log('Event Details:', details);
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Monitor placeholder position changes using MutationObserver
|
||||||
|
const placeholderObserver = new MutationObserver(() => {
|
||||||
|
const placeholder = grid.shadowRoot?.querySelector('.placeholder') as HTMLElement;
|
||||||
|
if (placeholder) {
|
||||||
|
const currentPosition = {
|
||||||
|
left: placeholder.style.left,
|
||||||
|
top: placeholder.style.top,
|
||||||
|
width: placeholder.style.width,
|
||||||
|
height: placeholder.style.height
|
||||||
|
};
|
||||||
|
|
||||||
|
if (JSON.stringify(currentPosition) !== JSON.stringify(lastPlaceholderPosition)) {
|
||||||
|
console.group('📍 Placeholder Position Changed');
|
||||||
|
console.log('Previous:', lastPlaceholderPosition);
|
||||||
|
console.log('Current:', currentPosition);
|
||||||
|
|
||||||
|
// Extract grid coordinates from style
|
||||||
|
const gridInfo = grid.shadowRoot?.querySelector('.grid-container');
|
||||||
|
if (gridInfo) {
|
||||||
|
console.log('Grid Container Dimensions:', {
|
||||||
|
width: gridInfo.clientWidth,
|
||||||
|
height: gridInfo.clientHeight
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.groupEnd();
|
||||||
|
lastPlaceholderPosition = currentPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start observing the shadow DOM for placeholder changes
|
||||||
|
if (grid.shadowRoot) {
|
||||||
|
placeholderObserver.observe(grid.shadowRoot, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style']
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log initial state
|
||||||
|
logGridState('Initial Grid State');
|
||||||
|
|
||||||
grid.addEventListener('widget-move', (e: CustomEvent) => {
|
grid.addEventListener('widget-move', (e: CustomEvent) => {
|
||||||
console.log('Widget moved:', e.detail.widget, 'Displaced:', e.detail.displaced);
|
logGridState('Widget Move', {
|
||||||
|
widget: e.detail.widget,
|
||||||
|
displaced: e.detail.displaced,
|
||||||
|
swappedWith: e.detail.swappedWith
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
grid.addEventListener('widget-resize', (e: CustomEvent) => {
|
grid.addEventListener('widget-resize', (e: CustomEvent) => {
|
||||||
console.log('Widget resized:', e.detail.widget, 'Displaced:', e.detail.displaced);
|
logGridState('Widget Resize', {
|
||||||
|
widget: e.detail.widget,
|
||||||
|
displaced: e.detail.displaced,
|
||||||
|
swappedWith: e.detail.swappedWith
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
grid.addEventListener('widget-remove', (e: CustomEvent) => {
|
grid.addEventListener('widget-remove', (e: CustomEvent) => {
|
||||||
console.log('Widget removed:', e.detail.widget);
|
logGridState('Widget Remove', {
|
||||||
|
removedWidget: e.detail.widget
|
||||||
|
});
|
||||||
updateStatus();
|
updateStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
grid.addEventListener('layout-change', () => {
|
grid.addEventListener('layout-change', () => {
|
||||||
console.log('Layout changed:', grid.getLayout());
|
logGridState('Layout Change');
|
||||||
updateStatus();
|
updateStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Monitor during drag/resize operations using pointer events
|
||||||
|
grid.addEventListener('pointerdown', (e: PointerEvent) => {
|
||||||
|
const isHeader = (e.target as HTMLElement).closest('.widget-header');
|
||||||
|
const isResizeHandle = (e.target as HTMLElement).closest('.resize-handle');
|
||||||
|
|
||||||
|
if (isHeader || isResizeHandle) {
|
||||||
|
console.group(`🎯 Interaction Started: ${isHeader ? 'Drag' : 'Resize'}`);
|
||||||
|
console.log('Target Widget:', (e.target as HTMLElement).closest('.widget')?.getAttribute('data-widget-id'));
|
||||||
|
console.log('Pointer Position:', { x: e.clientX, y: e.clientY });
|
||||||
|
console.groupEnd();
|
||||||
|
|
||||||
|
// Track pointer move during interaction
|
||||||
|
const handlePointerMove = (moveEvent: PointerEvent) => {
|
||||||
|
const widget = (e.target as HTMLElement).closest('.widget');
|
||||||
|
if (widget) {
|
||||||
|
console.log(`↔️ Pointer Move:`, {
|
||||||
|
widgetId: widget.getAttribute('data-widget-id'),
|
||||||
|
position: { x: moveEvent.clientX, y: moveEvent.clientY },
|
||||||
|
delta: {
|
||||||
|
x: moveEvent.clientX - e.clientX,
|
||||||
|
y: moveEvent.clientY - e.clientY
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePointerUp = () => {
|
||||||
|
console.group('🏁 Interaction Ended');
|
||||||
|
logGridState('Final State After Interaction');
|
||||||
|
console.groupEnd();
|
||||||
|
document.removeEventListener('pointermove', handlePointerMove);
|
||||||
|
document.removeEventListener('pointerup', handlePointerUp);
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener('pointermove', handlePointerMove);
|
||||||
|
document.addEventListener('pointerup', handlePointerUp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Log when widgets are added
|
||||||
|
const originalAddWidget = grid.addWidget.bind(grid);
|
||||||
|
grid.addWidget = (widget: any, autoPosition?: boolean) => {
|
||||||
|
console.group('➕ Adding Widget');
|
||||||
|
console.log('New Widget:', widget);
|
||||||
|
console.log('Auto Position:', autoPosition);
|
||||||
|
const result = originalAddWidget(widget, autoPosition);
|
||||||
|
logGridState('After Widget Added');
|
||||||
|
console.groupEnd();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log compact operations
|
||||||
|
const originalCompact = grid.compact.bind(grid);
|
||||||
|
grid.compact = (direction?: string) => {
|
||||||
|
console.group('🗜️ Compacting Grid');
|
||||||
|
console.log('Direction:', direction || 'vertical');
|
||||||
|
logGridState('Before Compact');
|
||||||
|
const result = originalCompact(direction);
|
||||||
|
logGridState('After Compact');
|
||||||
|
console.groupEnd();
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}}>
|
}}>
|
||||||
<style>
|
<style>
|
||||||
|
@@ -413,20 +413,42 @@ export class DeesDashboardgrid extends DeesElement {
|
|||||||
|
|
||||||
const layoutSource = this.widgets;
|
const layoutSource = this.widgets;
|
||||||
this.previewWidgets = null;
|
this.previewWidgets = null;
|
||||||
|
|
||||||
|
// Always validate the final position, don't rely on lastPlacement from drag
|
||||||
const target = this.placeholderPosition ?? dragState.start;
|
const target = this.placeholderPosition ?? dragState.start;
|
||||||
const placement =
|
const placement = resolveWidgetPlacement(
|
||||||
dragState.lastPlacement ??
|
layoutSource,
|
||||||
resolveWidgetPlacement(
|
dragState.widgetId,
|
||||||
layoutSource,
|
{ x: target.x, y: target.y },
|
||||||
dragState.widgetId,
|
this.columns,
|
||||||
{ x: target.x, y: target.y },
|
dragState.previousPosition,
|
||||||
this.columns,
|
);
|
||||||
dragState.previousPosition,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (placement) {
|
if (placement) {
|
||||||
this.commitPlacement(placement, dragState.widgetId, 'widget-move');
|
// Verify that the placement doesn't result in overlapping widgets
|
||||||
|
const finalWidget = placement.widgets.find(w => w.id === dragState.widgetId);
|
||||||
|
if (finalWidget) {
|
||||||
|
const hasOverlap = placement.widgets.some(w => {
|
||||||
|
if (w.id === dragState.widgetId) return false;
|
||||||
|
return (
|
||||||
|
finalWidget.x < w.x + w.w &&
|
||||||
|
finalWidget.x + finalWidget.w > w.x &&
|
||||||
|
finalWidget.y < w.y + w.h &&
|
||||||
|
finalWidget.y + finalWidget.h > w.y
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasOverlap) {
|
||||||
|
this.commitPlacement(placement, dragState.widgetId, 'widget-move');
|
||||||
|
} else {
|
||||||
|
// Return to start position if overlap detected
|
||||||
|
this.widgets = this.widgets.map(widget =>
|
||||||
|
widget.id === dragState.widgetId ? { ...widget, x: dragState.start.x, y: dragState.start.y } : widget,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Return to start position if no valid placement
|
||||||
this.widgets = this.widgets.map(widget =>
|
this.widgets = this.widgets.map(widget =>
|
||||||
widget.id === dragState.widgetId ? { ...widget, x: dragState.start.x, y: dragState.start.y } : widget,
|
widget.id === dragState.widgetId ? { ...widget, x: dragState.start.x, y: dragState.start.y } : widget,
|
||||||
);
|
);
|
||||||
|
@@ -161,10 +161,23 @@ export const resolveWidgetPlacement = (
|
|||||||
if (!other.locked && !other.noMove && other.w === moving.w && other.h === moving.h) {
|
if (!other.locked && !other.noMove && other.w === moving.w && other.h === moving.h) {
|
||||||
const otherClone = sourceWidgets.find(widget => widget.id === other.id);
|
const otherClone = sourceWidgets.find(widget => widget.id === other.id);
|
||||||
if (otherClone) {
|
if (otherClone) {
|
||||||
const swapTarget = previousPosition ?? original;
|
// Use the original position of the moving widget for a clean swap
|
||||||
|
// This prevents the "snapping together" issue where both widgets end up at the same position
|
||||||
|
const swapTarget = original;
|
||||||
|
const previousOtherPosition = { x: otherClone.x, y: otherClone.y };
|
||||||
otherClone.x = swapTarget.x;
|
otherClone.x = swapTarget.x;
|
||||||
otherClone.y = swapTarget.y;
|
otherClone.y = swapTarget.y;
|
||||||
return { widgets: sourceWidgets, movedWidgets: [moving.id, otherClone.id], swappedWith: otherClone.id };
|
|
||||||
|
const swapValid =
|
||||||
|
collectCollisions(sourceWidgets, moving, moving.x, moving.y, moving.w, moving.h).length === 0 &&
|
||||||
|
collectCollisions(sourceWidgets, otherClone, otherClone.x, otherClone.y, otherClone.w, otherClone.h).length === 0;
|
||||||
|
|
||||||
|
if (swapValid) {
|
||||||
|
return { widgets: sourceWidgets, movedWidgets: [moving.id, otherClone.id], swappedWith: otherClone.id };
|
||||||
|
}
|
||||||
|
|
||||||
|
otherClone.x = previousOtherPosition.x;
|
||||||
|
otherClone.y = previousOtherPosition.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ import {
|
|||||||
cssManager,
|
cssManager,
|
||||||
} from '@design.estate/dees-element';
|
} from '@design.estate/dees-element';
|
||||||
import * as domtools from '@design.estate/dees-domtools';
|
import * as domtools from '@design.estate/dees-domtools';
|
||||||
|
import { MONACO_VERSION } from './version.js';
|
||||||
|
|
||||||
import type * as monaco from 'monaco-editor';
|
import type * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
@@ -80,10 +81,11 @@ export class DeesEditor extends DeesElement {
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
super.firstUpdated(_changedProperties);
|
super.firstUpdated(_changedProperties);
|
||||||
const container = this.shadowRoot.getElementById('container');
|
const container = this.shadowRoot.getElementById('container');
|
||||||
|
const monacoCdnBase = `https://cdn.jsdelivr.net/npm/monaco-editor@${MONACO_VERSION}`;
|
||||||
|
|
||||||
if (!DeesEditor.monacoDeferred) {
|
if (!DeesEditor.monacoDeferred) {
|
||||||
DeesEditor.monacoDeferred = domtools.plugins.smartpromise.defer();
|
DeesEditor.monacoDeferred = domtools.plugins.smartpromise.defer();
|
||||||
const scriptUrl = `https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/loader.js`;
|
const scriptUrl = `${monacoCdnBase}/min/vs/loader.js`;
|
||||||
const script = document.createElement('script');
|
const script = document.createElement('script');
|
||||||
script.src = scriptUrl;
|
script.src = scriptUrl;
|
||||||
script.onload = () => {
|
script.onload = () => {
|
||||||
@@ -94,7 +96,7 @@ export class DeesEditor extends DeesElement {
|
|||||||
await DeesEditor.monacoDeferred.promise;
|
await DeesEditor.monacoDeferred.promise;
|
||||||
|
|
||||||
(window as any).require.config({
|
(window as any).require.config({
|
||||||
paths: { vs: 'https://cdn.jsdelivr.net/npm/monaco-editor/min/vs' },
|
paths: { vs: `${monacoCdnBase}/min/vs` },
|
||||||
});
|
});
|
||||||
(window as any).require(['vs/editor/editor.main'], async () => {
|
(window as any).require(['vs/editor/editor.main'], async () => {
|
||||||
const editor = ((window as any).monaco.editor as typeof monaco.editor).create(container, {
|
const editor = ((window as any).monaco.editor as typeof monaco.editor).create(container, {
|
||||||
@@ -109,7 +111,7 @@ export class DeesEditor extends DeesElement {
|
|||||||
this.editorDeferred.resolve(editor);
|
this.editorDeferred.resolve(editor);
|
||||||
});
|
});
|
||||||
const css = await (
|
const css = await (
|
||||||
await fetch('https://cdn.jsdelivr.net/npm/monaco-editor/min/vs/editor/editor.main.css')
|
await fetch(`${monacoCdnBase}/min/vs/editor/editor.main.css`)
|
||||||
).text();
|
).text();
|
||||||
const styleElement = document.createElement('style');
|
const styleElement = document.createElement('style');
|
||||||
styleElement.textContent = css;
|
styleElement.textContent = css;
|
2
ts_web/elements/dees-editor/index.ts
Normal file
2
ts_web/elements/dees-editor/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './dees-editor.js';
|
||||||
|
export * from './version.js';
|
2
ts_web/elements/dees-editor/version.ts
Normal file
2
ts_web/elements/dees-editor/version.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// Auto-generated by scripts/update-monaco-version.cjs
|
||||||
|
export const MONACO_VERSION = '0.52.2';
|
@@ -20,7 +20,7 @@ import { DeesInputMultitoggle } from './dees-input-multitoggle.js';
|
|||||||
import { DeesInputPhone } from './dees-input-phone.js';
|
import { DeesInputPhone } from './dees-input-phone.js';
|
||||||
import { DeesInputTypelist } from './dees-input-typelist.js';
|
import { DeesInputTypelist } from './dees-input-typelist.js';
|
||||||
import { DeesFormSubmit } from './dees-form-submit.js';
|
import { DeesFormSubmit } from './dees-form-submit.js';
|
||||||
import { DeesTable } from './dees-table/dees-table.js';
|
import { DeesTable } from './dees-table/index.js';
|
||||||
import { demoFunc } from './dees-form.demo.js';
|
import { demoFunc } from './dees-form.demo.js';
|
||||||
|
|
||||||
// Unified set for form input types
|
// Unified set for form input types
|
||||||
|
@@ -1,8 +1,8 @@
|
|||||||
import { html, css, type TemplateResult } from '@design.estate/dees-element';
|
import { html, css, type TemplateResult } from '@design.estate/dees-element';
|
||||||
import '@design.estate/dees-wcctools/demotools';
|
import '@design.estate/dees-wcctools/demotools';
|
||||||
import './dees-panel.js';
|
import './dees-panel.js';
|
||||||
import type { DeesInputWysiwyg } from './dees-input-wysiwyg.js';
|
import type { DeesInputWysiwyg } from './dees-input-wysiwyg/dees-input-wysiwyg.js';
|
||||||
import type { IBlock } from './wysiwyg/wysiwyg.types.js';
|
import type { IBlock } from './dees-input-wysiwyg/wysiwyg.types.js';
|
||||||
|
|
||||||
interface IDemoEditor {
|
interface IDemoEditor {
|
||||||
basic: DeesInputWysiwyg;
|
basic: DeesInputWysiwyg;
|
||||||
@@ -250,6 +250,30 @@ const setupExportDemo = (container: HTMLElement, editor: DeesInputWysiwyg) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setupOutputFormatDemo = (
|
||||||
|
container: HTMLElement,
|
||||||
|
htmlEditor?: DeesInputWysiwyg,
|
||||||
|
markdownEditor?: DeesInputWysiwyg,
|
||||||
|
) => {
|
||||||
|
const htmlBtn = container.querySelector('#btn-show-html-output') as HTMLButtonElement | null;
|
||||||
|
const htmlPreview = container.querySelector('#output-preview-html') as HTMLElement | null;
|
||||||
|
if (htmlBtn && htmlPreview && htmlEditor) {
|
||||||
|
htmlBtn.addEventListener('click', () => {
|
||||||
|
htmlPreview.textContent = htmlEditor.getValue();
|
||||||
|
htmlPreview.classList.add('visible');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const markdownBtn = container.querySelector('#btn-show-markdown-output') as HTMLButtonElement | null;
|
||||||
|
const markdownPreview = container.querySelector('#output-preview-markdown') as HTMLElement | null;
|
||||||
|
if (markdownBtn && markdownPreview && markdownEditor) {
|
||||||
|
markdownBtn.addEventListener('click', () => {
|
||||||
|
markdownPreview.textContent = markdownEditor.getValue();
|
||||||
|
markdownPreview.classList.add('visible');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const populateInitialContent = (editors: IDemoEditor) => {
|
const populateInitialContent = (editors: IDemoEditor) => {
|
||||||
// Article editor content
|
// Article editor content
|
||||||
if (editors.article) {
|
if (editors.article) {
|
||||||
@@ -359,7 +383,10 @@ export const demoFunc = (): TemplateResult => html`
|
|||||||
if (editors.exportDemo) {
|
if (editors.exportDemo) {
|
||||||
setupExportDemo(elementArg, editors.exportDemo);
|
setupExportDemo(elementArg, editors.exportDemo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup output format preview buttons
|
||||||
|
setupOutputFormatDemo(elementArg, editors.meeting, editors.recipe);
|
||||||
|
|
||||||
// Populate initial content
|
// Populate initial content
|
||||||
populateInitialContent(editors);
|
populateInitialContent(editors);
|
||||||
|
|
||||||
@@ -488,11 +515,46 @@ export const demoFunc = (): TemplateResult => html`
|
|||||||
|
|
||||||
.output-grid {
|
.output-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
|
||||||
gap: 24px;
|
gap: 24px;
|
||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.output-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-preview {
|
||||||
|
display: none;
|
||||||
|
background: rgba(15, 23, 42, 0.04);
|
||||||
|
color: var(--dees-color-text, #0f172a);
|
||||||
|
border: 1px solid rgba(15, 23, 42, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: 'Geist Mono', 'Fira Code', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
max-height: 280px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([theme='dark']) .output-preview {
|
||||||
|
background: rgba(250, 250, 250, 0.06);
|
||||||
|
border-color: rgba(250, 250, 250, 0.15);
|
||||||
|
color: var(--dees-color-text, #f4f4f5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.output-preview.visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.output-grid {
|
.output-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@@ -858,7 +920,7 @@ git merge feature-branch
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="output-grid">
|
<div class="output-grid">
|
||||||
<div>
|
<div class="output-card">
|
||||||
<dees-input-wysiwyg
|
<dees-input-wysiwyg
|
||||||
id="editor-meeting"
|
id="editor-meeting"
|
||||||
label="Meeting Notes"
|
label="Meeting Notes"
|
||||||
@@ -866,9 +928,13 @@ git merge feature-branch
|
|||||||
outputFormat="html"
|
outputFormat="html"
|
||||||
value="<h2>Q4 Planning Meeting</h2><p><strong>Date:</strong> December 15, 2024<br><strong>Attendees:</strong> Product Team, Engineering, Design</p><h3>Agenda Items</h3><ol><li>Review Q3 achievements</li><li>Set Q4 objectives</li><li>Resource allocation</li><li>Timeline discussion</li></ol><h3>Key Decisions</h3><ul><li>Launch new dashboard feature by end of January</li><li>Increase engineering team by 2 developers</li><li>Implement weekly design reviews</li></ul><blockquote>"Focus on user experience improvements based on Q3 feedback" - Product Manager</blockquote><h3>Action Items</h3><ul><li>Sarah: Create detailed project timeline</li><li>Mike: Draft technical requirements</li><li>Lisa: Schedule user research sessions</li></ul><hr><p>Next meeting: January 5, 2025</p>"
|
value="<h2>Q4 Planning Meeting</h2><p><strong>Date:</strong> December 15, 2024<br><strong>Attendees:</strong> Product Team, Engineering, Design</p><h3>Agenda Items</h3><ol><li>Review Q3 achievements</li><li>Set Q4 objectives</li><li>Resource allocation</li><li>Timeline discussion</li></ol><h3>Key Decisions</h3><ul><li>Launch new dashboard feature by end of January</li><li>Increase engineering team by 2 developers</li><li>Implement weekly design reviews</li></ul><blockquote>"Focus on user experience improvements based on Q3 feedback" - Product Manager</blockquote><h3>Action Items</h3><ul><li>Sarah: Create detailed project timeline</li><li>Mike: Draft technical requirements</li><li>Lisa: Schedule user research sessions</li></ul><hr><p>Next meeting: January 5, 2025</p>"
|
||||||
></dees-input-wysiwyg>
|
></dees-input-wysiwyg>
|
||||||
|
<div class="output-actions">
|
||||||
|
<button id="btn-show-html-output" class="demo-button">Show HTML Output</button>
|
||||||
|
</div>
|
||||||
|
<pre id="output-preview-html" class="output-preview" aria-live="polite"></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="output-card">
|
||||||
<dees-input-wysiwyg
|
<dees-input-wysiwyg
|
||||||
id="editor-recipe"
|
id="editor-recipe"
|
||||||
label="Recipe Blog Post"
|
label="Recipe Blog Post"
|
||||||
@@ -927,6 +993,10 @@ Gradually blend in flour mixture, then stir in chocolate chips. Drop rounded tab
|
|||||||
|
|
||||||
**Yield:** About 5 dozen cookies"
|
**Yield:** About 5 dozen cookies"
|
||||||
></dees-input-wysiwyg>
|
></dees-input-wysiwyg>
|
||||||
|
<div class="output-actions">
|
||||||
|
<button id="btn-show-markdown-output" class="demo-button">Show Markdown Output</button>
|
||||||
|
</div>
|
||||||
|
<pre id="output-preview-markdown" class="output-preview" aria-live="polite"></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</dees-panel>
|
</dees-panel>
|
||||||
@@ -1066,4 +1136,4 @@ POST /api/v2/batch
|
|||||||
</dees-panel>
|
</dees-panel>
|
||||||
</div>
|
</div>
|
||||||
</dees-demowrapper>
|
</dees-demowrapper>
|
||||||
`;
|
`;
|
||||||
|
@@ -1,2 +1,3 @@
|
|||||||
// Re-export the modular component from the wysiwyg directory
|
// Re-export the component and related helpers from the dedicated subdirectory
|
||||||
export { DeesInputWysiwyg } from './wysiwyg/dees-input-wysiwyg.js';
|
export { DeesInputWysiwyg } from './dees-input-wysiwyg/dees-input-wysiwyg.js';
|
||||||
|
export * from './dees-input-wysiwyg/index.js';
|
||||||
|
@@ -118,6 +118,17 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async firstUpdated() {
|
async firstUpdated() {
|
||||||
|
if (this.value && this.value.trim().length > 0) {
|
||||||
|
const parsedBlocks =
|
||||||
|
this.outputFormat === 'html'
|
||||||
|
? WysiwygConverters.parseHtmlToBlocks(this.value)
|
||||||
|
: WysiwygConverters.parseMarkdownToBlocks(this.value);
|
||||||
|
|
||||||
|
if (parsedBlocks.length > 0) {
|
||||||
|
this.blocks = parsedBlocks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.updateValue();
|
this.updateValue();
|
||||||
this.editorContentRef = this.shadowRoot!.querySelector('.editor-content') as HTMLDivElement;
|
this.editorContentRef = this.shadowRoot!.querySelector('.editor-content') as HTMLDivElement;
|
||||||
|
|
||||||
@@ -988,4 +999,4 @@ export class DeesInputWysiwyg extends DeesInputBase<string> {
|
|||||||
this.history.saveCheckpoint(this.blocks, this.selectedBlockId, cursorPosition);
|
this.history.saveCheckpoint(this.blocks, this.selectedBlockId, cursorPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,4 +15,4 @@ export * from './wysiwyg.modalmanager.js';
|
|||||||
export * from './wysiwyg.history.js';
|
export * from './wysiwyg.history.js';
|
||||||
export * from './dees-wysiwyg-block.js';
|
export * from './dees-wysiwyg-block.js';
|
||||||
export * from './dees-slash-menu.js';
|
export * from './dees-slash-menu.js';
|
||||||
export * from './dees-formatting-menu.js';
|
export * from './dees-formatting-menu.js';
|
134
ts_web/elements/dees-stepper/dees-stepper.demo.ts
Normal file
134
ts_web/elements/dees-stepper/dees-stepper.demo.ts
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
import { html } from '@design.estate/dees-element';
|
||||||
|
|
||||||
|
export const stepperDemo = () => html`
|
||||||
|
<dees-stepper
|
||||||
|
.steps=${[
|
||||||
|
{
|
||||||
|
title: 'Account Setup',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-text key="email" label="Work Email" required></dees-input-text>
|
||||||
|
<dees-input-text key="password" label="Create Password" type="password" required></dees-input-text>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Profile Details',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-text key="firstName" label="First Name" required></dees-input-text>
|
||||||
|
<dees-input-text key="lastName" label="Last Name" required></dees-input-text>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Contact Information',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-phone key="phone" label="Mobile Number" required></dees-input-phone>
|
||||||
|
<dees-input-text key="company" label="Company"></dees-input-text>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Team Size',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-dropdown
|
||||||
|
key="teamSize"
|
||||||
|
label="How big is your team?"
|
||||||
|
.options=${[
|
||||||
|
{ label: '1-5', value: '1-5' },
|
||||||
|
{ label: '6-20', value: '6-20' },
|
||||||
|
{ label: '21-50', value: '21-50' },
|
||||||
|
{ label: '51+', value: '51+' },
|
||||||
|
]}
|
||||||
|
required
|
||||||
|
></dees-input-dropdown>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Goals',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-multitoggle
|
||||||
|
key="goal"
|
||||||
|
label="Main objective"
|
||||||
|
.options=${[
|
||||||
|
{ label: 'Onboarding', value: 'onboarding' },
|
||||||
|
{ label: 'Analytics', value: 'analytics' },
|
||||||
|
{ label: 'Automation', value: 'automation' },
|
||||||
|
]}
|
||||||
|
required
|
||||||
|
></dees-input-multitoggle>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Brand Preferences',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-text key="brandColor" label="Primary brand color"></dees-input-text>
|
||||||
|
<dees-input-text key="tone" label="Preferred tone (e.g. friendly, formal)"></dees-input-text>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Integrations',
|
||||||
|
content: html`
|
||||||
|
<dees-form>
|
||||||
|
<dees-input-list
|
||||||
|
key="integrations"
|
||||||
|
label="Integrations in use"
|
||||||
|
placeholder="Add integration"
|
||||||
|
></dees-input-list>
|
||||||
|
<dees-form-submit>Continue</dees-form-submit>
|
||||||
|
</dees-form>
|
||||||
|
`,
|
||||||
|
validationFunc: async (stepperArg, elementArg) => {
|
||||||
|
const deesForm = elementArg.querySelector('dees-form');
|
||||||
|
deesForm.addEventListener('formData', () => stepperArg.goNext(), { once: true });
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Review & Launch',
|
||||||
|
content: html`
|
||||||
|
<dees-panel>
|
||||||
|
<p>Almost there! Review your selections and launch whenever you're ready.</p>
|
||||||
|
</dees-panel>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
] as const}
|
||||||
|
></dees-stepper>
|
||||||
|
`;
|
@@ -1,5 +1,5 @@
|
|||||||
import * as plugins from './00plugins.js';
|
import * as plugins from '../00plugins.js';
|
||||||
import * as colors from './00colors.js';
|
import * as colors from '../00colors.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DeesElement,
|
DeesElement,
|
||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
} from '@design.estate/dees-element';
|
} from '@design.estate/dees-element';
|
||||||
|
|
||||||
import * as domtools from '@design.estate/dees-domtools';
|
import * as domtools from '@design.estate/dees-domtools';
|
||||||
|
import { stepperDemo } from './dees-stepper.demo.js';
|
||||||
|
|
||||||
export interface IStep {
|
export interface IStep {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -31,39 +32,7 @@ declare global {
|
|||||||
|
|
||||||
@customElement('dees-stepper')
|
@customElement('dees-stepper')
|
||||||
export class DeesStepper extends DeesElement {
|
export class DeesStepper extends DeesElement {
|
||||||
public static demo = () =>
|
public static demo = stepperDemo;
|
||||||
html`
|
|
||||||
<dees-stepper
|
|
||||||
.steps=${[
|
|
||||||
{
|
|
||||||
title: 'Whats your name?',
|
|
||||||
content: html`
|
|
||||||
<dees-form>
|
|
||||||
<dees-input-text
|
|
||||||
key="email"
|
|
||||||
label="Your Email"
|
|
||||||
value="hello@something.com"
|
|
||||||
disabled
|
|
||||||
></dees-input-text>
|
|
||||||
<dees-input-text key="firstName" required label="Vorname"></dees-input-text>
|
|
||||||
<dees-input-text key="lastName" required label="Nachname"></dees-input-text>
|
|
||||||
<dees-form-submit>Next</dees-form-submit>
|
|
||||||
</dees-form>
|
|
||||||
`,
|
|
||||||
validationFunc: async (stepperArg, elementArg) => {
|
|
||||||
const deesForm = elementArg.querySelector('dees-form');
|
|
||||||
deesForm.addEventListener('formData', (eventArg) => {
|
|
||||||
stepperArg.goNext();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Whats your mobile number?',
|
|
||||||
content: html``,
|
|
||||||
},
|
|
||||||
] as IStep[]}
|
|
||||||
></dees-stepper>
|
|
||||||
`;
|
|
||||||
|
|
||||||
@property({
|
@property({
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -99,30 +68,33 @@ export class DeesStepper extends DeesElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: all 0.7s ease-in-out;
|
transition: transform 0.35s ease, box-shadow 0.35s ease, filter 0.35s ease, border 0.35s ease;
|
||||||
max-width: 500px;
|
max-width: 500px;
|
||||||
min-height: 300px;
|
min-height: 300px;
|
||||||
border-radius: 16px;
|
border-radius: 18px;
|
||||||
background: ${cssManager.bdTheme('#ffffff', '#181818')};
|
background: ${cssManager.bdTheme('#ffffff', '#18181b')};
|
||||||
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#181818')};
|
border: 1px solid ${cssManager.bdTheme('rgba(226, 232, 240, 0.9)', 'rgba(63, 63, 70, 0.85)')};
|
||||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
color: ${cssManager.bdTheme('#0f172a', '#f5f5f5')};
|
||||||
margin: auto;
|
margin: auto;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
filter: opacity(0.5) grayscale(1);
|
filter: opacity(0.55) saturate(0.85);
|
||||||
box-shadow: 0px 0px 3px #00000010;
|
box-shadow: ${cssManager.bdTheme('0 20px 40px -25px rgba(15, 23, 42, 0.45)', '0 20px 36px -22px rgba(15, 23, 42, 0.65)')};
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step.selected {
|
.step.selected {
|
||||||
border-top: 1px solid #e4002b;
|
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
filter: opacity(1) grayscale(0);
|
filter: opacity(1) saturate(1);
|
||||||
box-shadow: 0px 0px 5px #00000010;
|
transform: translateY(-6px);
|
||||||
|
border: 1px solid ${cssManager.bdTheme(colors.dark.blue, colors.dark.blue)};
|
||||||
|
box-shadow: ${cssManager.bdTheme('0 28px 60px -30px rgba(15, 23, 42, 0.42)', '0 26px 55px -28px rgba(37, 99, 235, 0.6)')};
|
||||||
user-select: auto;
|
user-select: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step.hiddenStep {
|
.step.hiddenStep {
|
||||||
filter: opacity(0);
|
filter: opacity(0);
|
||||||
|
transform: translateY(16px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.step:last-child {
|
.step:last-child {
|
||||||
@@ -130,40 +102,50 @@ export class DeesStepper extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.step .stepCounter {
|
.step .stepCounter {
|
||||||
color: #999;
|
color: ${cssManager.bdTheme('#64748b', '#a1a1aa')};
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 12px;
|
||||||
right: 0px;
|
right: 12px;
|
||||||
padding: 10px 15px;
|
padding: 6px 14px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border-bottom-left-radius: 3px;
|
border-radius: 999px;
|
||||||
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
|
background: ${cssManager.bdTheme('rgba(226, 232, 240, 0.5)', 'rgba(63, 63, 70, 0.45)')};
|
||||||
|
border: 1px solid ${cssManager.bdTheme('rgba(226, 232, 240, 0.7)', 'rgba(63, 63, 70, 0.6)')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.step .goBack {
|
.step .goBack {
|
||||||
color: #999;
|
|
||||||
cursor: default;
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0px;
|
top: 12px;
|
||||||
left: 0px;
|
left: 12px;
|
||||||
padding: 10px 15px;
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 6px 12px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
border-bottom-right-radius: 3px;
|
font-weight: 500;
|
||||||
background: ${cssManager.bdTheme('#00000008', '#ffffff08')};
|
border-radius: 999px;
|
||||||
|
border: 1px solid ${cssManager.bdTheme('rgba(226, 232, 240, 0.9)', 'rgba(63, 63, 70, 0.85)')};
|
||||||
|
background: ${cssManager.bdTheme('rgba(255, 255, 255, 0.9)', 'rgba(39, 39, 42, 0.85)')};
|
||||||
|
color: ${cssManager.bdTheme('#475569', '#d4d4d8')};
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border 0.2s ease, color 0.2s ease, background 0.2s ease, transform 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step .goBack:hover {
|
.step .goBack:hover {
|
||||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
color: ${cssManager.bdTheme('#0f172a', '#fafafa')};
|
||||||
background: ${cssManager.bdTheme('#00000012', colors.dark.blue)};
|
border-color: ${cssManager.bdTheme(colors.dark.blue, colors.dark.blue)};
|
||||||
|
background: ${cssManager.bdTheme('rgba(226, 232, 240, 0.95)', 'rgba(63, 63, 70, 0.7)')};
|
||||||
|
transform: translateX(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.step .goBack:active {
|
.step .goBack:active {
|
||||||
color: ${cssManager.bdTheme('#333', '#fff')};
|
color: ${cssManager.bdTheme('#0f172a', '#fafafa')};
|
||||||
background: ${cssManager.bdTheme('#00000012', colors.dark.blueActive)};
|
border-color: ${cssManager.bdTheme(colors.dark.blueActive, colors.dark.blueActive)};
|
||||||
|
background: ${cssManager.bdTheme('rgba(226, 232, 240, 0.85)', 'rgba(63, 63, 70, 0.6)')};
|
||||||
}
|
}
|
||||||
|
|
||||||
.step .goBack span {
|
.step .goBack span {
|
||||||
transition: all 0.2s;
|
transition: transform 0.2s ease;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,15 +155,16 @@ export class DeesStepper extends DeesElement {
|
|||||||
|
|
||||||
.step .title {
|
.step .title {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding-top: 50px;
|
padding-top: 64px;
|
||||||
font-family: 'Geist Sans', sans-serif;
|
font-family: 'Geist Sans', sans-serif;
|
||||||
font-size: 22px;
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
font-weight: 500;
|
letter-spacing: -0.01em;
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.step .content {
|
.step .content {
|
||||||
padding: 20px;
|
padding: 24px 28px 32px;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
@@ -270,7 +253,14 @@ export class DeesStepper extends DeesElement {
|
|||||||
|
|
||||||
public async goBack() {
|
public async goBack() {
|
||||||
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
|
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
|
||||||
this.selectedStep = this.steps[currentIndex - 1];
|
if (currentIndex <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentStep = this.steps[currentIndex];
|
||||||
|
currentStep.validationFuncCalled = false;
|
||||||
|
const previousStep = this.steps[currentIndex - 1];
|
||||||
|
previousStep.validationFuncCalled = false;
|
||||||
|
this.selectedStep = previousStep;
|
||||||
await this.domtoolsPromise;
|
await this.domtoolsPromise;
|
||||||
await this.domtools.convenience.smartdelay.delayFor(100);
|
await this.domtools.convenience.smartdelay.delayFor(100);
|
||||||
this.selectedStep.onReturnToStepFunc?.(this, this.shadowRoot.querySelector('.selected'));
|
this.selectedStep.onReturnToStepFunc?.(this, this.shadowRoot.querySelector('.selected'));
|
||||||
@@ -278,6 +268,13 @@ export class DeesStepper extends DeesElement {
|
|||||||
|
|
||||||
public goNext() {
|
public goNext() {
|
||||||
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
|
const currentIndex = this.steps.findIndex((stepArg) => stepArg === this.selectedStep);
|
||||||
this.selectedStep = this.steps[currentIndex + 1];
|
if (currentIndex < 0 || currentIndex >= this.steps.length - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentStep = this.steps[currentIndex];
|
||||||
|
currentStep.validationFuncCalled = false;
|
||||||
|
const nextStep = this.steps[currentIndex + 1];
|
||||||
|
nextStep.validationFuncCalled = false;
|
||||||
|
this.selectedStep = nextStep;
|
||||||
}
|
}
|
||||||
}
|
}
|
1
ts_web/elements/dees-stepper/index.ts
Normal file
1
ts_web/elements/dees-stepper/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './dees-stepper.js';
|
2
ts_web/elements/dees-table/index.ts
Normal file
2
ts_web/elements/dees-table/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './dees-table.js';
|
||||||
|
export * from './types.js';
|
1
ts_web/elements/helperclasses/index.ts
Normal file
1
ts_web/elements/helperclasses/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from './formcontroller.js';
|
@@ -19,7 +19,7 @@ export * from './dees-contextmenu.js';
|
|||||||
export * from './dees-dataview-codebox.js';
|
export * from './dees-dataview-codebox.js';
|
||||||
export * from './dees-dataview-statusobject.js';
|
export * from './dees-dataview-statusobject.js';
|
||||||
export * from './dees-dashboardgrid/index.js';
|
export * from './dees-dashboardgrid/index.js';
|
||||||
export * from './dees-editor.js';
|
export * from './dees-editor/index.js';
|
||||||
export * from './dees-editor-markdown.js';
|
export * from './dees-editor-markdown.js';
|
||||||
export * from './dees-editor-markdownoutlet.js';
|
export * from './dees-editor-markdownoutlet.js';
|
||||||
export * from './dees-form-submit.js';
|
export * from './dees-form-submit.js';
|
||||||
@@ -56,8 +56,8 @@ export * from './dees-simple-login.js';
|
|||||||
export * from './dees-speechbubble.js';
|
export * from './dees-speechbubble.js';
|
||||||
export * from './dees-spinner.js';
|
export * from './dees-spinner.js';
|
||||||
export * from './dees-statsgrid.js';
|
export * from './dees-statsgrid.js';
|
||||||
export * from './dees-stepper.js';
|
export * from './dees-stepper/index.js';
|
||||||
export * from './dees-table/dees-table.js';
|
export * from './dees-table/index.js';
|
||||||
export * from './dees-terminal.js';
|
export * from './dees-terminal.js';
|
||||||
export * from './dees-toast.js';
|
export * from './dees-toast.js';
|
||||||
export * from './dees-updater.js';
|
export * from './dees-updater.js';
|
||||||
|
Reference in New Issue
Block a user