snice 4.12.0 → 4.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/accordion/snice-accordion.js +1 -1
- package/dist/cdn/accordion/snice-accordion.min.js +1 -1
- package/dist/cdn/alert/snice-alert.js +1 -1
- package/dist/cdn/alert/snice-alert.min.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.js +1 -1
- package/dist/cdn/app-tiles/snice-app-tiles.min.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.js +1 -1
- package/dist/cdn/audio-recorder/snice-audio-recorder.min.js +1 -1
- package/dist/cdn/avatar/snice-avatar.js +1 -1
- package/dist/cdn/avatar/snice-avatar.min.js +1 -1
- package/dist/cdn/badge/snice-badge.js +1 -1
- package/dist/cdn/badge/snice-badge.min.js +1 -1
- package/dist/cdn/banner/snice-banner.js +1 -1
- package/dist/cdn/banner/snice-banner.min.js +1 -1
- package/dist/cdn/book/snice-book.js +1 -1
- package/dist/cdn/book/snice-book.min.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.js +1 -1
- package/dist/cdn/breadcrumbs/snice-breadcrumbs.min.js +1 -1
- package/dist/cdn/button/snice-button.js +1 -1
- package/dist/cdn/button/snice-button.min.js +1 -1
- package/dist/cdn/calendar/snice-calendar.js +1 -1
- package/dist/cdn/calendar/snice-calendar.min.js +1 -1
- package/dist/cdn/camera/snice-camera.js +1 -1
- package/dist/cdn/camera/snice-camera.min.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.js +1 -1
- package/dist/cdn/camera-annotate/snice-camera-annotate.min.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.js +1 -1
- package/dist/cdn/candlestick/snice-candlestick.min.js +1 -1
- package/dist/cdn/card/snice-card.js +1 -1
- package/dist/cdn/card/snice-card.min.js +1 -1
- package/dist/cdn/carousel/snice-carousel.js +1 -1
- package/dist/cdn/carousel/snice-carousel.min.js +1 -1
- package/dist/cdn/chart/snice-chart.js +1 -1
- package/dist/cdn/chart/snice-chart.min.js +1 -1
- package/dist/cdn/chat/snice-chat.js +1 -1
- package/dist/cdn/chat/snice-chat.min.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.js +1 -1
- package/dist/cdn/checkbox/snice-checkbox.min.js +1 -1
- package/dist/cdn/chip/snice-chip.js +1 -1
- package/dist/cdn/chip/snice-chip.min.js +1 -1
- package/dist/cdn/code-block/snice-code-block.js +2 -2
- package/dist/cdn/code-block/snice-code-block.js.map +1 -1
- package/dist/cdn/code-block/snice-code-block.min.js +2 -2
- package/dist/cdn/code-block/snice-code-block.min.js.map +1 -1
- package/dist/cdn/color-display/snice-color-display.js +1 -1
- package/dist/cdn/color-display/snice-color-display.min.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.js +1 -1
- package/dist/cdn/color-picker/snice-color-picker.min.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.js +1 -1
- package/dist/cdn/command-palette/snice-command-palette.min.js +1 -1
- package/dist/cdn/comments/snice-comments.js +1 -1
- package/dist/cdn/comments/snice-comments.min.js +1 -1
- package/dist/cdn/countdown/snice-countdown.js +1 -1
- package/dist/cdn/countdown/snice-countdown.min.js +1 -1
- package/dist/cdn/cropper/snice-cropper.js +1 -1
- package/dist/cdn/cropper/snice-cropper.min.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.js +1 -1
- package/dist/cdn/date-picker/snice-date-picker.min.js +1 -1
- package/dist/cdn/diff/snice-diff.js +1 -1
- package/dist/cdn/diff/snice-diff.min.js +1 -1
- package/dist/cdn/divider/snice-divider.js +1 -1
- package/dist/cdn/divider/snice-divider.min.js +1 -1
- package/dist/cdn/doc/snice-doc.js +1 -1
- package/dist/cdn/doc/snice-doc.min.js +1 -1
- package/dist/cdn/draw/snice-draw.js +1 -1
- package/dist/cdn/draw/snice-draw.min.js +1 -1
- package/dist/cdn/drawer/snice-drawer.js +1 -1
- package/dist/cdn/drawer/snice-drawer.min.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.js +1 -1
- package/dist/cdn/empty-state/snice-empty-state.min.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.js +1 -1
- package/dist/cdn/file-gallery/snice-file-gallery.min.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.js +1 -1
- package/dist/cdn/file-upload/snice-file-upload.min.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.js +1 -1
- package/dist/cdn/flip-card/snice-flip-card.min.js +1 -1
- package/dist/cdn/flow/snice-flow.js +1 -1
- package/dist/cdn/flow/snice-flow.min.js +1 -1
- package/dist/cdn/funnel/snice-funnel.js +1 -1
- package/dist/cdn/funnel/snice-funnel.min.js +1 -1
- package/dist/cdn/gantt/snice-gantt.js +1 -1
- package/dist/cdn/gantt/snice-gantt.min.js +1 -1
- package/dist/cdn/gauge/snice-gauge.js +1 -1
- package/dist/cdn/gauge/snice-gauge.min.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.js +1 -1
- package/dist/cdn/heatmap/snice-heatmap.min.js +1 -1
- package/dist/cdn/image/snice-image.js +1 -1
- package/dist/cdn/image/snice-image.min.js +1 -1
- package/dist/cdn/input/snice-input.js +1 -1
- package/dist/cdn/input/snice-input.min.js +1 -1
- package/dist/cdn/kanban/snice-kanban.js +1 -1
- package/dist/cdn/kanban/snice-kanban.min.js +1 -1
- package/dist/cdn/kpi/snice-kpi.js +1 -1
- package/dist/cdn/kpi/snice-kpi.min.js +1 -1
- package/dist/cdn/layout/snice-layout.js +1 -1
- package/dist/cdn/layout/snice-layout.min.js +1 -1
- package/dist/cdn/link/snice-link.js +1 -1
- package/dist/cdn/link/snice-link.min.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.js +1 -1
- package/dist/cdn/link-preview/snice-link-preview.min.js +1 -1
- package/dist/cdn/list/snice-list.js +1 -1
- package/dist/cdn/list/snice-list.min.js +1 -1
- package/dist/cdn/location/snice-location.js +1 -1
- package/dist/cdn/location/snice-location.min.js +1 -1
- package/dist/cdn/login/snice-login.js +1 -1
- package/dist/cdn/login/snice-login.min.js +1 -1
- package/dist/cdn/map/snice-map.js +1 -1
- package/dist/cdn/map/snice-map.min.js +1 -1
- package/dist/cdn/markdown/snice-markdown.js +1 -1
- package/dist/cdn/markdown/snice-markdown.min.js +1 -1
- package/dist/cdn/masonry/snice-masonry.js +1 -1
- package/dist/cdn/masonry/snice-masonry.min.js +1 -1
- package/dist/cdn/menu/snice-menu.js +1 -1
- package/dist/cdn/menu/snice-menu.min.js +1 -1
- package/dist/cdn/modal/snice-modal.js +1 -1
- package/dist/cdn/modal/snice-modal.min.js +1 -1
- package/dist/cdn/music-player/snice-music-player.js +1 -1
- package/dist/cdn/music-player/snice-music-player.min.js +1 -1
- package/dist/cdn/nav/snice-nav.js +1 -1
- package/dist/cdn/nav/snice-nav.min.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.js +1 -1
- package/dist/cdn/network-graph/snice-network-graph.min.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.js +1 -1
- package/dist/cdn/notification-center/snice-notification-center.min.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.js +1 -1
- package/dist/cdn/org-chart/snice-org-chart.min.js +1 -1
- package/dist/cdn/pagination/snice-pagination.js +1 -1
- package/dist/cdn/pagination/snice-pagination.min.js +1 -1
- package/dist/cdn/paint/snice-paint.js +1 -1
- package/dist/cdn/paint/snice-paint.min.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.js +1 -1
- package/dist/cdn/pdf-viewer/snice-pdf-viewer.min.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.js +1 -1
- package/dist/cdn/podcast-player/snice-podcast-player.min.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.js +1 -1
- package/dist/cdn/pricing-table/snice-pricing-table.min.js +1 -1
- package/dist/cdn/progress/snice-progress.js +1 -1
- package/dist/cdn/progress/snice-progress.min.js +1 -1
- package/dist/cdn/qr-code/README.md +2 -2
- package/dist/cdn/qr-code/snice-qr-code.js +149 -20
- package/dist/cdn/qr-code/snice-qr-code.js.map +1 -1
- package/dist/cdn/qr-code/snice-qr-code.min.js +3 -3
- package/dist/cdn/qr-code/snice-qr-code.min.js.map +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.js +1 -1
- package/dist/cdn/qr-reader/snice-qr-reader.min.js +1 -1
- package/dist/cdn/radio/snice-radio.js +1 -1
- package/dist/cdn/radio/snice-radio.min.js +1 -1
- package/dist/cdn/rating/snice-rating.js +1 -1
- package/dist/cdn/rating/snice-rating.min.js +1 -1
- package/dist/cdn/recipe/snice-recipe.js +1 -1
- package/dist/cdn/recipe/snice-recipe.min.js +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.js +4 -4
- package/dist/cdn/runtime/snice-runtime.esm.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.esm.min.js +3 -3
- package/dist/cdn/runtime/snice-runtime.esm.min.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.js +4 -4
- package/dist/cdn/runtime/snice-runtime.js.map +1 -1
- package/dist/cdn/runtime/snice-runtime.min.js +3 -3
- package/dist/cdn/runtime/snice-runtime.min.js.map +1 -1
- package/dist/cdn/sankey/snice-sankey.js +1 -1
- package/dist/cdn/sankey/snice-sankey.min.js +1 -1
- package/dist/cdn/select/snice-select.js +1 -1
- package/dist/cdn/select/snice-select.min.js +1 -1
- package/dist/cdn/skeleton/snice-skeleton.js +1 -1
- package/dist/cdn/skeleton/snice-skeleton.min.js +1 -1
- package/dist/cdn/slider/snice-slider.js +2 -2
- package/dist/cdn/slider/snice-slider.js.map +1 -1
- package/dist/cdn/slider/snice-slider.min.js +5 -5
- package/dist/cdn/slider/snice-slider.min.js.map +1 -1
- package/dist/cdn/sortable/snice-sortable.js +1 -1
- package/dist/cdn/sortable/snice-sortable.min.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.js +1 -1
- package/dist/cdn/sparkline/snice-sparkline.min.js +1 -1
- package/dist/cdn/spinner/snice-spinner.js +1 -1
- package/dist/cdn/spinner/snice-spinner.min.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.js +1 -1
- package/dist/cdn/split-pane/snice-split-pane.min.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.js +1 -1
- package/dist/cdn/spotlight/snice-spotlight.min.js +1 -1
- package/dist/cdn/spreadsheet/snice-spreadsheet.js +1 -1
- package/dist/cdn/spreadsheet/snice-spreadsheet.min.js +1 -1
- package/dist/cdn/stepper/snice-stepper.js +1 -1
- package/dist/cdn/stepper/snice-stepper.min.js +1 -1
- package/dist/cdn/switch/snice-switch.js +1 -1
- package/dist/cdn/switch/snice-switch.min.js +1 -1
- package/dist/cdn/table/snice-table.js +1 -1
- package/dist/cdn/table/snice-table.min.js +1 -1
- package/dist/cdn/tabs/snice-tabs.js +1 -1
- package/dist/cdn/tabs/snice-tabs.min.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.js +1 -1
- package/dist/cdn/tag-input/snice-tag-input.min.js +1 -1
- package/dist/cdn/terminal/snice-terminal.js +1 -1
- package/dist/cdn/terminal/snice-terminal.min.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.js +1 -1
- package/dist/cdn/testimonial/snice-testimonial.min.js +1 -1
- package/dist/cdn/textarea/snice-textarea.js +1 -1
- package/dist/cdn/textarea/snice-textarea.min.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.js +1 -1
- package/dist/cdn/time-range-picker/snice-time-range-picker.min.js +1 -1
- package/dist/cdn/timeline/snice-timeline.js +1 -1
- package/dist/cdn/timeline/snice-timeline.min.js +1 -1
- package/dist/cdn/timer/snice-timer.js +1 -1
- package/dist/cdn/timer/snice-timer.min.js +1 -1
- package/dist/cdn/toast/snice-toast.js +1 -1
- package/dist/cdn/toast/snice-toast.min.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.js +1 -1
- package/dist/cdn/tooltip/snice-tooltip.min.js +1 -1
- package/dist/cdn/tree/snice-tree.js +1 -1
- package/dist/cdn/tree/snice-tree.min.js +1 -1
- package/dist/cdn/treemap/snice-treemap.js +1 -1
- package/dist/cdn/treemap/snice-treemap.min.js +1 -1
- package/dist/cdn/video-player/snice-video-player.js +1 -1
- package/dist/cdn/video-player/snice-video-player.min.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.js +1 -1
- package/dist/cdn/virtual-scroller/snice-virtual-scroller.min.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.js +1 -1
- package/dist/cdn/waterfall/snice-waterfall.min.js +1 -1
- package/dist/cdn/weather/snice-weather.js +1 -1
- package/dist/cdn/weather/snice-weather.min.js +1 -1
- package/dist/components/code-block/snice-code-block.js +1 -1
- package/dist/components/code-block/snice-code-block.js.map +1 -1
- package/dist/components/qr-code/qrcode.d.ts +1 -0
- package/dist/components/qr-code/qrcode.js +16 -8
- package/dist/components/qr-code/qrcode.js.map +1 -1
- package/dist/components/qr-code/snice-qr-code.d.ts +5 -2
- package/dist/components/qr-code/snice-qr-code.js +132 -11
- package/dist/components/qr-code/snice-qr-code.js.map +1 -1
- package/dist/components/qr-code/snice-qr-code.types.d.ts +3 -2
- package/dist/components/slider/snice-slider.js +1 -1
- package/dist/components/slider/snice-slider.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.esm.js +2 -2
- package/dist/index.iife.js +2 -2
- package/dist/symbols.cjs +1 -1
- package/dist/symbols.esm.js +1 -1
- package/dist/transitions.cjs +1 -1
- package/dist/transitions.esm.js +1 -1
- package/dist/types/request-options.d.ts +1 -1
- package/docs/ai/api.md +1 -1
- package/docs/ai/components/code-block.md +1 -1
- package/docs/ai/decorators.md +2 -2
- package/docs/ai/patterns.md +17 -6
- package/docs/code-block.md +1 -3
- package/docs/controllers.md +98 -391
- package/docs/elements.md +131 -117
- package/docs/events.md +74 -83
- package/docs/fetcher.md +64 -76
- package/docs/observe.md +13 -33
- package/docs/placards.md +6 -16
- package/docs/request-response.md +171 -693
- package/docs/routing.md +67 -136
- package/package.json +1 -1
- package/docs/migration-v2-to-v3.md +0 -569
package/docs/elements.md
CHANGED
|
@@ -10,7 +10,7 @@ Elements are the core building blocks of Snice components. They define custom HT
|
|
|
10
10
|
- [Queries](#queries)
|
|
11
11
|
- [Styling](#styling)
|
|
12
12
|
- [Template Events](#template-events)
|
|
13
|
-
- [Advanced Examples](#advanced-examples)
|
|
13
|
+
- [Advanced Examples](#advanced-examples) (Watch, Context, Conditionals)
|
|
14
14
|
|
|
15
15
|
## Basic Usage
|
|
16
16
|
|
|
@@ -30,8 +30,10 @@ class MyButton extends HTMLElement {
|
|
|
30
30
|
|
|
31
31
|
### Element Decorator Options
|
|
32
32
|
|
|
33
|
-
The `@element` decorator accepts
|
|
33
|
+
The `@element` decorator accepts:
|
|
34
34
|
- `tagName: string` - The custom element tag name (must contain a hyphen)
|
|
35
|
+
- `options?: ElementOptions` - Optional configuration
|
|
36
|
+
- `formAssociated?: boolean` - Enable form association (default: false)
|
|
35
37
|
|
|
36
38
|
## Lifecycle Methods
|
|
37
39
|
|
|
@@ -106,7 +108,7 @@ class SimpleList extends HTMLElement {
|
|
|
106
108
|
- Avoiding differential rendering issues with dynamic attributes
|
|
107
109
|
- Simple components where full re-render is acceptable
|
|
108
110
|
|
|
109
|
-
**Note:** When `differential: false`, the render method must return a string (not `html\`...\``)
|
|
111
|
+
**Note:** When `differential: false`, the render method must return a string (not `html\`...\``). Conditional rendering (`<if>`, `<switch>/<case>`) is NOT available — use ternary operators in the string template instead.
|
|
110
112
|
|
|
111
113
|
### Imperative Rendering
|
|
112
114
|
|
|
@@ -132,7 +134,7 @@ class UserCard extends HTMLElement {
|
|
|
132
134
|
}
|
|
133
135
|
|
|
134
136
|
@watch('name', 'role')
|
|
135
|
-
update() {
|
|
137
|
+
update(oldVal: any, newVal: any, prop: string) {
|
|
136
138
|
if (!this.$name) return;
|
|
137
139
|
this.$name.textContent = this.name;
|
|
138
140
|
this.$role.textContent = this.role;
|
|
@@ -195,44 +197,39 @@ class StyledCard extends HTMLElement {
|
|
|
195
197
|
}
|
|
196
198
|
```
|
|
197
199
|
|
|
198
|
-
**
|
|
200
|
+
**Note:** Only one `@styles()` method is supported per element. If multiple are declared, only the last one is used. Combine all styles in a single method:
|
|
201
|
+
|
|
199
202
|
```typescript
|
|
200
203
|
@styles()
|
|
201
|
-
|
|
204
|
+
componentStyles() {
|
|
202
205
|
return css`
|
|
203
206
|
:host { display: block; }
|
|
204
|
-
// ...
|
|
205
|
-
`;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
@styles()
|
|
209
|
-
themeStyles() {
|
|
210
|
-
return css`
|
|
211
207
|
.card { background: var(--bg-color); }
|
|
212
|
-
// ...
|
|
213
208
|
`;
|
|
214
209
|
}
|
|
215
210
|
```
|
|
216
211
|
|
|
217
212
|
### Lifecycle Decorators
|
|
218
213
|
|
|
219
|
-
**@ready()** - Called after
|
|
214
|
+
**@ready()** - Called after styles are applied and event handlers are set up. The initial render may still be completing in a microtask — use `@query` (which re-queries each access) to safely access rendered DOM:
|
|
220
215
|
|
|
221
216
|
```typescript
|
|
222
|
-
import { element, ready, render, html } from 'snice';
|
|
217
|
+
import { element, ready, query, render, html } from 'snice';
|
|
218
|
+
|
|
219
|
+
@element('auto-resize-textarea')
|
|
220
|
+
class AutoResizeTextarea extends HTMLElement {
|
|
221
|
+
@query('textarea') textarea?: HTMLTextAreaElement;
|
|
223
222
|
|
|
224
|
-
@element('data-loader')
|
|
225
|
-
class DataLoader extends HTMLElement {
|
|
226
223
|
@ready()
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
224
|
+
adjustHeight() {
|
|
225
|
+
if (this.textarea) {
|
|
226
|
+
this.textarea.style.height = `${this.textarea.scrollHeight}px`;
|
|
227
|
+
}
|
|
231
228
|
}
|
|
232
229
|
|
|
233
230
|
@render()
|
|
234
231
|
renderContent() {
|
|
235
|
-
return html`<
|
|
232
|
+
return html`<textarea @input=${this.adjustHeight}></textarea>`;
|
|
236
233
|
}
|
|
237
234
|
}
|
|
238
235
|
```
|
|
@@ -240,27 +237,29 @@ class DataLoader extends HTMLElement {
|
|
|
240
237
|
**@dispose()** - Called when element is removed from DOM:
|
|
241
238
|
|
|
242
239
|
```typescript
|
|
243
|
-
@element('
|
|
244
|
-
class
|
|
245
|
-
private
|
|
240
|
+
@element('animated-element')
|
|
241
|
+
class AnimatedElement extends HTMLElement {
|
|
242
|
+
private rafId?: number;
|
|
246
243
|
|
|
247
244
|
@ready()
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
245
|
+
startAnimation() {
|
|
246
|
+
const animate = () => {
|
|
247
|
+
// Update animation frame
|
|
248
|
+
this.rafId = requestAnimationFrame(animate);
|
|
249
|
+
};
|
|
250
|
+
this.rafId = requestAnimationFrame(animate);
|
|
252
251
|
}
|
|
253
252
|
|
|
254
253
|
@dispose()
|
|
255
|
-
|
|
256
|
-
if (this.
|
|
257
|
-
|
|
254
|
+
stopAnimation() {
|
|
255
|
+
if (this.rafId) {
|
|
256
|
+
cancelAnimationFrame(this.rafId);
|
|
258
257
|
}
|
|
259
258
|
}
|
|
260
259
|
|
|
261
260
|
@render()
|
|
262
261
|
renderContent() {
|
|
263
|
-
return html`<
|
|
262
|
+
return html`<canvas width="300" height="200"></canvas>`;
|
|
264
263
|
}
|
|
265
264
|
}
|
|
266
265
|
```
|
|
@@ -279,20 +278,23 @@ await (el as any).ready; // Wait for element to be ready
|
|
|
279
278
|
|
|
280
279
|
All elements automatically use Shadow DOM for style encapsulation.
|
|
281
280
|
|
|
282
|
-
### Accessing Shadow
|
|
281
|
+
### Accessing Shadow DOM Elements
|
|
282
|
+
|
|
283
|
+
Use `@query` instead of manual `shadowRoot.querySelector`:
|
|
283
284
|
|
|
284
285
|
```typescript
|
|
285
286
|
@element('shadow-demo')
|
|
286
287
|
class ShadowDemo extends HTMLElement {
|
|
288
|
+
@query('#content') content?: HTMLElement;
|
|
289
|
+
|
|
287
290
|
@render()
|
|
288
291
|
renderContent() {
|
|
289
292
|
return html`<div id="content">Hello</div>`;
|
|
290
293
|
}
|
|
291
294
|
|
|
292
295
|
updateContent(text: string) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
content.textContent = text;
|
|
296
|
+
if (this.content) {
|
|
297
|
+
this.content.textContent = text;
|
|
296
298
|
}
|
|
297
299
|
}
|
|
298
300
|
}
|
|
@@ -341,7 +343,7 @@ Usage:
|
|
|
341
343
|
```typescript
|
|
342
344
|
interface PropertyOptions {
|
|
343
345
|
type?: String | Number | Boolean | Array | Object | Date | BigInt | SimpleArray;
|
|
344
|
-
attribute?: string;
|
|
346
|
+
attribute?: string | boolean; // Custom attribute name, or false to disable attribute sync
|
|
345
347
|
converter?: PropertyConverter; // Custom converter
|
|
346
348
|
hasChanged?: (value: any, oldValue: any) => boolean;
|
|
347
349
|
}
|
|
@@ -351,10 +353,12 @@ interface PropertyOptions {
|
|
|
351
353
|
|
|
352
354
|
All properties automatically:
|
|
353
355
|
- Read from DOM attributes when present
|
|
354
|
-
- Reflect changes to corresponding attributes
|
|
356
|
+
- Reflect property setter changes to corresponding attributes
|
|
355
357
|
- Convert between string attributes and typed properties
|
|
356
358
|
- Trigger re-renders when changed
|
|
357
359
|
|
|
360
|
+
**Note:** Initial field values (defaults like `name = 'Anonymous'`) are NOT reflected to attributes. Only changes made via the property setter are reflected. Set `attribute: false` to disable attribute sync entirely.
|
|
361
|
+
|
|
358
362
|
```typescript
|
|
359
363
|
@element('reflected-props')
|
|
360
364
|
class ReflectedProps extends HTMLElement {
|
|
@@ -568,26 +572,28 @@ class ScopedStyles extends HTMLElement {
|
|
|
568
572
|
|
|
569
573
|
### Dynamic Styles
|
|
570
574
|
|
|
575
|
+
`@styles()` is called **once** during initialization and does not update on property changes. For dynamic styling, use CSS custom properties set in the template:
|
|
576
|
+
|
|
571
577
|
```typescript
|
|
572
578
|
@element('theme-component')
|
|
573
579
|
class ThemeComponent extends HTMLElement {
|
|
574
580
|
@property()
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
@property({ type: Number })
|
|
578
|
-
fontSize = 16;
|
|
581
|
+
accentColor = '#007bff';
|
|
579
582
|
|
|
580
583
|
@render()
|
|
581
584
|
renderContent() {
|
|
582
|
-
return html
|
|
585
|
+
return html`
|
|
586
|
+
<div class="themed" style="--accent: ${this.accentColor}">
|
|
587
|
+
Themed content
|
|
588
|
+
</div>
|
|
589
|
+
`;
|
|
583
590
|
}
|
|
584
591
|
|
|
585
592
|
@styles()
|
|
586
593
|
themeStyles() {
|
|
587
594
|
return css`
|
|
588
595
|
.themed {
|
|
589
|
-
color:
|
|
590
|
-
font-size: ${this.fontSize}px;
|
|
596
|
+
color: var(--accent);
|
|
591
597
|
}
|
|
592
598
|
`;
|
|
593
599
|
}
|
|
@@ -722,35 +728,38 @@ class FormHandler extends HTMLElement {
|
|
|
722
728
|
|
|
723
729
|
## Advanced Examples
|
|
724
730
|
|
|
725
|
-
###
|
|
731
|
+
### Form Component
|
|
732
|
+
|
|
733
|
+
Elements handle visual behavior — they render the form and emit events. Business logic (API calls, validation) belongs in controllers:
|
|
726
734
|
|
|
727
735
|
```typescript
|
|
728
|
-
import { element, property, query,
|
|
736
|
+
import { element, property, query, dispatch, render, styles, html, css } from 'snice';
|
|
729
737
|
|
|
730
738
|
@element('registration-form')
|
|
731
739
|
class RegistrationForm extends HTMLElement {
|
|
732
740
|
@property({ type: Boolean })
|
|
733
741
|
loading = false;
|
|
734
742
|
|
|
735
|
-
@query('form')
|
|
736
|
-
|
|
743
|
+
@query('form') form?: HTMLFormElement;
|
|
744
|
+
|
|
745
|
+
@dispatch('register-submit')
|
|
746
|
+
handleSubmit(event: Event) {
|
|
747
|
+
event.preventDefault();
|
|
748
|
+
return Object.fromEntries(new FormData(this.form!));
|
|
749
|
+
}
|
|
737
750
|
|
|
738
751
|
@render()
|
|
739
752
|
renderContent() {
|
|
740
753
|
return html`
|
|
741
754
|
<form @submit=${this.handleSubmit}>
|
|
742
|
-
<h2>Register</h2>
|
|
743
|
-
|
|
744
755
|
<div class="field">
|
|
745
756
|
<label>Username</label>
|
|
746
757
|
<input type="text" name="username" required>
|
|
747
758
|
</div>
|
|
748
|
-
|
|
749
759
|
<div class="field">
|
|
750
760
|
<label>Email</label>
|
|
751
761
|
<input type="email" name="email" required>
|
|
752
762
|
</div>
|
|
753
|
-
|
|
754
763
|
<button type="submit" ?disabled=${this.loading}>
|
|
755
764
|
${this.loading ? 'Registering...' : 'Register'}
|
|
756
765
|
</button>
|
|
@@ -764,78 +773,37 @@ class RegistrationForm extends HTMLElement {
|
|
|
764
773
|
:host {
|
|
765
774
|
display: block;
|
|
766
775
|
max-width: 400px;
|
|
767
|
-
margin: 0 auto;
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
form {
|
|
771
|
-
padding: 20px;
|
|
772
|
-
background: white;
|
|
773
|
-
border-radius: 8px;
|
|
774
776
|
}
|
|
775
777
|
|
|
776
778
|
.field {
|
|
777
|
-
margin-bottom:
|
|
779
|
+
margin-bottom: 1rem;
|
|
778
780
|
}
|
|
779
781
|
|
|
780
782
|
label {
|
|
781
783
|
display: block;
|
|
782
|
-
margin-bottom:
|
|
784
|
+
margin-bottom: 0.25rem;
|
|
783
785
|
font-weight: bold;
|
|
784
786
|
}
|
|
785
787
|
|
|
786
788
|
input {
|
|
787
789
|
width: 100%;
|
|
788
|
-
padding:
|
|
789
|
-
border: 1px solid #ddd;
|
|
790
|
+
padding: 0.5rem;
|
|
791
|
+
border: 1px solid var(--snice-color-border, #ddd);
|
|
790
792
|
border-radius: 4px;
|
|
791
793
|
}
|
|
792
794
|
|
|
793
|
-
button {
|
|
794
|
-
width: 100%;
|
|
795
|
-
padding: 10px;
|
|
796
|
-
background: #007bff;
|
|
797
|
-
color: white;
|
|
798
|
-
border: none;
|
|
799
|
-
border-radius: 4px;
|
|
800
|
-
cursor: pointer;
|
|
801
|
-
}
|
|
802
|
-
|
|
803
795
|
button:disabled {
|
|
804
796
|
opacity: 0.6;
|
|
805
797
|
cursor: not-allowed;
|
|
806
798
|
}
|
|
807
799
|
`;
|
|
808
800
|
}
|
|
809
|
-
|
|
810
|
-
async handleSubmit(event: Event) {
|
|
811
|
-
event.preventDefault();
|
|
812
|
-
|
|
813
|
-
this.loading = true;
|
|
814
|
-
|
|
815
|
-
try {
|
|
816
|
-
const formData = new FormData(this.form!);
|
|
817
|
-
|
|
818
|
-
// Simulate API call
|
|
819
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
820
|
-
|
|
821
|
-
this.dispatchEvent(new CustomEvent('registration-success', {
|
|
822
|
-
detail: Object.fromEntries(formData),
|
|
823
|
-
bubbles: true
|
|
824
|
-
}));
|
|
825
|
-
|
|
826
|
-
this.form?.reset();
|
|
827
|
-
} catch (error) {
|
|
828
|
-
console.error('Registration failed:', error);
|
|
829
|
-
} finally {
|
|
830
|
-
this.loading = false;
|
|
831
|
-
}
|
|
832
|
-
}
|
|
833
801
|
}
|
|
834
802
|
```
|
|
835
803
|
|
|
836
804
|
### Watch Decorator
|
|
837
805
|
|
|
838
|
-
Use `@watch` to react to property changes:
|
|
806
|
+
Use `@watch` to react to property changes. Handlers receive three arguments: `(oldValue, newValue, propertyName)`.
|
|
839
807
|
|
|
840
808
|
```typescript
|
|
841
809
|
@element('reactive-component')
|
|
@@ -847,8 +815,8 @@ class ReactiveComponent extends HTMLElement {
|
|
|
847
815
|
score = 0;
|
|
848
816
|
|
|
849
817
|
@watch('userName')
|
|
850
|
-
onUserNameChange(oldVal: string, newVal: string) {
|
|
851
|
-
console.log(
|
|
818
|
+
onUserNameChange(oldVal: string, newVal: string, prop: string) {
|
|
819
|
+
console.log(`${prop} changed from ${oldVal} to ${newVal}`);
|
|
852
820
|
}
|
|
853
821
|
|
|
854
822
|
@watch('score')
|
|
@@ -858,6 +826,12 @@ class ReactiveComponent extends HTMLElement {
|
|
|
858
826
|
}
|
|
859
827
|
}
|
|
860
828
|
|
|
829
|
+
// Wildcard watcher — fires on any @property change
|
|
830
|
+
@watch('*')
|
|
831
|
+
onAnyChange(oldVal: any, newVal: any, prop: string) {
|
|
832
|
+
console.log(`${prop}: ${oldVal} → ${newVal}`);
|
|
833
|
+
}
|
|
834
|
+
|
|
861
835
|
@render()
|
|
862
836
|
renderContent() {
|
|
863
837
|
return html`
|
|
@@ -870,6 +844,60 @@ class ReactiveComponent extends HTMLElement {
|
|
|
870
844
|
}
|
|
871
845
|
```
|
|
872
846
|
|
|
847
|
+
### @context() Decorator
|
|
848
|
+
|
|
849
|
+
Receive router context updates. The decorated method is called whenever the router context changes (navigation, app context update, etc.):
|
|
850
|
+
|
|
851
|
+
```typescript
|
|
852
|
+
import { element, context, property, render, html } from 'snice';
|
|
853
|
+
import type { Context, Placard } from 'snice';
|
|
854
|
+
|
|
855
|
+
@element('nav-bar')
|
|
856
|
+
class NavBar extends HTMLElement {
|
|
857
|
+
@property({ type: Array })
|
|
858
|
+
placards: Placard[] = [];
|
|
859
|
+
|
|
860
|
+
@property()
|
|
861
|
+
currentRoute = '';
|
|
862
|
+
|
|
863
|
+
@context()
|
|
864
|
+
onContextUpdate(ctx: Context) {
|
|
865
|
+
this.placards = ctx.navigation.placards;
|
|
866
|
+
this.currentRoute = ctx.navigation.route;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
@render()
|
|
870
|
+
renderContent() {
|
|
871
|
+
return html`
|
|
872
|
+
<nav>
|
|
873
|
+
${this.placards
|
|
874
|
+
.filter(p => p.show !== false)
|
|
875
|
+
.map(p => html`
|
|
876
|
+
<a href="#/${p.name}" class="${this.currentRoute === p.name ? 'active' : ''}">
|
|
877
|
+
${p.icon} ${p.title}
|
|
878
|
+
</a>
|
|
879
|
+
`)}
|
|
880
|
+
</nav>
|
|
881
|
+
`;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
**Context Options:**
|
|
887
|
+
|
|
888
|
+
```typescript
|
|
889
|
+
@context({ debounce: 300 }) // Wait 300ms after last change
|
|
890
|
+
@context({ throttle: 500 }) // At most once per 500ms
|
|
891
|
+
@context({ once: true }) // Only called once, then auto-unregisters
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
The `Context` object provides:
|
|
895
|
+
- `ctx.application` — App context (theme, auth, config, etc.)
|
|
896
|
+
- `ctx.navigation.route` — Current route path
|
|
897
|
+
- `ctx.navigation.params` — Route parameters
|
|
898
|
+
- `ctx.navigation.placards` — All registered page placards
|
|
899
|
+
- `ctx.fetch` — Fetch with middleware support (see Fetcher docs)
|
|
900
|
+
|
|
873
901
|
### Conditional Rendering
|
|
874
902
|
|
|
875
903
|
```typescript
|
|
@@ -898,18 +926,4 @@ class ConditionalContent extends HTMLElement {
|
|
|
898
926
|
}
|
|
899
927
|
```
|
|
900
928
|
|
|
901
|
-
## Best Practices
|
|
902
|
-
|
|
903
|
-
1. **Use @render() for templates**: Always return `html\`...\`` tagged templates
|
|
904
|
-
2. **Use @styles() for CSS**: Always return `css\`...\`` tagged templates
|
|
905
|
-
3. **Leverage auto-rendering**: Properties automatically trigger re-renders
|
|
906
|
-
4. **Use template events**: Handle events with `@event=${handler}` in templates
|
|
907
|
-
5. **Clean up resources**: Use @dispose() for cleanup tasks
|
|
908
|
-
6. **Type your queries**: Use proper TypeScript types for queried elements
|
|
909
|
-
7. **Handle errors**: Wrap async operations in try-catch blocks
|
|
910
|
-
|
|
911
|
-
## Removed in v3.0.0
|
|
912
929
|
|
|
913
|
-
- **@part decorator**: Removed in favor of differential rendering. Use `@render()` with templates instead.
|
|
914
|
-
- **html() method**: Replaced with `@render()` decorator
|
|
915
|
-
- **css() method**: Replaced with `@styles()` decorator
|
package/docs/events.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Events API Documentation
|
|
2
2
|
|
|
3
|
-
Event handling in Snice provides two powerful approaches: **template event syntax** and the **`@on` decorator
|
|
3
|
+
Event handling in Snice provides two powerful approaches: **template event syntax** and the **`@on` decorator**. The `@on` decorator works in **both elements AND controllers** with full event delegation, keyboard modifiers, debounce/throttle, and more. Additionally, the `@dispatch` decorator enables automatic custom event dispatching.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
- [Template Event Syntax](#template-event-syntax)
|
|
7
|
-
- [@on Decorator
|
|
7
|
+
- [@on Decorator](#on-decorator)
|
|
8
8
|
- [@dispatch Decorator](#dispatch-decorator)
|
|
9
9
|
- [Custom Events](#custom-events)
|
|
10
10
|
- [Event Delegation](#event-delegation)
|
|
@@ -247,15 +247,15 @@ class TaskList extends HTMLElement {
|
|
|
247
247
|
}
|
|
248
248
|
```
|
|
249
249
|
|
|
250
|
-
## @on Decorator
|
|
250
|
+
## @on Decorator
|
|
251
251
|
|
|
252
|
-
The `@on` decorator
|
|
252
|
+
The `@on` decorator works in **both elements AND controllers**. It provides powerful event delegation, keyboard modifiers, debounce/throttle, and automatic event handling features.
|
|
253
253
|
|
|
254
254
|
**Use `@on` when you need:**
|
|
255
255
|
- Event delegation with CSS selectors
|
|
256
256
|
- Keyboard modifier matching (`Enter`, `ctrl+s`, etc.)
|
|
257
257
|
- Debounce or throttle
|
|
258
|
-
- Multiple events on one handler
|
|
258
|
+
- Multiple events on one handler (accepts `string[]`: `@on(['mouseenter', 'focus'])`)
|
|
259
259
|
- Automatic preventDefault or stopPropagation
|
|
260
260
|
|
|
261
261
|
### Basic Controller Usage
|
|
@@ -378,12 +378,9 @@ interface OnOptions {
|
|
|
378
378
|
preventDefault?: boolean; // Automatically call preventDefault on the event
|
|
379
379
|
stopPropagation?: boolean; // Automatically call stopPropagation on the event
|
|
380
380
|
|
|
381
|
-
// Timing controls
|
|
381
|
+
// Timing controls
|
|
382
382
|
debounce?: number; // Debounce the handler by specified milliseconds
|
|
383
383
|
throttle?: number; // Throttle the handler by specified milliseconds
|
|
384
|
-
|
|
385
|
-
// Event delegation
|
|
386
|
-
target?: string; // CSS selector to target specific elements within shadow root
|
|
387
384
|
}
|
|
388
385
|
```
|
|
389
386
|
|
|
@@ -437,8 +434,8 @@ While template syntax is preferred, `@on` can also be used in elements:
|
|
|
437
434
|
```typescript
|
|
438
435
|
import { element, on, render, html } from 'snice';
|
|
439
436
|
|
|
440
|
-
@element('
|
|
441
|
-
class
|
|
437
|
+
@element('my-button')
|
|
438
|
+
class MyButton extends HTMLElement {
|
|
442
439
|
@render()
|
|
443
440
|
renderContent() {
|
|
444
441
|
return html`<button class="btn">Click me</button>`;
|
|
@@ -505,6 +502,8 @@ input.addEventListener('value-changed', (e: CustomEvent) => {
|
|
|
505
502
|
|
|
506
503
|
### Event Options
|
|
507
504
|
|
|
505
|
+
Events dispatched by `@dispatch` default to `bubbles: true` and `composed: true` (crosses shadow DOM boundaries). Override if needed:
|
|
506
|
+
|
|
508
507
|
```typescript
|
|
509
508
|
@element('status-indicator')
|
|
510
509
|
class StatusIndicator extends HTMLElement {
|
|
@@ -513,7 +512,8 @@ class StatusIndicator extends HTMLElement {
|
|
|
513
512
|
return html`<div>Status</div>`;
|
|
514
513
|
}
|
|
515
514
|
|
|
516
|
-
|
|
515
|
+
// Defaults: bubbles: true, composed: true
|
|
516
|
+
@dispatch('status-changed')
|
|
517
517
|
updateStatus(status: string) {
|
|
518
518
|
return {
|
|
519
519
|
status,
|
|
@@ -523,36 +523,77 @@ class StatusIndicator extends HTMLElement {
|
|
|
523
523
|
}
|
|
524
524
|
```
|
|
525
525
|
|
|
526
|
-
###
|
|
526
|
+
### DispatchOptions
|
|
527
527
|
|
|
528
528
|
```typescript
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
529
|
+
interface DispatchOptions extends EventInit {
|
|
530
|
+
dispatchOnUndefined?: boolean; // Skip dispatch when return is undefined (default: true)
|
|
531
|
+
debounce?: number; // Debounce dispatch by ms
|
|
532
|
+
throttle?: number; // Throttle dispatch by ms
|
|
533
|
+
}
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Debounce/Throttle
|
|
532
537
|
|
|
538
|
+
```typescript
|
|
539
|
+
@element('search-box')
|
|
540
|
+
class SearchBox extends HTMLElement {
|
|
533
541
|
@render()
|
|
534
542
|
renderContent() {
|
|
535
|
-
return html`<
|
|
543
|
+
return html`<input @input=${this.handleInput}>`;
|
|
536
544
|
}
|
|
537
545
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
this.data.push(item);
|
|
541
|
-
return { item, total: this.data.length };
|
|
546
|
+
handleInput(e: Event) {
|
|
547
|
+
this.emitSearch((e.target as HTMLInputElement).value);
|
|
542
548
|
}
|
|
543
549
|
|
|
544
|
-
@dispatch('
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
this.data = this.data.filter(i => i.id !== id);
|
|
548
|
-
return { id, item, total: this.data.length };
|
|
550
|
+
@dispatch('search-query', { debounce: 300 })
|
|
551
|
+
emitSearch(query: string) {
|
|
552
|
+
return { query };
|
|
549
553
|
}
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Async Methods
|
|
558
|
+
|
|
559
|
+
`@dispatch` works with async methods — the event dispatches after the promise resolves:
|
|
550
560
|
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
561
|
+
```typescript
|
|
562
|
+
@dispatch('validation-complete')
|
|
563
|
+
async validate() {
|
|
564
|
+
const result = await this.runValidation();
|
|
565
|
+
return { valid: result.isValid, errors: result.errors };
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### Multiple Events
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
@element('color-picker')
|
|
573
|
+
class ColorPicker extends HTMLElement {
|
|
574
|
+
@property() color = '#000000';
|
|
575
|
+
|
|
576
|
+
@render()
|
|
577
|
+
renderContent() {
|
|
578
|
+
return html`
|
|
579
|
+
<input type="color" .value=${this.color} @input=${this.handleInput}>
|
|
580
|
+
<button @click=${this.confirm}>OK</button>
|
|
581
|
+
`;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
handleInput(e: Event) {
|
|
585
|
+
this.changeColor((e.target as HTMLInputElement).value);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
@dispatch('color-preview')
|
|
589
|
+
changeColor(color: string) {
|
|
590
|
+
this.color = color;
|
|
591
|
+
return { color };
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
@dispatch('color-selected')
|
|
595
|
+
confirm() {
|
|
596
|
+
return { color: this.color };
|
|
556
597
|
}
|
|
557
598
|
}
|
|
558
599
|
```
|
|
@@ -615,8 +656,8 @@ class TableController implements IController {
|
|
|
615
656
|
// Single event listener handles all rows
|
|
616
657
|
@on('click', 'tr')
|
|
617
658
|
handleRowClick(event: MouseEvent) {
|
|
618
|
-
const row = event.
|
|
619
|
-
console.log('Row clicked:', row
|
|
659
|
+
const row = (event.target as HTMLElement).closest('tr');
|
|
660
|
+
console.log('Row clicked:', row?.dataset.id);
|
|
620
661
|
}
|
|
621
662
|
|
|
622
663
|
// Handle button clicks in cells
|
|
@@ -754,54 +795,4 @@ class KeyboardController implements IController {
|
|
|
754
795
|
}
|
|
755
796
|
```
|
|
756
797
|
|
|
757
|
-
## Best Practices
|
|
758
|
-
|
|
759
|
-
### For Elements
|
|
760
|
-
|
|
761
|
-
1. **Prefer template syntax**: Use `@event=${handler}` in templates
|
|
762
|
-
2. **Use arrow functions for parameters**: `@click=${() => this.delete(id)}`
|
|
763
|
-
3. **Handle events at appropriate level**: Use event delegation for dynamic content
|
|
764
|
-
4. **Prevent default when needed**: Call `e.preventDefault()` in handlers
|
|
765
|
-
5. **Use keyboard shortcuts**: Leverage `@keydown.ctrl+s` syntax
|
|
766
|
-
|
|
767
|
-
### For Controllers
|
|
768
|
-
|
|
769
|
-
1. **Use @on decorator**: Controllers should use `@on` for event handling
|
|
770
|
-
2. **Leverage event delegation**: Use selectors to handle dynamic content
|
|
771
|
-
3. **Throttle/debounce high-frequency events**: Use `{ throttle }` or `{ debounce }` options
|
|
772
|
-
4. **Clean up automatically**: Event listeners are cleaned up on detach
|
|
773
|
-
5. **Handle keyboard shortcuts**: Use `@on('keydown:Ctrl+S')` syntax
|
|
774
|
-
|
|
775
|
-
### General
|
|
776
|
-
|
|
777
|
-
1. **Type your events**: Use TypeScript event types for type safety
|
|
778
|
-
2. **Stop propagation carefully**: Only use `stopPropagation()` when necessary
|
|
779
|
-
3. **Use custom events**: Dispatch custom events for component communication
|
|
780
|
-
4. **Compose events**: Use `{ composed: true }` for cross-shadow-DOM events
|
|
781
|
-
5. **Test event handlers**: Write tests for all event handling logic
|
|
782
|
-
|
|
783
|
-
## Event Types Reference
|
|
784
|
-
|
|
785
|
-
**Mouse Events:**
|
|
786
|
-
- `click`, `dblclick`, `mousedown`, `mouseup`
|
|
787
|
-
- `mouseenter`, `mouseleave`, `mousemove`, `mouseover`, `mouseout`
|
|
788
|
-
- `contextmenu`
|
|
789
|
-
|
|
790
|
-
**Keyboard Events:**
|
|
791
|
-
- `keydown`, `keyup`, `keypress`
|
|
792
|
-
|
|
793
|
-
**Form Events:**
|
|
794
|
-
- `input`, `change`, `submit`, `reset`
|
|
795
|
-
- `focus`, `blur`, `focusin`, `focusout`
|
|
796
|
-
|
|
797
|
-
**Drag Events:**
|
|
798
|
-
- `drag`, `dragstart`, `dragend`
|
|
799
|
-
- `dragenter`, `dragover`, `dragleave`, `drop`
|
|
800
|
-
|
|
801
|
-
**Touch Events:**
|
|
802
|
-
- `touchstart`, `touchmove`, `touchend`, `touchcancel`
|
|
803
798
|
|
|
804
|
-
**Other Events:**
|
|
805
|
-
- `scroll`, `resize`, `load`, `error`
|
|
806
|
-
- `animationstart`, `animationend`, `animationiteration`
|
|
807
|
-
- `transitionstart`, `transitionend`
|